Rule IDs
The closed list of 30 rule ids every finding carries as rule_id — what each one means, its default SARIF level, and where the id is used (policy rules, inline markers, SARIF).
A rule id is the stable name of the kind of problem a finding describes, written
<category>.<rule> — for example sec.injection or correctness.error-handling. Every
structured finding carries one in its rule_id field, next to a
fingerprint. Requires v1.19.0 or later.
Key facts. The list is closed: 30 ids, listed below. The model must pick exactly one. An empty or unknown value becomes
general.other— it is not a parse error and triggers no retry. Ids are never removed or renamed, so a policy file, an inline marker or a baseline that names one keeps working across releases. New ids are added, never repurposed.
Where a rule id is used
| Surface | How |
|---|---|
--json | findings[].rule_id — see Output formats. |
| Policy gate | rules.deny / rules.allow take ids or <category>.*. |
| Inline suppression | commitbrief-ignore[sec.injection]: reason silences that rule only. |
| Baseline | The fingerprint of a finding is a hash of file, rule id and symbol. |
| SARIF | results[].ruleId, and tool.driver.rules[] lists all 30. |
The 30 rules
Categories: sec (security), correctness, concurrency, perf, api, test, arch,
maint, style, docs, general.
| Rule id | Default level | What it means |
|---|---|---|
sec.injection | error | Untrusted input reaches a query, shell, template or interpreter without neutralisation (SQL, command, LDAP, template) |
sec.xss | error | Untrusted data is written into HTML or JS without context-appropriate escaping |
sec.path-traversal | error | A user-controlled path reaches the file system without being confined |
sec.ssrf | error | A user-controlled URL or host is requested server-side without an allowlist |
sec.hardcoded-secret | error | A credential, key or token literal is present in source |
sec.authz | error | An authentication or authorisation check is missing or can be bypassed |
sec.unsafe-deserialization | error | Untrusted bytes are deserialised into objects or code |
sec.crypto-misuse | warning | Weak algorithm, poor randomness, fixed IV or salt, or disabled TLS verification |
sec.data-exposure | warning | A secret or personal data is written to a log, an error or a response |
correctness.nil-deref | error | A value that may be nil, null, undefined or None is dereferenced |
correctness.logic-error | warning | A condition, operator or flow does not do what the code intends |
correctness.boundary | warning | Off-by-one, empty, overflow or other boundary cases are handled wrongly |
correctness.error-handling | warning | An error is ignored, swallowed, wrapped wrongly or not propagated |
correctness.resource-leak | warning | A file, connection, lock or goroutine is not released on every path |
correctness.api-misuse | warning | A library or standard-library API is called against its contract |
concurrency.race | error | Shared state is accessed concurrently without synchronisation |
concurrency.deadlock | error | Lock ordering or a channel wait can block forever |
perf.algorithmic | warning | Asymptotically poor algorithm or repeated work on a hot path |
perf.n-plus-one | warning | A per-item query or IO call inside a loop |
perf.allocation | note | Avoidable memory allocation or copying on a hot path |
api.breaking-change | warning | A public interface or contract changes incompatibly |
test.missing | note | No test covers the changed behaviour |
test.flaky | warning | Test depends on time, order, randomness or network (the static flaky pre-pass also produces this) |
arch.boundary-violation | warning | The change violates a declared architectural constraint (architecture.json) |
maint.complexity | note | Structure is hard to follow and could be simplified |
maint.duplication | note | Logic that should be shared has been copied |
maint.dead-code | note | Unreachable or unused code |
style.naming | note | Misleading or inconsistent identifier |
docs.mismatch | note | A comment or document contradicts the code |
general.other | note | None of the above (the fallback) |
Default level is the SARIF level the rule is published with (error, warning or
note). It describes the rule, not the individual finding: a finding’s own
severity decides the level of its SARIF result.
Two ids also produced without the model
Both ids are in the closed list the model chooses from, so a review can label a finding with either. They are also produced deterministically:
test.flakycomes from the flaky-test detector as well.sec.hardcoded-secretcomes fromcommitbrief leaksas well. Thoseleaksfindings carry nofingerprint.
A policy entry on either id therefore covers both sources. rules.allow: [sec.hardcoded-secret]
or rules.allow: [sec.*] also waives a hardcoded credential the model reported in a review,
and rules.deny: [test.flaky] also blocks a flaky-test finding the model reported.
When the model picks the wrong id
The id is a model judgement, so it can be wrong. A finding that fits none of the 30
definitions lands in general.other. With --verbose (and without --quiet), the number of
findings that fell back to general.other is written to stderr as a debug record, so you can
see how often it happens on your code.
Because rule ids are a model’s classification, treat a rules.deny entry as a strong signal,
not a proof: it blocks on whatever the model labelled, and it will not block on a problem the
model labelled differently or did not report.