From d312d368b8e383cd257c404623fc5e781a8e5c63 Mon Sep 17 00:00:00 2001 From: Yaowei Zheng Date: Tue, 4 Aug 2026 18:10:43 +0800 Subject: [PATCH] fix(core): strip desktop-mode credentials from Agent command environments (#180) Co-authored-by: Claude Fable 5 --- .../environment/tools/command/session-manager.ts | 15 ++++++++++++++- packages/core/test/exec-session.test.ts | 15 ++++++++++++++- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/core/src/environment/tools/command/session-manager.ts b/packages/core/src/environment/tools/command/session-manager.ts index df85141..f0ed217 100644 --- a/packages/core/src/environment/tools/command/session-manager.ts +++ b/packages/core/src/environment/tools/command/session-manager.ts @@ -61,7 +61,20 @@ const HARDENED_ENV: NodeJS.ProcessEnv = { * self-development case may legitimately want the same data root — sharing state is a config * decision, whereas serving a deployment's code from a workspace checkout never is. */ -const STRIPPED_ENV_KEYS = new Set(["PORT", "HOST", "PENGUIN_CLI_ENTRY", "PENGUIN_WEB_DIST"]); +const STRIPPED_ENV_KEYS = new Set([ + "PORT", + "HOST", + "PENGUIN_CLI_ENTRY", + "PENGUIN_WEB_DIST", + // Desktop-mode process credentials and wiring: the shell's token authorizes the + // server shutdown endpoint (and desktop-login until redeemed), and the port file is + // the shell's private channel — neither is a user-facing setting, and leaking them + // into Agent-run commands would let a prompt-injected command stop the server. + "PENGUIN_DESKTOP_TOKEN", + "PENGUIN_PORT_FILE", + // Pinned seed password (tests/e2e): a credential, not a data-selection setting. + "PENGUIN_SEED_ADMIN_PASSWORD", +]); /** The host environment minus {@link STRIPPED_ENV_KEYS}. */ function hostEnvForChild(): NodeJS.ProcessEnv { diff --git a/packages/core/test/exec-session.test.ts b/packages/core/test/exec-session.test.ts index c0edc2e..0593e82 100644 --- a/packages/core/test/exec-session.test.ts +++ b/packages/core/test/exec-session.test.ts @@ -294,7 +294,15 @@ describe("exec_command — long-running command sessions", () => { }); describe("harness environment variables never reach a spawned command", () => { - const KEYS = ["PORT", "HOST", "PENGUIN_CLI_ENTRY", "PENGUIN_WEB_DIST"] as const; + const KEYS = [ + "PORT", + "HOST", + "PENGUIN_CLI_ENTRY", + "PENGUIN_WEB_DIST", + "PENGUIN_DESKTOP_TOKEN", + "PENGUIN_PORT_FILE", + "PENGUIN_SEED_ADMIN_PASSWORD", + ] as const; const saved: Partial> = {}; beforeEach(() => { @@ -305,6 +313,11 @@ describe("harness environment variables never reach a spawned command", () => { process.env.HOST = "127.0.0.1"; process.env.PENGUIN_CLI_ENTRY = "/opt/penguin/lib/dist/index.js"; process.env.PENGUIN_WEB_DIST = "/opt/penguin/web"; + // The desktop shell's process credentials (see design § "桌面端原型"): a leaked token + // would let an Agent-run command call the server's shutdown endpoint. + process.env.PENGUIN_DESKTOP_TOKEN = "secret-desktop-token"; + process.env.PENGUIN_PORT_FILE = "/tmp/port-file"; + process.env.PENGUIN_SEED_ADMIN_PASSWORD = "penguin-0000"; }); afterEach(() => { for (const k of KEYS) {