fix(security): harden compaction secret redaction - #49556
Conversation
|
Related: #13228 (merged base — redacts secrets from compaction input/output), #20998 (open — force-redact compression summaries), #43666 / #43940 (persistence-boundary redaction gaps). This PR extends the merged baseline by threading a force=True, redact_urls=True helper through all compaction summary boundaries (focus topic, previous summary, summarizer input/output, fallback) and broadens URL userinfo redaction to catch token-only userinfo (scheme://token@host). Verified against main: _redact_compaction_text and the redact_urls parameter are not yet present, so this is a genuine gap-fill rather than a duplicate of the merged fix. |
ac32fdf to
e495b6d
Compare
|
Thanks for closing the compaction-specific redaction gap. The premise remains valid on current The existing merged baseline, Automated hermes-sweeper review. |
…ndary Compaction summaries persist across sessions and re-enter every subsequent summarizer prompt, but every redact_sensitive_text() call in context_compressor.py used default mode: a no-op under security.redact_secrets:false, and opaque OAuth-callback / URL-userinfo credentials passed through even when enabled. The stored _previous_summary also re-entered the iterative-update prompt unredacted. Add _redact_compaction_text() — redact_sensitive_text(force=True, redact_url_credentials=True) — and thread it through all compaction text boundaries: serializer input (content + tool args), deterministic fallback summary, summarizer LLM output, manual + auto focus topics, the latest-user task snapshot, and _previous_summary re-entry. Note: force=True at this boundary intentionally overrides security.redact_secrets:false — that opt-out targets live tool output, not persisted summaries. Salvages the compaction half of #49556 (the redact.py strict-URL half landed independently via 75af6dc/62a00a739). Addresses #43666 item 2. Co-authored-by: AndrewMoryakov <topazd2@gmail.com>
…ndary (#69294) Compaction summaries persist across sessions and re-enter every subsequent summarizer prompt, but every redact_sensitive_text() call in context_compressor.py used default mode: a no-op under security.redact_secrets:false, and opaque OAuth-callback / URL-userinfo credentials passed through even when enabled. The stored _previous_summary also re-entered the iterative-update prompt unredacted. Add _redact_compaction_text() — redact_sensitive_text(force=True, redact_url_credentials=True) — and thread it through all compaction text boundaries: serializer input (content + tool args), deterministic fallback summary, summarizer LLM output, manual + auto focus topics, the latest-user task snapshot, and _previous_summary re-entry. Note: force=True at this boundary intentionally overrides security.redact_secrets:false — that opt-out targets live tool output, not persisted summaries. Salvages the compaction half of #49556 (the redact.py strict-URL half landed independently via 75af6dc/62a00a739). Addresses #43666 item 2. Co-authored-by: AndrewMoryakov <topazd2@gmail.com>
|
Merged via #69294 (commit 0acdf1d). The compaction half of your PR was rebuilt against main's landed strict-URL redaction (your redact.py half had been superseded by 75af6dc/62a00a739) and merged with Co-authored-by credit to you — strict redaction now applies at all nine compaction text boundaries. Thanks! |
…ndary (NousResearch#69294) Compaction summaries persist across sessions and re-enter every subsequent summarizer prompt, but every redact_sensitive_text() call in context_compressor.py used default mode: a no-op under security.redact_secrets:false, and opaque OAuth-callback / URL-userinfo credentials passed through even when enabled. The stored _previous_summary also re-entered the iterative-update prompt unredacted. Add _redact_compaction_text() — redact_sensitive_text(force=True, redact_url_credentials=True) — and thread it through all compaction text boundaries: serializer input (content + tool args), deterministic fallback summary, summarizer LLM output, manual + auto focus topics, the latest-user task snapshot, and _previous_summary re-entry. Note: force=True at this boundary intentionally overrides security.redact_secrets:false — that opt-out targets live tool output, not persisted summaries. Salvages the compaction half of NousResearch#49556 (the redact.py strict-URL half landed independently via 9179c57/04577d1bf). Addresses NousResearch#43666 item 2. Co-authored-by: AndrewMoryakov <topazd2@gmail.com>
…ndary (NousResearch#69294) Compaction summaries persist across sessions and re-enter every subsequent summarizer prompt, but every redact_sensitive_text() call in context_compressor.py used default mode: a no-op under security.redact_secrets:false, and opaque OAuth-callback / URL-userinfo credentials passed through even when enabled. The stored _previous_summary also re-entered the iterative-update prompt unredacted. Add _redact_compaction_text() — redact_sensitive_text(force=True, redact_url_credentials=True) — and thread it through all compaction text boundaries: serializer input (content + tool args), deterministic fallback summary, summarizer LLM output, manual + auto focus topics, the latest-user task snapshot, and _previous_summary re-entry. Note: force=True at this boundary intentionally overrides security.redact_secrets:false — that opt-out targets live tool output, not persisted summaries. Salvages the compaction half of NousResearch#49556 (the redact.py strict-URL half landed independently via 75af6dc/62a00a739). Addresses NousResearch#43666 item 2. Co-authored-by: AndrewMoryakov <topazd2@gmail.com>
Summary
Security notes
This preserves the existing default behavior where web URLs pass through unchanged for browsing/OAuth workflows. Only compaction uses
redact_urls=True, because compaction sends text to an auxiliary summarizer and persists the resulting summary.Tests
python -m pytest tests/agent/test_redact.py tests/agent/test_context_compressor.py tests/agent/test_compress_focus.py tests/agent/test_context_compressor_summary_continuity.py -qgit diff --check