AUR-450 — aurumcode review --seguranca: the pass names what it searched
Documento histórico do board. Pode descrever componentes removidos; para uso atual, consulte o guia do produto.
Command
aurumcode review --base <ref> [--fail-on <level>] [--modelo <nome>] [--seguranca]
Same command AUR-435/AUR-442/AUR-449 published. This card adds no flag and
changes no byte of stdout; it adds one line to stderr, printed only when
--seguranca runs.
The measured defect (2026-08-14 re-test)
Code carrying hashlib.md5(password) (security/weak-crypto) and
"<b>" + name + "</b>" (security/xss) gets Security findings
(standards/security-review): No security findings. with exit 0. Five of
the embedded catalog's eight security-category rules are metadata only
(internal/review/rules/security.yml) — AUR-442 gave three of them
(sql-injection, command-injection, hardcoded-secret) a real matcher
and documented, scoped reasons for leaving the other five patternless — but
the CLI never says which rules ran. A user reads the silent
No security findings. as full coverage. This is not a matcher false
positive or false negative; it is the output omitting its own scope.
The decision
--seguranca now prints, on stderr, exactly one additional line: how many
and which security-category rules of the embedded catalog actually carry a
matcher, against how many the category declares in total. The figures are
derived from the catalog at run time
(internal/review.RulesLoader.AppliedInCategory, cmd/aurumcode/main.go's
printSecurityCoverage), never a hand-written list — a list typed once
into a doc or a stderr format string would silently go stale the next time
a card adds, removes, or gives a rule a matcher (internal/review/rules/
security.yml's own pattern: field is the single source of truth; nothing
here duplicates it). tests/acceptance/AUR-442.sh's own MUT-001 case
(which removes security/hardcoded-secret's matcher) demonstrates the
derivation empirically: rerun on this card's tree, the note recomputes to
"2 of 8" without a single line of AUR-450 code changing.
The note prints before the findings are known and is catalog-derived, not diff-derived — it does not depend on what the reviewed diff contains. That is deliberate: the empty case ("No security findings.") and the non-empty case must carry the byte-identical note, because the defect this card fixes is specifically the empty case being misread as full coverage.
Why stderr, not stdout
tests/acceptance/AUR-449.sh pins an exact sha256 of git-demo's
--seguranca stdout with a configured provider
(63c649af1c90e38b473e1bd45b4152b1f96ecad17d5d9c05c17bb94df7b8240f) as a
regression guard. That pin is not this card's to break: internal/review
and cmd/aurumcode are this card's paths, but the byte contract AUR-449
already published on that exact command is not a "format" this card is
free to redefine — it is exactly the kind of already-published contract the
card's own Compatibility section requires to stay inalterado. Any new byte
on that pinned stdout — even a single trailing character — changes the
hash and turns a passing sibling acceptance red for a reason unrelated to
what AUR-449 actually tests.
This codebase already has an established idiom for exactly this situation
— additive, caller-facing information that must not touch a byte-pinned
stdout contract goes to stderr, prefixed aurumcode review:, the same
sink and prefix AUR-433's cost lines, AUR-441's cache-reuse note, AUR-448's
discard warning, and AUR-449's skip explanation already use. The coverage
note follows that convention instead of inventing a new one. It is filter-
identity under the AUR-432/AUR-009 redaction writer exactly like every
other canonical stderr line this command publishes (pinned by
cmd/aurumcode/redaction_wiring_test.go's
TestStderrLinesSurviveTheRedactionWriter, which now includes this note
verbatim) — the rule-id tokens it names (e.g. security/hardcoded-secret)
never sit immediately before a bare :/= value, the shape the filter's
secret-key rules require to trigger, so nothing in the note is ever
rewritten or masked.
tests/acceptance/AUR-450.sh's nominal_case re-asserts the exact same
AUR-449 stdout sha256, byte for byte, to prove this card's whole point:
new information reaches the user without disturbing a single byte of a
contract this card does not own.
Output shape
--seguranca, no provider, findings present (git-demo,
config/demo-tokens.txt plants three secrets AUR-442 already teaches the
pass to find):
Security findings (standards/security-review):
config/demo-tokens.txt:4: [error] Hardcoded credentials or API keys detected [standards/security-review SCR-003] (rule security/hardcoded-secret: Hardcoded Secrets)
config/demo-tokens.txt:5: [error] Hardcoded credentials or API keys detected [standards/security-review SCR-003] (rule security/hardcoded-secret: Hardcoded Secrets)
config/demo-tokens.txt:6: [error] Hardcoded credentials or API keys detected [standards/security-review SCR-003] (rule security/hardcoded-secret: Hardcoded Secrets)
stderr (new under this card, printed before the findings are known):
aurumcode review: security pass applied 3 of 8 security rules (security/command-injection, security/hardcoded-secret, security/sql-injection); see internal/review/rules/security.yml for the full catalog
--seguranca, nothing matches (e.g. --base HEAD against itself, an
empty diff): stdout prints the honest absence, exactly as AUR-449 already
publishes it —
Security findings (standards/security-review):
No security findings.
— and stderr carries the byte-identical coverage note above: the figures are catalog-derived, not diff-derived, so the empty case can never be told apart from the non-empty case by looking at the note alone. That identity is exactly what stops "No security findings." from being misread as "the whole catalog ran."
--seguranca, a provider configured: stdout is unaffected — the exact
AUR-442/AUR-449 contract, sha256-pinned by both
tests/acceptance/AUR-449.sh and this card's own acceptance program — and
the same coverage note appears on stderr.
Without --seguranca: no coverage note anywhere, on either stream —
the note is scoped to the security pass, never printed on a plain quality
review.
With --modelo unavailable: the command still fails loudly
(reportModelUnavailable, AUR-436) before --seguranca is ever reached,
so neither the security section nor the coverage note prints.
Exit codes
Unchanged from AUR-435/AUR-442/AUR-449 (docs/specs/AUR-449.md). This card
adds no new exit code and changes no exit code's meaning; only stderr grows
one line, and only when --seguranca runs.
Offline, secret-free example
cd tests/fixtures/repos/git-demo/repo.git
unset AURUMCODE_LLM_FIXTURE LLM_API_KEY LLM_BASE_URL
aurumcode review --base HEAD~1 --seguranca
prints, with exit 0, on stdout (unchanged from AUR-442/AUR-449):
Security findings (standards/security-review):
config/demo-tokens.txt:4: [error] Hardcoded credentials or API keys detected [standards/security-review SCR-003] (rule security/hardcoded-secret: Hardcoded Secrets)
config/demo-tokens.txt:5: [error] Hardcoded credentials or API keys detected [standards/security-review SCR-003] (rule security/hardcoded-secret: Hardcoded Secrets)
config/demo-tokens.txt:6: [error] Hardcoded credentials or API keys detected [standards/security-review SCR-003] (rule security/hardcoded-secret: Hardcoded Secrets)
and on stderr (the AUR-449 skip note, since no provider is configured, followed by this card's new coverage note):
aurumcode review: no LLM provider configured: quality review skipped, running --seguranca only (...)
aurumcode review: security pass applied 3 of 8 security rules (security/command-injection, security/hardcoded-secret, security/sql-injection); see internal/review/rules/security.yml for the full catalog
Failure diagnosis
- The security section says
No security findings.and you are unsure whether the pass actually looked for what you expected — check the coverage note on stderr: it names every rule the pass can match, by id, every single run. A rule not in that list is metadata only (internal/review/rules/security.ymlhas nopattern:for it) and will never fire regardless of what the diff contains — seedocs/specs/AUR-442.md's "The decision" for why each of the five stayed patternless. - No coverage note anywhere — either
--segurancawas not given, or an earlier failure (a broken rules catalog, an unavailable--modelo) stopped the command before the security pass was reached; check the exit code and the rest of stderr. aurumcode review: review rules unavailable: ...— the embedded rules catalog is broken; fixinternal/review/rules/and rebuild. Both the findings and the coverage note come from the same catalog load, so this failure blocks both, before either prints anything.
Security
- No new sink: the coverage note is trusted catalog text (rule ids, a literal count, a literal repository-relative path to the catalog file) — never diff content, never a matched value, never a secret. It is filter- identity under the redaction writer (see "Why stderr, not stdout" above).
- The
AURUM_SECRET_CANARYvalue never reaches stdout, stderr, prompt, cache, or evidence alongside the new note;tests/acceptance/AUR-450.shasserts this.
Compatibility
review --basewithout--seguranca, with--modelo, or with a configured provider is byte-identical to the already-published contract on both stdout and stderr:tests/acceptance/AUR-442.sh,tests/acceptance/AUR-443.sh,tests/acceptance/AUR-448.sh, andtests/acceptance/AUR-449.shall pass unchanged against this card's tree (re-verified, exit0each, as part of this card's own validation).tests/acceptance/AUR-449.sh's exact stdout sha256 pin for--segurancawith a provider configured is re-asserted, unchanged, by this card's owntests/acceptance/AUR-450.sh.--fail-on's gate composition (docs/specs/AUR-442.md) is unaffected: it still gates onresult.Issuesplus the security findings, which this card does not touch — only the coverage figures are new, and they never enter the gate.- The findings themselves — content and order — are exactly what
securityScanWithRulesalready produced;SecurityScanWithCoverage(the new entry pointcmd/aurumcodecalls) computes them with the exact same unexported helperSecurityScanuses, so the two can never diverge.SecurityScanitself is untouched:tests/unit/AUR-435.goandtests/unit/AUR-442.gokeep calling it with its original two-value signature.
Validation
tests/acceptance/AUR-450.sh AC-001 (sealed:
./.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-450), with
selectors TestAUR450 (tests/unit/AUR-450.go), IntegrationAUR450
(tests/integration/AUR-450.go), E2EAUR450 (tests/e2e/AUR-450.sh) and
AC-001-MUT-001 (silencing the coverage note's print call while leaving
the security pass itself untouched, which must make the acceptance fail —
internal/review.SecurityScanWithCoverage still returns the right figures;
only cmd/aurumcode's print of them is suppressed).
Selector naming note (the same technique tests/unit/AUR-442.go and
tests/unit/AUR-449.go document): the card text's TDD proof section names
TestAUR435 / IntegrationAUR435 / E2EAUR435, copy-paste artifacts from
the AUR-435 card this one was drafted against. Those symbols already exist
in their packages (tests/unit/AUR-435.go, tests/integration/AUR-435.go)
and TestAUR449/IntegrationAUR449 already occupy the next-available
names too, so this card's proofs are TestAUR450 / IntegrationAUR450 /
E2EAUR450 instead.