Skip to content

fix(security): force-redact context compression summaries - #20998

Closed
qWaitCrypto wants to merge 1 commit into
NousResearch:mainfrom
qWaitCrypto:fix/force-redact-context-compressor-summaries
Closed

fix(security): force-redact context compression summaries#20998
qWaitCrypto wants to merge 1 commit into
NousResearch:mainfrom
qWaitCrypto:fix/force-redact-context-compressor-summaries

Conversation

@qWaitCrypto

Copy link
Copy Markdown
Contributor

What does this PR do?

This fixes a security boundary gap in context compaction summary generation.

ContextCompressor._serialize_for_summary() prepares conversation history for the auxiliary summarizer, and the resulting summary may persist across later compactions. The method docstring already states that content is redacted before serialization so secrets do not leak into the auxiliary model or into compacted summaries. In practice, the summary path was calling redact_sensitive_text() without force=True, which meant default installations with global redaction disabled could still send raw secrets through this path.

This PR makes context-compression summaries always enforce redaction at that boundary:

  • message content serialized for the auxiliary summarizer is force-redacted
  • assistant tool-call arguments serialized for the auxiliary summarizer are force-redacted
  • auxiliary summarizer output is force-redacted before being stored as the compacted summary

This keeps the existing user-facing redaction preference unchanged everywhere else while ensuring the compaction summary path never returns raw secrets.

Related Issue

No tracked issue.

This fixes a source-level security mismatch between the documented behavior of ContextCompressor and the actual summary-serialization path.

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • Updated agent/context_compressor.py to call redact_sensitive_text(..., force=True) when serializing message content for summary generation
  • Updated agent/context_compressor.py to call redact_sensitive_text(..., force=True) when serializing assistant tool-call arguments for summary generation
  • Updated agent/context_compressor.py to force-redact auxiliary summarizer output before storing the compacted summary
  • Added regression tests in tests/agent/test_context_compressor.py covering:
    • forced redaction of serialized message content when global redaction is disabled
    • forced redaction of serialized tool-call arguments when global redaction is disabled
    • forced redaction of summarizer output when global redaction is disabled

How to Test

  1. Run python -m pytest tests/agent/test_context_compressor.py -q
  2. Verify the suite passes, including the new TestSummarySerializationRedaction coverage
  3. Optionally reproduce manually by disabling global redaction and confirming _serialize_for_summary() / _generate_summary() no longer retain raw secrets in serialized summary content

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: Windows 11 (WSL)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

Validation run:

python -m pytest tests/agent/test_context_compressor.py -q
71 passed, 71 warnings in 68.73s

Manual verification with global redaction disabled confirmed the fix closes both leak paths:

  • _serialize_for_summary() no longer retains the original secret value in serialized message content, tool results, or tool-call arguments
  • _generate_summary() no longer retains the original secret value when the auxiliary summarizer echoes it back

@alt-glitch alt-glitch added type/security Security vulnerability or hardening P1 High — major feature broken, no workaround comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels May 7, 2026

@egilewski egilewski left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Recommendation: request changes

I reviewed this against current GitHub main 36ae958473b8530ffb1a395c4944b8cdbcae82fe, PR base 49c3c2e0d37c96dc593a807a5e81fdf4f0aa3d85, and PR head 81a54d1a31f1b4789866eb5fb74704efcd16d8d2.

Validation:

  • gh pr view 20998 --repo NousResearch/hermes-agent --json mergeable,mergeStateStatus,baseRefOid,headRefOid: fails mergeability with CONFLICTING / DIRTY on the same PR head.
  • git fetch --no-write-fetch-head --no-tags upstream main:refs/remotes/upstream/main +pull/20998/head:refs/remotes/upstream/pr/20998: fetched current main and PR head successfully.
  • git rev-list --left-right --count refs/remotes/upstream/main...refs/remotes/upstream/pr/20998: reports 4068 1, so this branch is far behind current main.
  • git merge-tree --write-tree refs/remotes/upstream/main refs/remotes/upstream/pr/20998: fails with a content conflict in agent/context_compressor.py.

Finding:
The PR cannot currently be reviewed or merged safely against current main because the security change conflicts in agent/context_compressor.py. Please rebase or port the patch onto current main, then rerun the focused context-compressor tests from the PR (python -m pytest tests/agent/test_context_compressor.py -q) so the forced-redaction behavior can be validated on the actual merge candidate.

Signed: GPT-5.5-xhigh in Codex

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jun 29, 2026
@teknium1

teknium1 commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Thanks for the careful write-up and tests — but we're going to pass on this one.

The premise doesn't hold up: if a value is genuinely a secret, it's already redacted upstream when it first flows through tool output / logs (redaction is on by default). By the time _serialize_for_summary / _generate_summary run, they're reading already-redacted history, so force=True here just re-runs the regex scan on every compaction for content that's either already masked or that the user deliberately chose to keep raw.

That's the other half: security.redact_secrets: false is a documented, opt-in global preference that logs a downgrade warning at startup. A user who sets it is explicitly asking for raw values. force=True silently overrides that choice at one arbitrary boundary while honoring it everywhere else — inconsistent, and not a decision we want to make on the operator's behalf.

No behavior change needed on main. Appreciate the contribution regardless.

@teknium1 teknium1 closed this Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P1 High — major feature broken, no workaround sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/security Security vulnerability or hardening

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants