Skip to content

fix(core): close env leak regressions in claude subprocess + remediation copy#1154

Closed
LaplaceYoung wants to merge 1 commit intocoleam00:devfrom
LaplaceYoung:fix/env-leak-hardening-1150-1149
Closed

fix(core): close env leak regressions in claude subprocess + remediation copy#1154
LaplaceYoung wants to merge 1 commit intocoleam00:devfrom
LaplaceYoung:fix/env-leak-hardening-1150-1149

Conversation

@LaplaceYoung
Copy link
Copy Markdown
Contributor

@LaplaceYoung LaplaceYoung commented Apr 13, 2026

Summary

This PR addresses two high-impact env leak issues in Claude provider paths:

  1. Fixes regression of #1030: CLAUDE_CODE_ENTRYPOINT still leaks to Claude subprocess env in v0.3.6 — nested-Claude-Code hang reproduces #1150 by hardening subprocess env sanitization so nested Claude markers cannot reappear in options.env.
  2. Fixes EnvLeakError suggested-fix command hardcoded to ANTHROPIC_API_KEY regardless of actual offending key #1149 by generating EnvLeakError remediation commands from the actual offending keys instead of hardcoding ANTHROPIC_API_KEY.

It also aligns with the zero-leak direction discussed in #1135.

Changes

  • packages/core/src/providers/claude.ts

    • Added sanitizeSubprocessEnv() defense-in-depth filtering.
    • Always strips nested-session/debugger markers from final subprocess env:
      • CLAUDECODE
      • CLAUDE_CODE_* (except auth vars: CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX)
      • NODE_OPTIONS, VSCODE_INSPECTOR_OPTIONS
    • Sanitization now runs both:
      • when building base env from process.env
      • after merging requestOptions.env (prevents re-injection).
  • packages/core/src/utils/env-leak-scanner.ts

    • formatLeakError() now interpolates offending keys from report.findings[].keys.
    • Remediation command now emits key-accurate grep expression, e.g.:
      • grep -vE '^(GEMINI_API_KEY|OPENAI_API_KEY)=' .env > .env.tmp && mv .env.tmp .env
    • Message wording now switches between singular/plural (key / keys).
  • Added regression tests:

    • packages/core/src/providers/claude.test.ts
      • verifies nested marker stripping while preserving normal/user env keys
      • verifies requestOptions.env cannot re-inject CLAUDECODE / CLAUDE_CODE_ENTRYPOINT
    • packages/core/src/utils/env-leak-scanner.test.ts
      • verifies dynamic single-key command generation
      • verifies dynamic multi-key command generation

Validation

  • bun test packages/core/src/providers/claude.test.ts
  • bun test packages/core/src/utils/env-leak-scanner.test.ts

Both pass locally.

Closes #1149
Closes #1150
Refs #1135

Summary by CodeRabbit

  • Bug Fixes
    • Improved environment variable security by filtering sensitive variables and preventing their injection into subprocess operations.
    • Enhanced error messages with auto-generated remediation commands for environment variable configuration issues.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 13, 2026

📝 Walkthrough

Walkthrough

This PR implements environment variable sanitization to prevent Claude-specific marker variables from leaking into subprocess environments and updates error remediation commands to dynamically reference actual offending keys found.

Changes

Cohort / File(s) Summary
Claude provider env sanitization
packages/core/src/providers/claude.ts, packages/core/src/providers/claude.test.ts
Introduced sanitizeSubprocessEnv() function to filter out blocked keys (CLAUDECODE, NODE_OPTIONS, VSCODE_INSPECTOR_OPTIONS) and strip all CLAUDE_CODE_* variables except whitelisted ones (CLAUDE_CODE_OAUTH_TOKEN, CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX). Updated buildSubprocessEnv() and sendQuery() to apply sanitization before passing env to SDK. Test coverage added to verify stripped keys become undefined and user-provided keys are preserved.
Env leak scanner remediation
packages/core/src/utils/env-leak-scanner.ts, packages/core/src/utils/env-leak-scanner.test.ts
Added helper functions to escape key strings for regex/grep usage and build dynamic remediation commands. Updated formatLeakError() to generate context-specific grep -vE filters targeting detected offending keys instead of hardcoded ANTHROPIC_API_KEY. Tests verify single-key and multi-key regex patterns are correctly generated.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Poem

🐰 Hops of joy! The markers flee—
Claude's secrets locked, subprocess free!
With grep that knows each guilty key,
Leaks are plugged, as it should be! 🔐

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main changes: fixing env leak regressions in Claude subprocess and remediation copy. It is specific, concise, and directly related to the primary objectives of the changeset.
Description check ✅ Passed The PR description comprehensively covers the summary, changes, and validation. While it omits some template sections (UX Journey, Architecture Diagram, Label Snapshot, etc.), the critical sections—Summary, Changes, and Validation—are complete and well-detailed.
Linked Issues check ✅ Passed The PR fully addresses both linked issues: #1150 (hardened subprocess env sanitization preventing CLAUDE_CODE_* marker re-injection) and #1149 (dynamic remediation command generation from actual offending keys). All key requirements are met.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issues: env sanitization, remediation command generation, and comprehensive test coverage. No unrelated modifications are present.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/providers/claude.test.ts (1)

449-486: ⚠️ Potential issue | 🟡 Minor

Put the env restore in finally.

This test mutates global process.env and only cleans it up on the happy path. If sendQuery() or any assertion starts failing, the leaked CLAUDE_CODE_* / NODE_OPTIONS values bleed into later cases and make the suite order-dependent. Wrap the mutation block in try/finally or move restoration to afterEach.

Based on learnings: Applies to **/*.test.ts : Keep tests deterministic — no flaky timing or network dependence without guardrails; ensure local validation commands (bun run validate) map directly to CI expectations (Determinism + Reproducibility).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/providers/claude.test.ts` around lines 449 - 486, The test
"subprocess env keeps user keys but strips nested Claude markers" mutates
process.env but only restores it on the happy path; wrap the mutation and test
execution (the portion that sets process.env values and calls client.sendQuery /
consumes mockQuery) in a try/finally so the cleanup of CUSTOM_USER_KEY,
CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_OAUTH_TOKEN, and NODE_OPTIONS always runs in
the finally block (or alternatively move the restoration logic to an afterEach),
ensuring mockQuery and client.sendQuery behavior remains isolated and no env
leaks occur across tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/core/src/providers/claude.ts`:
- Around line 64-70: sanitizeSubprocessEnv currently does case-sensitive checks
against BLOCKED_SUBPROCESS_ENV_KEYS and CLAUDE_CODE_AUTH_VARS and uses
startsWith, which allows lowercase variants on Windows to bypass filtering;
update sanitizeSubprocessEnv to normalize the environment variable name (e.g.,
const keyUpper = key.toUpperCase()) before testing with
BLOCKED_SUBPROCESS_ENV_KEYS.has(keyUpper) and
keyUpper.startsWith('CLAUDE_CODE_') so Windows-style lowercase keys are properly
filtered, but continue to set sanitized[key] = value using the original key when
not blocked; reference sanitizeSubprocessEnv, BLOCKED_SUBPROCESS_ENV_KEYS and
CLAUDE_CODE_AUTH_VARS.

In `@packages/core/src/utils/env-leak-scanner.ts`:
- Around line 126-139: The current buildRemediationCommand uses a global grep
fallback and collectOffendingKeys that can produce a generic ANTHROPIC_API_KEY
fallback; change it to build per-report.findings entries: iterate
report.findings and for each finding by its finding.file, if finding.keys
contains concrete keys (filter out keys starting with '[') build a file-specific
grep command using escapeRegexForGrep for those keys and include it in the
returned string, and if a finding has no concrete keys (only unreadable markers)
return a permission-oriented remediation message for that file (e.g.,
instructing to restrict file permissions or remove the file from the repo)
instead of a grep command; update or remove the global ANTHROPIC_API_KEY
fallback and ensure collectOffendingKeys is only used where appropriate,
returning a multi-line string that combines each per-file command/message.

---

Outside diff comments:
In `@packages/core/src/providers/claude.test.ts`:
- Around line 449-486: The test "subprocess env keeps user keys but strips
nested Claude markers" mutates process.env but only restores it on the happy
path; wrap the mutation and test execution (the portion that sets process.env
values and calls client.sendQuery / consumes mockQuery) in a try/finally so the
cleanup of CUSTOM_USER_KEY, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_OAUTH_TOKEN, and
NODE_OPTIONS always runs in the finally block (or alternatively move the
restoration logic to an afterEach), ensuring mockQuery and client.sendQuery
behavior remains isolated and no env leaks occur across tests.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 536d9a8e-982a-45dd-a59b-e0c5e28541c0

📥 Commits

Reviewing files that changed from the base of the PR and between eb75ab6 and b96a036.

📒 Files selected for processing (4)
  • packages/core/src/providers/claude.test.ts
  • packages/core/src/providers/claude.ts
  • packages/core/src/utils/env-leak-scanner.test.ts
  • packages/core/src/utils/env-leak-scanner.ts

Comment thread packages/core/src/providers/claude.ts
Comment thread packages/core/src/utils/env-leak-scanner.ts
@Wirasm
Copy link
Copy Markdown
Collaborator

Wirasm commented Apr 13, 2026

Closing — both fixes in this PR are superseded by #1169 (merged into dev). The nested-session marker leak (#1150) is now handled structurally: stripCwdEnv() strips markers at boot, and executableArgs: ['--no-env-file'] prevents the Claude Code subprocess from re-loading them from CWD. The remediation copy was removed along with the entire scanner. Thanks for the solid regression analysis and the sanitizeSubprocessEnv() defense-in-depth approach — it confirmed the leak path that led to the structural fix.

@LaplaceYoung
Copy link
Copy Markdown
Contributor Author

Ok!Wish u all the best

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants