Skip to content

fix(review): stop model-written text quoting leaked secrets and personal data - #706

Merged
piekstra merged 1 commit into
mainfrom
fix/no-verbatim-sensitive-values
Nov 3, 2026
Merged

piekstra merged 1 commit into
mainfrom
fix/no-verbatim-sensitive-values

Conversation

@piekstra

@piekstra piekstra commented Nov 3, 2026

Copy link
Copy Markdown
Member

Problem

When a PR commits a credential or a personal or financial record (an account number, a balance, a name), reviewers quote the value verbatim in their findings. On a public repository those comments are public, so the review republishes the leak it found. Only a catalog reviewer whose own prompt forbade quoting avoided it. Every other reviewer, and every other catalog, did not.

Change

One shared output rule, llm.SensitiveValueRule (internal/llm/disclosure.go), is added to every prompt whose model-written text cr posts or logs:

Prompt Where its text goes
Reviewer findings contract (findingsOutputContractWithRelocations); coverage repair inherits it finding bodies (inline comments and the rollup), coverage constraints (rollup diagnostics)
Thread analysis (cr review and cr respond) reply_body and summary, posted as thread replies
Rollup contract review_event_rationale, dedupe_log reasons (run artifacts)
Selection contract reasoning and per-reviewer rationale (progress logs)
Dossier discussion summary summaries carried into reviewer prompts

The rule tells the model to name the kind of value and where it is (file, line, JSON path or key) instead of reproducing it. If a fragment is needed, it masks the value to at most its last 2 characters ("account number ending ..21"). The rule applies even when the value is the subject of the finding.

The change adds no regex redactor. A pattern cannot tell a real identifier from an example, and the problem is fixed at its source.

Prompt fingerprints include the prompt text, so cached tasks from earlier runs are invalidated and rerun under the new contract.

Tests

  • TestOutputContractsCarrySensitiveValueRule checks the selection (both budget modes), findings (plain and with relocation and symlink assignments) and rollup contracts.
  • TestEveryReviewPromptCarriesSensitiveValueRule sends one dry-run review through all six model stages: dossier summary, selection, thread analysis, reviewer, coverage repair and rollup. It asserts that every prompt the adapter receives carries the rule.
    • Mutation check: removing the rule from the thread-analysis prompt or the dossier prompt fails this test.
  • TestPromptForInputCarriesSensitiveValueRule (threadanalysis) and TestDossierDiscussionSummaryPromptCarriesSensitiveValueRule (dossier) are package-level checks.
  • make check passes.

Docs

  • New section "Sensitive Values In Model-Written Text" in docs/checkout-native-review-contract.md.
  • A note for agent authors in the README agent-definition section.

Eval

The privacy-inline-json case in piekstra/cr-reviewer-catalog (.codereview/benchmarks/review-lenses.yml, branch feat/review-lenses) measures this. Rerun it against this build once the reviewer profile's credential is available.

…nal data

When a change commits a credential or a personal or financial record,
reviewers quoted the value in their findings, so the review republished
the leak on the pull request.

Add one shared output rule, llm.SensitiveValueRule, to every prompt whose
model text cr posts or logs: the reviewer findings contract (which coverage
repair inherits), the rollup and selection contracts, thread analysis
replies and summaries, and the dossier discussion summary. The model names
the kind of value and its location and masks any fragment to at most its
last 2 characters, instead of reproducing it.

Contract tests assert the rule in each prompt, and a dry-run test drives a
review through every model stage and checks each prompt the adapter
receives. The rule fixes the source; there is no after-the-fact redactor,
since a pattern cannot tell a real identifier from an example.

Twapp-Session: 59d912f3-982f-402e-8bd8-3e31269a28b0

@piekstra-dev piekstra-dev left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Automated PR Review

Reviewed commit: 24f9028be347
Profile: reviewer - Posting as: piekstra-dev

Summary

Reviewer Findings
go:implementation-tests 0
structure:repo-health 0
policies:conventions 0
security:code-auditor 0
documentation:docs 0
go:implementation 0

Reviewer Coverage

