AUR-432 — Revisar código privado sem vazar segredo
Documento histórico do board. Pode descrever componentes removidos; para uso atual, consulte o guia do produto.
aurumcode review --base <ref> reviews private code that may contain
credentials. This card wires the single AUR-009 redaction filter
(internal/security/redaction) into every path a secret could take out of
that command: a secret present in the reviewed diff never reaches the
model, and a secret echoed back by the model never reaches stdout, stderr,
the prompt capture, or the report. The finding that cites the secret's
line still arrives, because the filter replaces values, not context.
Command
aurumcode review --base <ref> [--fail-on <level>] [--modelo <nome>]
Flags, output format, and exit codes are exactly those published by
AUR-430 (docs/specs/AUR-430.md), AUR-431 (--fail-on), and AUR-436
(--modelo). This card changes none of them: for a secret-free review,
stdout and stderr are byte-identical to the previous behavior.
Exit codes
0— the review ran; findings (if any) printed.1— the review itself failed (provider, parse, repository error).2— usage error.3—--fail-ongate closed (AUR-431).
Redaction never changes an exit code: it sanitizes content, it does not judge it.
The three wiring points
- Send path (
internal/review/reviewer.go): the diff — the untrusted material — is redacted before the prompt is assembled (redactDiff). Composition matters, not just coverage: a diff line carries a+/-/space marker and the filter's header rule is anchored at line start, so redacting the assembled prompt would let+Authorization: Bearer xthrough verbatim. Each line's marker is stripped, the bodies are redacted together (anchored header rules and the multi-line private-key rule both apply), and the markers are re-attached; a collapsed multi-line secret fails closed without markers. Secret values become[REDACTED]; key names, header names, file paths and line structure survive, so the model still sees that a credential sits on a line and can report it usefully. - Model output boundary (
internal/review/reviewer.go): the parsed response — untrusted model output that may echo a secret — is redacted field by field (file, message, suggestion, summary, comments) at the boundary. Prose fields get the same marker-stripping treatment, because a model quoting the offending diff line echoes it marker and all. This runs before the AUR-434 rule-citation suffix is appended: the suffix(rule security/hardcoded-secret: <title>)comes from the trusted embedded catalog and would itself be rewritten by the filter'ssecret: valuerule, so redacting first is what keeps the published output byte-stable for secret-free reviews. - Process sinks (
cmd/aurumcode/main.go): stderr writes throughredaction.NewWriter(every canonical stderr line is filter-identity, pinned byTestStderrLinesSurviveTheRedactionWriter); diff-derived notices passFilter.Redactbefore stdout. The--modeloselection note masks anLLM_BASE_URLuserinfo password at the origin withurl.Redacted()(redactedEndpoint), and the stderr writer replaces the whole userinfo component on top of that.
The AURUM_SECRET_CANARY environment value, when present, is registered
as an exact-match secret (redaction.FromEnv) in both the reviewer's
filter and the CLI's sink filter. There is no response cache in this
path (llm.NewOrchestrator(provider, nil, nil)); the only persisted
prompt artifact is the opt-in capture below, which is written after
redaction.
Observing the guarantee: AURUMCODE_PROMPT_CAPTURE
With the deterministic offline provider (AURUMCODE_LLM_FIXTURE),
setting AURUMCODE_PROMPT_CAPTURE=<file> makes the provider write the
exact prompt it received to <file> (mode 0600) before answering. That
is the observability hook the acceptance uses to prove nothing secret
leaves the process — with the wiring in place the captured prompt is
already redacted. Live providers have no equivalent; the wire-level proof
for them is tests/integration/AUR-432.go, which records the raw bytes a
loopback endpoint receives.
Offline, secret-free example
cd "$(mktemp -d)"
cat > response.json <<'EOF'
{
"issues": [
{
"file": "config/demo-tokens.txt",
"line": 4,
"severity": "error",
"rule_id": "security/hardcoded-secret",
"message": "A hardcoded credential is committed in plain text at this line."
}
],
"summary": "The change commits a plaintext credential."
}
EOF
cd <repo-under-review>
AURUMCODE_LLM_FIXTURE=$OLDPWD/response.json \
AURUMCODE_PROMPT_CAPTURE=$OLDPWD/prompt.txt \
aurumcode review --base HEAD~1
# prompt.txt now holds what the model was shown: values [REDACTED],
# context intact. Every value in this example is synthetic.
Failure diagnosis
- A planted value found in
prompt.txt, stdout, or stderr is the behavioral RED this card guards against (AUR-432/AC-001/behavior-missingin the acceptance vocabulary). tests/acceptance/AUR-432.sh AC-001-MUT-001proves the guard bites at both points: disabling the send-path diff redaction makes the planted secret appear in the capture, and disabling the output-boundary redaction makes the model-echoed secret appear on stdout; either way the accept fails, and restoring reproduces the GREEN.- The e2e (
tests/e2e/AUR-432.sh) also pins that the AUR-437 fixture builder refuses credential-shaped content (git-demo/non-synthetic-secret), which is why credential shapes are exercised in-process bytests/unit/AUR-432.gowith runtime-assembled values, never through tracked files.
Known residual notes (review 2026-08-13, non-blocking)
internal/llm/httpbase/client.gologs vialog.Printf(default stderr), bypassing the CLI's stderr redaction writer. Today those lines carry only method/path/provider names, and httpbase's ownRedactSecrettruncates response bodies; wiringlog.SetOutputto the redaction writer belongs to whichever card next touches httpbase.AURUMCODE_PROMPT_CAPTUREis a truncating file write gated by an environment variable in shipped code. It is confined to the offlineFakeProvider(live providers have no capture path) and, with the wiring in place, the captured prompt is already redacted.
Validation
- Unit:
tests/unit/AUR-432.go::TestAUR432(send path, private-key block, output boundary, determinism — in process). - Integration:
tests/integration/AUR-432.go::IntegrationAUR432(real binary; offline capture and wire-level loopback proof; userinfo note). - E2E:
tests/e2e/AUR-432.sh::E2EAUR432(runtime-planted secrets in a rebuilt fixture repository, full user journey). - Acceptance:
tests/acceptance/AUR-432.sh(AC-001, byte-pinned published formats, MUT-001).