Skip to content

feat(redaction): additive shape-based redaction on top of the existing redactor (see Consiliency/pmcp#234) - #310

Merged
ViperJuice merged 4 commits into
mainfrom
feat/234-additive-redaction
Sep 27, 2026
Merged

ViperJuice merged 4 commits into
mainfrom
feat/234-additive-redaction

Conversation

@ViperJuice

@ViperJuice ViperJuice commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds shape-based and separator-anchored secret redaction on top of the existing redactor, as the merged plan specifies (.consiliency/plans/detailed-234-redactor-shape-based-20260923-0921.md, merged in #288). See #234.

  • The existing redactor runs unchanged (_sanitize_base in src/pmcp/auth.py is its body, moved). The new rules (src/pmcp/redaction_additive.py) run over its output and can only replace more of it with [REDACTED].
  • The rules add:
    • vendor token shapes (AWS, Slack, Google, prefixed tokens);
    • JWTs, PEM private-key blocks and high-entropy runs;
    • values under a wider set of secret keys and in keyword lists;
    • URL userinfo, fragments and secret-keyed query values (judged after percent-decoding);
    • the rest of a value after an existing marker (the "glued run"). The glued run:
      • stops at whitespace, &, #, quotes, backslash, ,, ;, (, [, { and <;
      • is replaced only if it holds a letter or a digit;
      • follows on across markers (past an ARN body only when the second key is written with =);
      • keeps ARN bodies.
  • Entry points: sanitize_auth_diagnostic, PolicyManager.redact_secrets, and process_output on a string and on a structured result.

Commits:

  1. 58a7f95: the plan's embedded patch, applied with its own extractor to 4d2790f. pyproject.toml, src/ and tests/ equal wip/234-additive @ 4f2ca0a.
  2. 4593c78: the implementation board's N-1 and the documentation.
    • The follow-on across markers now goes past an ARN body that ends in a key's =, so token=Ab3cd:aws:s3:::x/token=Zz9!TAIL77 loses !TAIL77. There is a test, and the mutant is rev 19's rule.
    • The remaining shape is pinned and disclosed. The F-2 claim is narrowed in the docstring, the test comments and the plan's Residuals.
    • Docs: a CHANGELOG ### Security entry, SECURITY.md's redaction paragraph, and CONTRIBUTING's slow-tier note.
  3. cdd6607 and a follow-up commit: docs only. They narrow the N-1 residual to =-keyed follow-on values, and make CHANGELOG and SECURITY.md say "the text glued to the end of an existing marker (up to a delimiter; see the residuals)".

Guarantees (tests in tests/test_redaction_additive.py)

  1. Floor. The final output is the base output with markers written over parts of it. _sanitize_base equals the vendored redactor (tests/_main_redactor.py) on every corpus. Each unmarked stretch of the output occurs, in order, in the base output. A piece-level differential runs on tier 1 (default tier) and tier 2 (slow tier).
  2. JSON. A document the base pass keeps valid stays valid. Spans are confined to string values, widened to whole escapes. A dict stays a dict.
  3. Linear. A deterministic work counter grows linearly on all four entry points, and so does tracemalloc peak memory. A quantifier-structure sweep covers every redactor regex. tests/test_keyword_matcher.py passes unchanged with --cov.
  4. Markers. Markers are never split, swallowed, re-spelled or marked again, on E, P, POs and POd. A second pass only adds.

Purpose tests:

  • the end of generated values (full punctuation set and %xx) is removed on all surfaces;
  • partly-marked values;
  • values marked twice;
  • the credential corpus;
  • prose, code and log lines equal the base output where the rules add nothing.

Every fix has a mutant.

Residuals (disclosed in the plan)

  • Tails after a stop. A tail survives after ,, ;, &, #, (, [, { or <, or after punctuation alone before such a stop.
  • Flag form. The tail of a value under a whitespace-keyed flag (--password v) stays: 353 of 500 in the seat's corpus. Extending the rule was not approved.
  • Bearer + whitespace escape. token: bearer<escape>v keeps the token in 13 % (28 % bracketed) of that shape, on E, P and POd.
  • | and : fields. A code or log field after | or : is taken with the value the redactor marked. It is over-redaction: 168 E / 152 P of 120 000 natural key-word lines differ from rev 17.
  • ARN bodies. An ARN body under any key is kept. A key inside an ARN's resource, after the redactor's marker inside the ARN, keeps its tail (N-1 remainder).
  • :-keyed value after an ARN body. token=Ab3cd:aws:s3:::x/token:Zz9!TAIL77 keeps !TAIL77, as the existing redactor does. The tail survives in 875 E / 112 P of a 1,500-case matrix with an ARN in between, and 0 / 0 otherwise. The code does not treat : like =, because that would take a secretsmanager ARN's account and resource.
  • Other kept values.
  • Cost. Linear, but about 9 s on 1 MB of a joined key run on the diagnostic path. process_output is capped at 50 KB by default.

Verification (as read from the logs, dev0, npm/pnpm store variables unset)

check result
ruff check src/ tests/ All checks passed!
ruff format --check src/ tests/ 170 files already formatted
mypy src/ Success: no issues found in 52 source files
pytest tests/test_keyword_matcher.py --cov=pmcp 17 passed in 2.44s
default tier -m 'not live and not slow' 4707 passed, 3 skipped, 80 deselected in 513.48s (0:08:33)
slow tier -m 'slow and not live' 55 passed, 4735 deselected in 360.10s (0:06:00)

The slow tier's log starts with a faulthandler stack dump at 120 s. That dump comes from test_floor_on_tier_2, which took 128.77 s and passed.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LT8D8tRmExyQve69DCWRik


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

ViperJuice and others added 4 commits September 27, 2026 18:18
…g redactor (see #234)

The embedded patch of the merged plan
(.consiliency/plans/detailed-234-redactor-shape-based-20260923-0921.md),
applied with its own extractor to 4d2790f. pyproject.toml, src/ and
tests/ equal wip/234-additive @ 4f2ca0a.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LT8D8tRmExyQve69DCWRik
…at ends in a key (see #234)

The implementation board's N-1: an ARN body glued between two markers
stopped the follow-on, so a second key's tail after it survived
(`token=Ab3cd:aws:s3:::x/token=Zz9!TAIL77`). The chain now goes past an
ARN body when the body ends in a key's `=` glued to the next marker.
A key inside an ARN's resource after the redactor's marker inside the
ARN still keeps its tail; that is pinned and disclosed, and the F-2
claim is narrowed in the docstring, the test comments and the plan's
Residuals.

Docs: a CHANGELOG Security entry, SECURITY.md's redaction paragraph
widened to the added rules, and CONTRIBUTING's note on the slow tier.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LT8D8tRmExyQve69DCWRik
The follow-on across markers goes past an ARN body only when it ends in
a key's `=`. A second key written with `:` after an ARN body keeps its
tail (`token=Ab3cd:aws:s3:::x/token:Zz9!TAIL77`; 875 E / 112 P of the
implementation board's 1 500-case matrix with an ARN between, 0 / 0
without). The plan's Residuals sentence is narrowed and the `:`-keyed
shape gets its own line; CHANGELOG and SECURITY.md now describe the
glued text as "up to a delimiter; see the residuals". No code change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LT8D8tRmExyQve69DCWRik
The plan's summary of when "a value marked twice keeps no tail" holds
contradicted itself; it now states the condition once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LT8D8tRmExyQve69DCWRik
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant