From ec375c6069a08f899d5452561991a843ba9d48a9 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Fri, 30 Oct 2026 02:24:10 -0700 Subject: [PATCH] test(cli): pin the host env out of a Linux workspace Claude seat (TASK-284) #2442 closed the env leak for public bwrap seats, and #2784 gave workspace seats their own bwrap path, reachable on Linux since #2820. That path's env is filtered on main, but its only pin was the CLAUDE_CONFIG_DIR check: a spawn that dropped that one key and passed the rest of the host env kept the suite green. The new case plants AWS_SECRET_ACCESS_KEY, GH_TOKEN and OPENAI_API_KEY, pins the exact env key set the jail receives, and checks that bwrap's argv sets HOME and nothing else. Test-only. Written by Rivet, a Commonly agent. Co-Authored-By: Claude Opus 5.5 --- .../adapters.claude.environment.test.mjs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cli/__tests__/adapters.claude.environment.test.mjs b/cli/__tests__/adapters.claude.environment.test.mjs index cfee1b482f..cb4fdae817 100644 --- a/cli/__tests__/adapters.claude.environment.test.mjs +++ b/cli/__tests__/adapters.claude.environment.test.mjs @@ -1527,6 +1527,28 @@ describe('claude adapter — a workspace seat on Linux', () => { expect(inner.join(' ')).toContain('Read(./.env)'); }); + // TASK-284: the host env stays outside the jail. The CLAUDE_CONFIG_DIR check + // above covers one key; a spawn that dropped that key and kept the rest of + // the host env passed it. This one plants secrets and pins the whole set. + test('gets only the safe part of the host env, never its secrets', async () => { + const planted = { + AWS_SECRET_ACCESS_KEY: 'host-secret', GH_TOKEN: 'host-secret', OPENAI_API_KEY: 'host-secret', + }; + const { impl, calls } = childImpl(); + await linuxSpawn(impl, { trust: 'workspace' }, { ...planted, LANG: 'C.UTF-8' }); + + expect(calls).toHaveLength(1); + const { args, opts } = calls[0]; + expect(Object.keys(opts.env).sort()).toEqual([ + 'CLAUDE_CODE_TMPDIR', 'HOME', 'LANG', 'PATH', 'TMPDIR', + 'XDG_CACHE_HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', + ]); + expect(Object.values(opts.env)).not.toContain('host-secret'); + // Nor through bwrap's argv: --setenv carries HOME and nothing else. + expect(args).not.toContain('host-secret'); + expect(flagTriples(args, '--setenv')).toEqual([['HOME', state]]); + }); + test('a write grant makes the project writable and keeps its .git read-only', async () => { const gitDir = path.join(cwd, '.git'); fs.mkdirSync(gitDir);