Pular para conteúdo

AUR-442 — aurumcode review --seguranca: the passage finds what the catalog promises

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 published. This card does not add a flag or change the output shape; it repairs what --seguranca's deterministic pass actually matches, so the pass stops claiming coverage it never delivered.

The measured defect (2026-08-13 dogfooding)

Running the real binary against real repositories: --seguranca matched only 2 of the 8 catalog rules (security/sql-injection, security/command-injection), both requiring the narrow "..." + var string-concatenation shape. The other six (xss, path-traversal, hardcoded-secret, weak-crypto, insecure-random, missing-auth) were metadata with no pattern, so they never matched anything. The observable consequence: the project's own demo fixture, tests/fixtures/repos/git-demo — whose sole purpose is to plant a plaintext secret — reported No security findings. on every run.

The decision: option (a), scoped to one rule

The card offered two legitimate fixes: give the missing rules a pattern with a fixture proving each one (a), or declare the covered subset honestly and stop promising the rest (b). This card takes (a), scoped to exactly one rule — security/hardcoded-secret, the card's named priority and the rule git-demo's fixture depends on — rather than (a) for several rules under the same review pass. The other five stay declared-only: each now carries an explicit code comment in internal/review/rules/security.yml recording that decision (not silence), and this file is the honest, spec-level declaration the card's option (b) requires for them. Two reasons narrowed scope to one rule instead of the card's own hardcoded-secret-plus-weak-crypto example:

  • Precision cost. A matcher shipped without a validated, tested false-positive boundary is exactly the failure mode this card exists to close (a rule that fires on the wrong things is not meaningfully better than a rule that never fires). hardcoded-secret alone required two regex branches, an explicit "letter mixed with a digit or hyphen" requirement to reject plain function calls (token := getToken()) and plain numeric constants (TOKEN_MAX_AGE_SECONDS = 31536000), and a dedicated benign-line test suite (see internal/review/rules/security.yml's rule comment and tests/unit/AUR-442.go's ScanRejectsBenignAssignments). A second matcher (weak-crypto, or any other) needs the same rigor, which this card's scope did not budget for without rushing it.
  • xss, path-traversal, and missing-auth are not line-shape questions at all — they turn on data flow (does untrusted input reach an unescaped sink / an unvalidated path / a handler that should require auth and does not), which a single added line cannot answer regardless of how carefully the regex is written.

Every undelivered rule remains citable by a model finding (AUR-434's rule-citation gate resolves any catalog id, pattern or not) — only the deterministic --seguranca pass is scoped. Nothing in --help, this document, or the command's printed output claims coverage beyond what is proven below.

What security/hardcoded-secret now matches

Two shapes, both requiring a credential-named identifier (api key, secret, password, passwd, token, credential, access key, case-insensitive) assigned a value that mixes a letter with a digit or a hyphen:

  1. <keyword> (:=|:|=) "<value>" — a quoted assignment, the shape common in application source (api_key := "AURUM-FAKE-KEY-9000-2222").
  2. UPPER_SNAKE_KEYWORD = <value> unquoted — the env/config-file shape tests/fixtures/repos/git-demo/content/03/config/demo-tokens.txt uses (DEMO_API_TOKEN=AURUM-FAKE-TOKEN-0000-0001). The keyword may be the whole identifier with no prefix (TOKEN=..., SECRET=..., API_KEY=..., PASSWORD=...) or prefixed (DEMO_API_TOKEN=...); a review fix (2026-08-13) closed a gap where a mandatory leading uppercase letter silently consumed the keyword's own first character whenever the identifier had no prefix, so the four bare forms never matched despite this shape being documented without that exclusion (tests/unit/AUR-442.go's ScanFindsTheBareUnquotedEnvShape).

The letter-plus-(digit-or-hyphen) requirement is deliberate: it is what lets the matcher find password = "hunter2xyz123" and SERVICE_ACCESS_KEY=AURUM-FAKE-ACCESS-9000-3333 while passing over token := getAuthToken(), API_TOKEN = get_token_from_vault(), and TOKEN_MAX_AGE_SECONDS = 31536000 — a bare function call or a bare numeric constant, neither a hardcoded secret. Findings cite the rule and, new under this card, the project security standard SCR-003 (Cryptographic Failures / hardcoded credential, standards/security-review/rules.md).

Known limitation, same category as the SQL-injection pattern's own documented breadth (docs/specs/AUR-435.md, noted by the AUR-435 reviewer): a line-level regexp cannot see intent. A variable named secretary assigned an unrelated 16+ character token, or a comment line that happens to read secret: <8+ char word>, can still match. This is an accepted, bounded trade-off — the alternative (no matcher at all) is the defect this card fixes — not a promise of perfect precision.

Output shape

Unchanged from AUR-435:

<notices and quality findings, exactly as without the flag>

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)

The message is trusted catalog text (rule.Description), never the matched diff content: the secret VALUE never reaches this line, only the file, the line number, and the rule/standard citation. This is structural (internal/review/securitypass.go builds every message from the catalog, not from the matched body), proven in tests/unit/AUR-442.go's ScanNeverEchoesTheMatchedValue and exercised end to end in tests/integration/AUR-442.go and tests/e2e/AUR-442.sh.

Exit codes

