This is an env-protection.js plugin for OpenCode to prevent the agent from accidentally reading or modifying .env files or dumping environment variables to its memory.
Drop this into ~/.config/opencode/plugins/env-protection.js and register it in your opencode.jsonc.
// Secret-file guard for every tool that reads/writes via paths or shell.
// Blocks .env-style files, key material, home rc files holding tokens, and
// environment dumps. .env.example / .env.template / env.ts schemas pass.
export const EnvProtection = async () => {
const EXAMPLE_BASES = new Set([
".env.example", ".env.template", ".env.sample", ".env.defaults",
"env.ts", "env.mjs", "env.d.ts",
]);
const HOME_RC = new Set([
".netrc", ".npmrc", ".pypirc", ".yarnrc", ".git-credentials",
".pgpass", ".my.cnf", ".htpasswd", ".tfstate", "credentials", ".vercel",
]);
const SECRET_PATH = /(^|\/)(\.ssh|\.gnupg|\.aws|\.kube\/config|\.docker\/config|\.config\/gh|\.cargo\/credentials|\.codex\/config\.toml)\//;
const isSensitive = (value) => {
if (typeof value !== "string") return false;
const tokens = (value.match(/[^\s"';&|<>()]+/g) || []).map((t) => t.replace(/^['"]|['"]$/g, ""));
return tokens.some((t) => {
const base = t.split("/").pop();
if (EXAMPLE_BASES.has(base)) return false;
if (base === ".env" || base.startsWith(".env.") || base === ".envrc") return true;
if (base === ".dev.vars") return true;
if (/\.(pem|p12|pfx|ppk|p8)$/.test(base)) return true;
if (/\.key$/.test(base)) return true;
if (/^(id_rsa|id_dsa|id_ed25519|id_ecdsa)\b/.test(base)) return true;
if (/^(credentials|secrets)\.(json|ya?ml|toml)$/.test(base)) return true;
if (HOME_RC.has(base)) return true;
if (/_history$/.test(base) || /\.[a-z0-9]+_history$/.test(base)) return true;
if (SECRET_PATH.test(t)) return true;
if (/\/proc\/[^/]+\/environ/.test(t)) return true;
return false;
});
};
const isEnvDump = (command) => /\bprintenv\b/.test(command) || /(^|[\s;&|"'])env\s*($|\|)/.test(command);
const SENSITIVE_TOOLS = new Set(["read", "edit", "write", "patch", "bash", "grep"]);
const blocked = (tool) => `Blocked: access to secret files (${tool}) is not allowed. Use .env.example or real env vars instead.`;
return {
"tool.execute.before": async (input, output) => {
if (!SENSITIVE_TOOLS.has(input.tool)) return;
const args = output.args || {};
if (input.tool === "bash") {
if (isSensitive(args.command) || isEnvDump(args.command)) {
throw new Error(blocked("bash"));
}
} else if (input.tool === "grep") {
const targets = [args.include, args.path].filter(Boolean).join(" ");
if (isSensitive(targets)) {
throw new Error(blocked("grep"));
}
} else if (isSensitive(args.filePath)) {
throw new Error(blocked(input.tool));
}
},
};
};