Skip to content

Commit fd4fbe3

Browse files
authored
fix: ban phantom Axxxxx codes with guards section 14 (#1489)
## Summary Closes the recurring class of issues where agents invent error codes in module docs (`A09104`, `A53007`, …) without catalog entries or emitters, then we file endless follow-ups. ## Rule (mechanical) 1. **Never** assign an `Axxxxx` number until the check is implemented **and** registered in `assura-diagnostics` catalog **in the same PR**. 2. Future work is described in prose without a code number. 3. `scripts/guards.sh` **section 14** hard-fails phantom mentions (CI). ## Changes - guards section 14 + AGENTS.md same-PR rule - Remove reserved fake codes from taint / quantifier-trigger module docs - Drop one-off unit test that listed three reserved codes (class-level guard replaces it) - Clarify project-check comment (no fake A02000) ## Closes Closes #1486 Closes #1487 These were symptoms of the class, not separate product features. Partial taint sanitization or richer trigger validation can return as real features later **with** catalog+emit+tests in one PR, without pre-reserving numbers. ## Test plan - [x] `bash scripts/guards.sh` (green) - [x] Injected phantom `A09104` is detected by guards - [x] `cargo test -p assura-diagnostics --locked --lib` - [ ] CI green --------- Signed-off-by: Sebastien Tardif <sebtardif@ncf.ca>
1 parent 8eb9479 commit fd4fbe3

7 files changed

Lines changed: 81 additions & 25 deletions

File tree

AGENTS.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,14 @@ Do not improvise a parallel pipeline. Follow the branch that matches your task.
8383

8484
Full meanings: `docs/SPECIFICATION.md` §7.2 / Appendix D. Do not fix an `A02` in `assura-smt` or an `A01` in `assura-types` unless the index explicitly says cross-phase.
8585

86-
**When you introduce or rely on a code not in the index:** append one row to
87-
`docs/error-codes.md` in the same PR (high-traffic section is fine). Do not
88-
regenerate all of Appendix D.
86+
**When you introduce a new error code:** in the **same PR** (1) implement and
87+
emit it, (2) add the catalog entry in `assura-diagnostics`, (3) append a row to
88+
`docs/error-codes.md` (high-traffic section is fine). Do **not** invent `Axxxxx`
89+
numbers in module docs, comments, or TODOs for unimplemented checks. Describe
90+
future work in prose without a code number until it is wired. `scripts/guards.sh`
91+
section 14 hard-fails phantom codes (mentions outside the catalog in
92+
`crates/**/*.{rs,md}` and `docs/**/*.md`). Sentinels `A00000` / `A88888` /
93+
`A99999` only. Do not regenerate all of Appendix D.
8994

9095
### `assura-types` layer map (summary)
9196

crates/assura-cli/src/check/project.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ pub(crate) fn run_check_project(
4848
.iter()
4949
.map(|e| {
5050
let msg = e.to_string();
51-
// Map to catalog codes (A02000 is not a valid code).
51+
// Map resolve errors onto real catalog codes only.
5252
let code = if msg.to_ascii_lowercase().contains("circular") {
5353
"A02005"
5454
} else {

crates/assura-diagnostics/src/lib.rs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -447,18 +447,6 @@ mod tests {
447447
}
448448
}
449449

450-
/// Codes mentioned in checker module docs as future work must not
451-
/// silently appear in the catalog until they are implemented and wired.
452-
#[test]
453-
fn reserved_error_codes_not_yet_in_catalog() {
454-
for code in ["A09104", "A53007", "A53008"] {
455-
assert!(
456-
explain(code).is_none(),
457-
"{code}: reserved/future code must not be in catalog until implemented"
458-
);
459-
}
460-
}
461-
462450
#[test]
463451
fn test_explain_all_catalog_codes() {
464452
let catalog = error_catalog();

crates/assura-types/src/checkers/taint.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ pub(crate) fn extract_taint_label(
9090
/// - **A09102**: Tainted data used as allocation size without validation
9191
/// - **A09103**: Tainted data flows to trusted sink
9292
///
93-
/// Reserved for future checks (not yet emitted; not in the diagnostics catalog):
94-
/// - **A09104**: Taint validation incomplete (partial sanitization)
93+
/// Do not invent further `Axxxxx` numbers here until the check is implemented
94+
/// and registered in `assura-diagnostics` catalog in the same change.
9595
#[derive(Debug, Clone)]
9696
pub(crate) struct TaintChecker {
9797
/// Maps variable name to its taint label.

crates/assura-types/src/domain/core/quantifier_trigger.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,11 @@ use crate::TypeError;
88

99
/// Validates quantifier trigger annotations for verification performance.
1010
///
11-
/// Error codes (currently emitted):
11+
/// Error codes:
1212
/// - A53006: quantifier has no trigger annotation
1313
///
14-
/// Reserved for future checks (not yet emitted; not in the diagnostics catalog):
15-
/// - A53007: trigger references variable not bound by the quantifier
16-
/// - A53008: trigger term is a sub-expression of the quantifier body (matching loop risk)
14+
/// Do not invent further `Axxxxx` numbers here until the check is implemented
15+
/// and registered in `assura-diagnostics` catalog in the same change.
1716
#[derive(Debug, Clone)]
1817
pub struct QuantifierTriggerChecker {
1918
quantifiers: Vec<QuantifierInfo>,

docs/EXPLAINER.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ graph TD
303303
subgraph "Pass 2: Checking"
304304
D["For each function body"] --> E["Track taint through\nexpressions"]
305305
E --> F{Tainted data at\nsensitive position?}
306-
F -->|Yes| G["Error A09101-A09104"]
306+
F -->|Yes| G["Error A09101-A09103"]
307307
F -->|No| H["OK"]
308308
end
309309
B --> D
@@ -353,7 +353,6 @@ The fix: wrap `index` in a `validate` block that bounds-checks it:
353353
| A09101 | Tainted array index | Buffer overflow (CVE-2023-4863) |
354354
| A09102 | Tainted allocation size | Integer overflow in malloc |
355355
| A09103 | Tainted data at trusted sink | SQL injection, command injection |
356-
| A09104 | Incomplete validation | Partial sanitization bypass |
357356

358357
---
359358

@@ -756,7 +755,7 @@ graph TD
756755
757756
NR --> HIR["4. HIR Lowering\nNormalize taint annotation\ninto canonical form"]
758757
759-
HIR --> TC["5. Type Checker\nRun TaintChecker:\nA09101-A09104 errors"]
758+
HIR --> TC["5. Type Checker\nRun TaintChecker:\nA09101-A09103 errors"]
760759
761760
TC --> PW["6. Pipeline Wiring\nEnsure run_taint_checks()\nis called"]
762761

scripts/guards.sh

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,71 @@ else
479479
jsec 13 "IR template single source" "ok" "crate-local IR templates present; root has no pattern bodies"
480480
fi
481481

482+
# ---------------------------------------------------------------------------
483+
# 14) No phantom Axxxxx codes outside the diagnostics catalog.
484+
# Agents must not invent error codes in module docs, comments, or docs/
485+
# before the code is implemented + registered in catalog.rs in the same
486+
# change. Allowlist intentional sentinels only (unknown / test).
487+
# Scans crates/**/*.{rs,md} and docs/**/*.md (catalog.rs excluded).
488+
# ---------------------------------------------------------------------------
489+
s14_fail=0
490+
if [[ -f crates/assura-diagnostics/src/catalog.rs ]]; then
491+
# Extract catalog codes and every Axxxxx mention under crates/ + docs/.
492+
# Fail if a mention is not in the catalog and not allowlisted.
493+
while IFS= read -r line; do
494+
[[ -z "$line" ]] && continue
495+
code="${line%% *}"
496+
file="${line#* }"
497+
case "$code" in
498+
A00000|A88888|A99999) continue ;; # unknown / test sentinels
499+
esac
500+
die "phantom error code $code (not in diagnostics catalog): $file"
501+
die " fix: implement the check + add catalog entry in the same PR, or remove the code number from docs"
502+
jfind 14 "$file" "phantom $code not in catalog"
503+
s14_fail=1
504+
done < <(
505+
python3 - <<'PY'
506+
import re
507+
from pathlib import Path
508+
509+
catalog = set(
510+
re.findall(
511+
r'code:\s*"(A\d{5})"',
512+
Path("crates/assura-diagnostics/src/catalog.rs").read_text(encoding="utf-8", errors="ignore"),
513+
)
514+
)
515+
# Intentional non-catalog sentinels (also allowlisted in bash above).
516+
allow = {"A00000", "A88888", "A99999"}
517+
seen = set()
518+
roots = [
519+
(Path("crates"), ("*.rs", "*.md")),
520+
(Path("docs"), ("*.md",)),
521+
]
522+
for root, globs in roots:
523+
if not root.is_dir():
524+
continue
525+
for g in globs:
526+
for p in root.rglob(g):
527+
if p.name == "catalog.rs" and "assura-diagnostics" in p.parts:
528+
continue
529+
text = p.read_text(encoding="utf-8", errors="ignore")
530+
for code in set(re.findall(r"\bA\d{5}\b", text)):
531+
if code in catalog or code in allow:
532+
continue
533+
key = (code, str(p))
534+
if key in seen:
535+
continue
536+
seen.add(key)
537+
print(f"{code}\t{p}")
538+
PY
539+
)
540+
fi
541+
if [[ $s14_fail -eq 1 ]]; then
542+
jsec 14 "no phantom Axxxxx codes" "fail" "Axxxxx mentioned outside catalog without allowlist"
543+
else
544+
jsec 14 "no phantom Axxxxx codes" "ok" "all mentioned Axxxxx are cataloged or allowlisted sentinels"
545+
fi
546+
482547
# ── Final output ─────────────────────────────────────────────────────────────
483548
if $json_mode; then
484549
python3 - "$_jdata" << 'PYEOF'

0 commit comments

Comments
 (0)