Unchanged from AUR-435 (docs/specs/AUR-435.md):

Code Meaning
0 Review ran; gate (if any) open.
1 The review itself failed (no provider, unreadable repo, broken rules catalog).
2 Usage error (missing --base, unknown flag or level).
3 --fail-on gate closed. A matched hardcoded-secret finding is severity error and closes --fail-on high on its own.

Offline, secret-free examples

The card's central proof: git-demo

tests/fixtures/repos/git-demo plants three synthetic secrets specifically so a security pass has something to find. Before this card, --seguranca reported No security findings. here; it now reports all three:

cd tests/fixtures/repos/git-demo/repo.git
AURUMCODE_LLM_FIXTURE=../../../review/known-problem-response.json \
  aurumcode review --base HEAD~1 --seguranca

A dedicated fixture proving both matched shapes

tests/fixtures/review/vuln/hardcoded-secret (new under this card, rebuildable deterministically with tests/fixtures/repos/git-demo/build-fixture.sh; sibling to and never touching AUR-435's own tests/fixtures/review/vuln/repo.git, whose HEAD~1..HEAD diff AUR-435's frozen tests pin byte for byte) plants one secret of each shape beside one deliberately benign line each:

cd tests/fixtures/review/vuln/hardcoded-secret/repo.git
AURUMCODE_LLM_FIXTURE=../../../known-problem-response.json \
  aurumcode review --base HEAD~1 --seguranca

prints, after the unchanged quality block:

Security findings (standards/security-review):
config/secrets.env:4: [error] Hardcoded credentials or API keys detected [standards/security-review SCR-003] (rule security/hardcoded-secret: Hardcoded Secrets)
src/config.py:4: [error] Hardcoded credentials or API keys detected [standards/security-review SCR-003] (rule security/hardcoded-secret: Hardcoded Secrets)

— and not the benign SERVICE_NAME=aurum-fixture or timeout_seconds = 30 lines beside them.

Failure diagnosis

  • flag provided but not defined: -seguranca — the binary predates AUR-435; rebuild from source.
  • aurumcode review: review rules unavailable: ... — the embedded rules catalog is broken; fix internal/review/rules/ and rebuild.
  • The section says No security findings. but you expected a hardcoded-secret match — check the letter-plus-(digit-or-hyphen) requirement above; a bare function call or a bare numeric constant next to a credential-named identifier is correctly out of scope.
  • The section is silent about XSS, path traversal, weak crypto, insecure randomness, or missing auth — those five rules are declared metadata only (see "The decision" above); they are never out of scope by accident, and a model finding (without --seguranca, or alongside it) may still cite them.

Security

  • Structural: the finding message is trusted catalog text, never the matched diff body (see "Output shape" above); the secret value cannot reach stdout through this path.
  • The AURUM_SECRET_CANARY value never reaches stdout, stderr, prompt, cache, or evidence; tests/acceptance/AUR-442.sh asserts this on every run.

Compatibility

  • review --base without --seguranca is byte-identical to the published AUR-430 contract; --fail-on, --modelo, --limite, --pr, --check, and docs are unaffected (tests/acceptance/AUR-433.sh, tests/acceptance/AUR-441.sh pass unchanged against this card's tree).
  • security/sql-injection and security/command-injection (AUR-435) keep matching exactly what they matched before (tests/unit/AUR-442.go's OtherSecurityRulesUnaffected).
  • tests/acceptance/AUR-435.sh and tests/integration/AUR-435.go are updated by this card, not left as follow-up debt. Both originally hardcoded that git-demo's --seguranca run prints No security findings. — true only because no rule had a matcher for the plaintext secret that fixture exists to plant. AUR-442's write-set was extended (.board/cards/ready/AUR-442.md's paths, same precedent as AUR-424 in this reconstruction: the card that changes behavior updates the tests that pinned the old behavior) to include both files. nominal_case (AUR-435.sh) and IntegrationAUR435 (AUR-435.go) now assert the correct behavior instead — the security/hardcoded-secret rule citation, the SCR-003 standard citation, and each of the three planted lines (config/demo-tokens.txt:4-6) by name — and explicitly fail if No security findings. reappears, so the rebase cannot regress to a weaker check. AUR-435's own MUT-001 (mutation_case, which runs against the unrelated tests/fixtures/review/vuln/repo.git, not git-demo) is untouched and still rejects its mutant.

Validation

tests/acceptance/AUR-442.sh AC-001 (sealed: ./.board/bin/oci-run --profile bootstrap-readonly-v1 --card AUR-442), with selectors TestAUR442 (tests/unit/AUR-442.go), IntegrationAUR442 (tests/integration/AUR-442.go), E2EAUR442 (tests/e2e/AUR-442.sh) and AC-001-MUT-001 (deleting security/hardcoded-secret's pattern line, which must make the expected git-demo finding vanish).

Selector naming note (same technique tests/unit/AUR-445.go and tests/integration/AUR-445.go document): the card text's TDD proof section names TestAUR435 / IntegrationAUR435, copy-paste artifacts from the AUR-435 card this one was drafted against. Both symbols already exist in their packages (tests/unit/AUR-435.go, tests/integration/AUR-435.go) and would collide, so this card's proofs are TestAUR442 / IntegrationAUR442 instead.