diff --git a/src/infra/dotenv.test.ts b/src/infra/dotenv.test.ts index ca60457927a..0f8cbbfe32d 100644 --- a/src/infra/dotenv.test.ts +++ b/src/infra/dotenv.test.ts @@ -5,11 +5,51 @@ import { describe, expect, it, vi } from "vitest"; import { loadCliDotEnv } from "../cli/dotenv.js"; import { loadDotEnv, loadWorkspaceDotEnvFile } from "./dotenv.js"; +const CREDENTIAL_AND_GATEWAY_ENV_KEYS = [ + "ANTHROPIC_API_KEY", + "ANTHROPIC_API_KEY_SECONDARY", + "ANTHROPIC_OAUTH_TOKEN", + "OPENAI_API_KEY", + "OPENAI_API_KEYS", + "OPENAI_API_KEY_SECONDARY", + "OPENCLAW_LIVE_ANTHROPIC_KEY", + "OPENCLAW_LIVE_ANTHROPIC_KEYS", + "OPENCLAW_LIVE_GEMINI_KEY", + "OPENCLAW_LIVE_OPENAI_KEY", + "OPENCLAW_GATEWAY_TOKEN", + "OPENCLAW_GATEWAY_PASSWORD", + "OPENCLAW_GATEWAY_SECRET", +] as const; + +const BUNDLED_TRUST_ROOT_ENV_LINES = [ + "OPENCLAW_BROWSER_CONTROL_MODULE=data:text/javascript,boom", + "OPENCLAW_BUNDLED_HOOKS_DIR=./attacker-hooks", + "OPENCLAW_BUNDLED_PLUGINS_DIR=./attacker-plugins", + "OPENCLAW_BUNDLED_SKILLS_DIR=./attacker-skills", + "OPENCLAW_SKIP_BROWSER_CONTROL_SERVER=1", +] as const; + +const BUNDLED_TRUST_ROOT_ENV_KEYS = BUNDLED_TRUST_ROOT_ENV_LINES.map( + (line) => line.split("=")[0] ?? "", +); + async function writeEnvFile(filePath: string, contents: string) { await fs.mkdir(path.dirname(filePath), { recursive: true }); await fs.writeFile(filePath, contents, "utf8"); } +function clearEnv(keys: readonly string[]) { + for (const key of keys) { + delete process.env[key]; + } +} + +function expectEnvUndefined(keys: readonly string[]) { + for (const key of keys) { + expect(process.env[key]).toBeUndefined(); + } +} + async function withIsolatedEnvAndCwd(run: () => Promise) { const prevEnv = { ...process.env }; try { @@ -210,35 +250,11 @@ describe("loadDotEnv", () => { ].join("\n"), ); - delete process.env.ANTHROPIC_API_KEY; - delete process.env.ANTHROPIC_API_KEY_SECONDARY; - delete process.env.ANTHROPIC_OAUTH_TOKEN; - delete process.env.OPENAI_API_KEY; - delete process.env.OPENAI_API_KEYS; - delete process.env.OPENAI_API_KEY_SECONDARY; - delete process.env.OPENCLAW_LIVE_ANTHROPIC_KEY; - delete process.env.OPENCLAW_LIVE_ANTHROPIC_KEYS; - delete process.env.OPENCLAW_LIVE_GEMINI_KEY; - delete process.env.OPENCLAW_LIVE_OPENAI_KEY; - delete process.env.OPENCLAW_GATEWAY_TOKEN; - delete process.env.OPENCLAW_GATEWAY_PASSWORD; - delete process.env.OPENCLAW_GATEWAY_SECRET; + clearEnv(CREDENTIAL_AND_GATEWAY_ENV_KEYS); loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true }); - expect(process.env.ANTHROPIC_API_KEY).toBeUndefined(); - expect(process.env.ANTHROPIC_API_KEY_SECONDARY).toBeUndefined(); - expect(process.env.ANTHROPIC_OAUTH_TOKEN).toBeUndefined(); - expect(process.env.OPENAI_API_KEY).toBeUndefined(); - expect(process.env.OPENAI_API_KEYS).toBeUndefined(); - expect(process.env.OPENAI_API_KEY_SECONDARY).toBeUndefined(); - expect(process.env.OPENCLAW_LIVE_ANTHROPIC_KEY).toBeUndefined(); - expect(process.env.OPENCLAW_LIVE_ANTHROPIC_KEYS).toBeUndefined(); - expect(process.env.OPENCLAW_LIVE_GEMINI_KEY).toBeUndefined(); - expect(process.env.OPENCLAW_LIVE_OPENAI_KEY).toBeUndefined(); - expect(process.env.OPENCLAW_GATEWAY_TOKEN).toBeUndefined(); - expect(process.env.OPENCLAW_GATEWAY_PASSWORD).toBeUndefined(); - expect(process.env.OPENCLAW_GATEWAY_SECRET).toBeUndefined(); + expectEnvUndefined(CREDENTIAL_AND_GATEWAY_ENV_KEYS); }); }); }); @@ -333,30 +349,13 @@ describe("loadDotEnv", () => { it("blocks bundled trust-root vars from workspace .env", async () => { await withIsolatedEnvAndCwd(async () => { await withDotEnvFixture(async ({ cwdDir }) => { - await writeEnvFile( - path.join(cwdDir, ".env"), - [ - "OPENCLAW_BROWSER_CONTROL_MODULE=data:text/javascript,boom", - "OPENCLAW_BUNDLED_HOOKS_DIR=./attacker-hooks", - "OPENCLAW_BUNDLED_PLUGINS_DIR=./attacker-plugins", - "OPENCLAW_BUNDLED_SKILLS_DIR=./attacker-skills", - "OPENCLAW_SKIP_BROWSER_CONTROL_SERVER=1", - ].join("\n"), - ); + await writeEnvFile(path.join(cwdDir, ".env"), [...BUNDLED_TRUST_ROOT_ENV_LINES].join("\n")); - delete process.env.OPENCLAW_BROWSER_CONTROL_MODULE; - delete process.env.OPENCLAW_BUNDLED_HOOKS_DIR; - delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR; - delete process.env.OPENCLAW_BUNDLED_SKILLS_DIR; - delete process.env.OPENCLAW_SKIP_BROWSER_CONTROL_SERVER; + clearEnv(BUNDLED_TRUST_ROOT_ENV_KEYS); loadWorkspaceDotEnvFile(path.join(cwdDir, ".env"), { quiet: true }); - expect(process.env.OPENCLAW_BROWSER_CONTROL_MODULE).toBeUndefined(); - expect(process.env.OPENCLAW_BUNDLED_HOOKS_DIR).toBeUndefined(); - expect(process.env.OPENCLAW_BUNDLED_PLUGINS_DIR).toBeUndefined(); - expect(process.env.OPENCLAW_BUNDLED_SKILLS_DIR).toBeUndefined(); - expect(process.env.OPENCLAW_SKIP_BROWSER_CONTROL_SERVER).toBeUndefined(); + expectEnvUndefined(BUNDLED_TRUST_ROOT_ENV_KEYS); }); }); }); @@ -411,19 +410,7 @@ describe("loadDotEnv", () => { ].join("\n"), ); vi.spyOn(process, "cwd").mockReturnValue(cwdDir); - delete process.env.ANTHROPIC_API_KEY; - delete process.env.ANTHROPIC_API_KEY_SECONDARY; - delete process.env.ANTHROPIC_OAUTH_TOKEN; - delete process.env.OPENAI_API_KEY; - delete process.env.OPENAI_API_KEYS; - delete process.env.OPENAI_API_KEY_SECONDARY; - delete process.env.OPENCLAW_LIVE_ANTHROPIC_KEY; - delete process.env.OPENCLAW_LIVE_ANTHROPIC_KEYS; - delete process.env.OPENCLAW_LIVE_GEMINI_KEY; - delete process.env.OPENCLAW_LIVE_OPENAI_KEY; - delete process.env.OPENCLAW_GATEWAY_TOKEN; - delete process.env.OPENCLAW_GATEWAY_PASSWORD; - delete process.env.OPENCLAW_GATEWAY_SECRET; + clearEnv(CREDENTIAL_AND_GATEWAY_ENV_KEYS); loadDotEnv({ quiet: true }); @@ -552,31 +539,14 @@ describe("loadCliDotEnv", () => { it("blocks bundled trust-root vars from workspace .env during CLI startup", async () => { await withIsolatedEnvAndCwd(async () => { await withDotEnvFixture(async ({ cwdDir }) => { - await writeEnvFile( - path.join(cwdDir, ".env"), - [ - "OPENCLAW_BROWSER_CONTROL_MODULE=data:text/javascript,boom", - "OPENCLAW_BUNDLED_HOOKS_DIR=./attacker-hooks", - "OPENCLAW_BUNDLED_PLUGINS_DIR=./attacker-plugins", - "OPENCLAW_BUNDLED_SKILLS_DIR=./attacker-skills", - "OPENCLAW_SKIP_BROWSER_CONTROL_SERVER=1", - ].join("\n"), - ); + await writeEnvFile(path.join(cwdDir, ".env"), [...BUNDLED_TRUST_ROOT_ENV_LINES].join("\n")); - delete process.env.OPENCLAW_BROWSER_CONTROL_MODULE; - delete process.env.OPENCLAW_BUNDLED_HOOKS_DIR; - delete process.env.OPENCLAW_BUNDLED_PLUGINS_DIR; - delete process.env.OPENCLAW_BUNDLED_SKILLS_DIR; - delete process.env.OPENCLAW_SKIP_BROWSER_CONTROL_SERVER; + clearEnv(BUNDLED_TRUST_ROOT_ENV_KEYS); vi.spyOn(process, "cwd").mockReturnValue(cwdDir); loadCliDotEnv({ quiet: true }); - expect(process.env.OPENCLAW_BROWSER_CONTROL_MODULE).toBeUndefined(); - expect(process.env.OPENCLAW_BUNDLED_HOOKS_DIR).toBeUndefined(); - expect(process.env.OPENCLAW_BUNDLED_PLUGINS_DIR).toBeUndefined(); - expect(process.env.OPENCLAW_BUNDLED_SKILLS_DIR).toBeUndefined(); - expect(process.env.OPENCLAW_SKIP_BROWSER_CONTROL_SERVER).toBeUndefined(); + expectEnvUndefined(BUNDLED_TRUST_ROOT_ENV_KEYS); }); }); });