Complete coverage details are retained in the local coverage.json artifact.

  • go:implementation-tests — complete (broad); scope: 7 files; examples: internal/dossier/dossier.go, internal/dossier/dossier_test.go, internal/llm/disclosure.go, internal/pipeline/prompts.go, internal/pipeline/sensitive_value_rule_test.go; 2 omitted; body-inspected: 7 files; examples: internal/dossier/dossier.go, internal/dossier/dossier_test.go, internal/llm/disclosure.go, internal/pipeline/prompts.go, internal/pipeline/sensitive_value_rule_test.go; 2 omitted; skipped: none; constraints: Reviewed from the diff between base and head; tests were not executed.
  • structure:repo-health — complete (broad); scope: 5 files; examples: docs/checkout-native-review-contract.md, internal/dossier/dossier.go, internal/llm/disclosure.go, internal/pipeline/prompts.go, internal/threadanalysis/threadanalysis.go; body-inspected: 5 files; examples: docs/checkout-native-review-contract.md, internal/dossier/dossier.go, internal/llm/disclosure.go, internal/pipeline/prompts.go, internal/threadanalysis/threadanalysis.go; skipped: none; constraints: Reviewed the diff only; tests were not run. The new test file and README are outside assigned scope and were seen only as diff context.
  • policies:conventions — complete (broad); scope: 2 files; examples: README.md, docs/checkout-native-review-contract.md; body-inspected: 2 files; examples: README.md, docs/checkout-native-review-contract.md; skipped: none; constraints: Shared cli-common docs were not available in context; review limited to repo-local docs consistency in the two assigned files. Code and tests were not in this assignment's scope.
  • security:code-auditor — complete (broad); scope: 4 files; examples: internal/dossier/dossier.go, internal/llm/disclosure.go, internal/pipeline/prompts.go, internal/threadanalysis/threadanalysis.go; body-inspected: 4 files; examples: internal/dossier/dossier.go, internal/llm/disclosure.go, internal/pipeline/prompts.go, internal/threadanalysis/threadanalysis.go; skipped: none; constraints: Reviewed diffs only; did not run tests or confirm that the llm import exists in each edited file.
  • documentation:docs — complete (broad); scope: 2 files; examples: README.md, docs/checkout-native-review-contract.md; body-inspected: 2 files; examples: README.md, docs/checkout-native-review-contract.md; skipped: none; constraints: Checked the docs against internal/llm/disclosure.go and the PR intent. Did not independently verify each prompt-to-field mapping in the table against the other Go call sites.
  • go:implementation — complete (broad); scope: 4 files; examples: internal/dossier/dossier.go, internal/llm/disclosure.go, internal/pipeline/prompts.go, internal/threadanalysis/threadanalysis.go; body-inspected: 4 files; examples: internal/dossier/dossier.go, internal/llm/disclosure.go, internal/pipeline/prompts.go, internal/threadanalysis/threadanalysis.go; skipped: none; constraints: none

0 PR discussion threads considered. 0 summarized; 0 resolved.


Completed in 1m 27s | ~$0.56 (est.) | claude-sonnet-5-5 | cr 0.10.362
Field Value
Model claude-sonnet-5-5
Reviewers go:implementation-tests, structure:repo-health, policies:conventions, security:code-auditor, documentation:docs, go:implementation
Engine claude_cli · claude-sonnet-5-5
Reviewed by cr · piekstra-dev
Duration 1m 27s wall · 2m 52s compute
Cost ~$0.56 (est.)
Pricing basis baseline-2026-10-02/pricing
Tokens 64 in / 8.8k out

Per-workstream usage

  • orchestrator-selection — claude-sonnet-5-5
    • In: 6
    • Out: 1.7k
    • Cache read: 24.4k
    • Cache create: 13.3k
    • Cost: ~$0.07 (est.)
    • Duration: 17s
  • go:implementation-tests — claude-sonnet-5-5
    • In: 8
    • Out: 1.1k
    • Cache read: 46.3k
    • Cache create: 16.1k
    • Cost: ~$0.08 (est.)
    • Duration: 25s
  • structure:repo-health — claude-sonnet-5-5
    • In: 10
    • Out: 1.3k
    • Cache read: 63.2k
    • Cache create: 14.9k
    • Cost: ~$0.09 (est.)
    • Duration: 30s
  • policies:conventions — claude-sonnet-5-5
    • In: 8
    • Out: 976
    • Cache read: 35.0k
    • Cache create: 15.0k
    • Cost: ~$0.08 (est.)
    • Duration: 21s
  • security:code-auditor — claude-sonnet-5-5
    • In: 8
    • Out: 970
    • Cache read: 42.9k
    • Cache create: 12.4k
    • Cost: ~$0.07 (est.)
    • Duration: 23s
  • documentation:docs — claude-sonnet-5-5
    • In: 8
    • Out: 1.1k
    • Cache read: 42.2k
    • Cache create: 12.0k
    • Cost: ~$0.07 (est.)
    • Duration: 26s
  • go:implementation — claude-sonnet-5-5
    • In: 10
    • Out: 1.2k
    • Cache read: 62.4k
    • Cache create: 14.1k
    • Cost: ~$0.08 (est.)
    • Duration: 18s
  • orchestrator-rollup — claude-sonnet-5-5
    • In: 6
    • Out: 430
    • Cache read: 52.6k
    • Cache create: 2.5k
    • Cost: ~$0.02 (est.)
    • Duration: 8s

@piekstra
piekstra merged commit e4e32ad into main Nov 3, 2026
12 checks passed
@piekstra
piekstra deleted the fix/no-verbatim-sensitive-values branch November 3, 2026 04:01
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.

2 participants