fix: extend trajectory JSONL redaction to cover Google, AWS, Daytona, and header patterns (#537)#585
Conversation
08f055a to
8a6cee7
Compare
Thermo-nuclear code quality reviewWhat this PR is trying to solveThis PR is trying to close #537 by expanding The direction is right, but I would not merge this yet. There are still high-confidence gaps against the issue this PR claims to close. Blocking findings
Test and maintainability gapsThe new tests are useful, but they mostly test Also, per this repo's Verdict: request changes. The abstraction is small and the file stays well under 1k lines, but the implementation still misses core cases from #537 and leaves the documented experiment-health audit ambiguous. |
Addresses bingran-you CHANGES_REQUESTED on PR #585 / issue #537. Finding 1 — raw-text header/kv forms leaked. Redaction only matched JSON keys ("x-api-key": "v"). Now also covers raw forms agents print into trajectories (env dumps, curl -v, request logs): x-api-key: v, api-key=v, api_key: v, x-goog-api-key: v, authorization: <scheme> v / bare value. The field name is kept; the value is dropped. Finding 2 — audit-sensitive prefixes survived. Google/AWS/Daytona/OpenAI tokens were redacted but kept their live prefix (AIzaSy***), so the v0.5 §14 leak audit (grep AIzaSy/dtn_) still flagged them. Now the whole token is replaced, prefix included. Updated the audit grep in docs/v05-e2e-testing-guide.md to match live-key *shapes* (suffix length), not bare variable names, with a note that names are not secrets. Header-pattern hardening from this work: underscore api_key gets a leading boundary so it can't match the tail of GEMINI_API_KEY; value classes exclude backslash (a JSON-escaped \n can't swallow the next header) and '*' (an already-redacted value isn't re-matched); authorization splits scheme/bare with a lookahead to avoid double-redaction. Tests run through Trajectory.to_jsonl() with raw env/header/kv text, not only the helper on idealized JSON. Module docstring names PR #585. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses bingran-you CHANGES_REQUESTED on PR #585 / issue #537. Finding 1 — raw-text header/kv forms leaked. Redaction only matched JSON keys ("x-api-key": "v"). Now also covers raw forms agents print into trajectories (env dumps, curl -v, request logs): x-api-key: v, api-key=v, api_key: v, x-goog-api-key: v, authorization: <scheme> v / bare value. The field name is kept; the value is dropped. Finding 2 — audit-sensitive prefixes survived. Google/AWS/Daytona/OpenAI tokens were redacted but kept their live prefix (AIzaSy***), so the v0.5 §14 leak audit (grep AIzaSy/dtn_) still flagged them. Now the whole token is replaced, prefix included. Updated the audit grep in docs/v05-e2e-testing-guide.md to match live-key *shapes* (suffix length), not bare variable names, with a note that names are not secrets. Header-pattern hardening from this work: underscore api_key gets a leading boundary so it can't match the tail of GEMINI_API_KEY; value classes exclude backslash (a JSON-escaped \n can't swallow the next header) and '*' (an already-redacted value isn't re-matched); authorization splits scheme/bare with a lookahead to avoid double-redaction. Tests run through Trajectory.to_jsonl() with raw env/header/kv text, not only the helper on idealized JSON. Module docstring names PR #585. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
These files (test_sandbox_hardening.py, test_usage_proxy_gemini_rewrite.py) were committed to main unformatted (via #unrelated PRs) and trip 'ruff format --check' on this PR's merge-commit CI run. Formatting them here to unblock #585; no logic change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bfb7293 to
8520eaa
Compare
…paced API keys (#585) Addresses the deeper review round on PR #585 / issue #537. 1. Redaction was only applied to llm_trajectory.jsonl (Trajectory.to_jsonl), never to acp_trajectory.jsonl — the file the PR set out to protect. Added a reusable redact_acp_trajectory_jsonl() helper and wired it into all three ACP-trajectory write sites (rollout.py local write + verifier-publish tmpfile, hosted_env.py), so an agent running `env` no longer leaks secrets there. 2. The underscore `api_key` rule had a (?<![A-Za-z0-9_]) lookbehind that left namespaced env vars (GEMINI_API_KEY=..., {"openai_api_key": ...}) unredacted. Removed it; the \1 capture preserves the key name, so GEMINI_API_KEY=secret -> GEMINI_API_KEY=***REDACTED*** with no over-redaction. Regression tests assert the raw secret is absent from the written file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Follow-up (
|
… and header patterns (#537) Trajectory.to_jsonl only redacted sk-ant- (Anthropic) and Bearer headers. Other production credential patterns could leak into acp_trajectory.jsonl if they entered trajectory content (e.g. agent running `env` as a tool call). Added redaction for: - Google AI / Gemini: AIzaSy* - AWS access keys: AKIA* / ASIA* (anchored to exact 20-char length) - Daytona: dtn_* (>=16 char suffix to avoid short identifiers) - OpenAI project keys: sk-proj-* - HTTP headers: x-api-key, api-key (Azure) Extracted patterns into a compiled _REDACTION_PATTERNS list and a reusable redact_trajectory_text() function. All test fixtures use obviously-fake keys. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses bingran-you CHANGES_REQUESTED on PR #585 / issue #537. Finding 1 — raw-text header/kv forms leaked. Redaction only matched JSON keys ("x-api-key": "v"). Now also covers raw forms agents print into trajectories (env dumps, curl -v, request logs): x-api-key: v, api-key=v, api_key: v, x-goog-api-key: v, authorization: <scheme> v / bare value. The field name is kept; the value is dropped. Finding 2 — audit-sensitive prefixes survived. Google/AWS/Daytona/OpenAI tokens were redacted but kept their live prefix (AIzaSy***), so the v0.5 §14 leak audit (grep AIzaSy/dtn_) still flagged them. Now the whole token is replaced, prefix included. Updated the audit grep in docs/v05-e2e-testing-guide.md to match live-key *shapes* (suffix length), not bare variable names, with a note that names are not secrets. Header-pattern hardening from this work: underscore api_key gets a leading boundary so it can't match the tail of GEMINI_API_KEY; value classes exclude backslash (a JSON-escaped \n can't swallow the next header) and '*' (an already-redacted value isn't re-matched); authorization splits scheme/bare with a lookahead to avoid double-redaction. Tests run through Trajectory.to_jsonl() with raw env/header/kv text, not only the helper on idealized JSON. Module docstring names PR #585. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…paced API keys (#585) Addresses the deeper review round on PR #585 / issue #537. 1. Redaction was only applied to llm_trajectory.jsonl (Trajectory.to_jsonl), never to acp_trajectory.jsonl — the file the PR set out to protect. Added a reusable redact_acp_trajectory_jsonl() helper and wired it into all three ACP-trajectory write sites (rollout.py local write + verifier-publish tmpfile, hosted_env.py), so an agent running `env` no longer leaks secrets there. 2. The underscore `api_key` rule had a (?<![A-Za-z0-9_]) lookbehind that left namespaced env vars (GEMINI_API_KEY=..., {"openai_api_key": ...}) unredacted. Removed it; the \1 capture preserves the key name, so GEMINI_API_KEY=secret -> GEMINI_API_KEY=***REDACTED*** with no over-redaction. Regression tests assert the raw secret is absent from the written file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
589597a to
35f8782
Compare
…paced API keys (#585) Addresses the deeper review round on PR #585 / issue #537. 1. Redaction was only applied to llm_trajectory.jsonl (Trajectory.to_jsonl), never to acp_trajectory.jsonl — the file the PR set out to protect. Added a reusable redact_acp_trajectory_jsonl() helper and wired it into all three ACP-trajectory write sites (rollout.py local write + verifier-publish tmpfile, hosted_env.py), so an agent running `env` no longer leaks secrets there. 2. The underscore `api_key` rule had a (?<![A-Za-z0-9_]) lookbehind that left namespaced env vars (GEMINI_API_KEY=..., {"openai_api_key": ...}) unredacted. Removed it; the \1 capture preserves the key name, so GEMINI_API_KEY=secret -> GEMINI_API_KEY=***REDACTED*** with no over-redaction. Regression tests assert the raw secret is absent from the written file. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
35f8782 to
8d0f203
Compare
Summary
Trajectory.to_jsonlonly redacted two secret patterns (sk-ant-,Bearer). Every other production credential pattern could leak intoacp_trajectory.jsonlif it entered trajectory content (e.g. agent runningenvas a tool call).Added redaction for:
AIzaSy*AKIA*/ASIA*(anchored to exact 20-char length to avoid matchingASIAPACIFICetc.)dtn_*(≥16 char suffix to avoid short identifiers)sk-proj-*x-api-key,api-key(Azure)Extracted patterns into a compiled
_REDACTION_PATTERNSlist and a reusableredact_trajectory_text()function for downstream callers.Test plan
Trajectory.to_jsonl(redact_keys=True/False)Closes #537
🤖 Generated with Claude Code