fix(#3120): add register_authored_at_plan_time guard — prevent rubber-stamping legacy phases (#3142)

* fix(#3120): add register_authored_at_plan_time guard to secure-phase

Root cause: Step 3 short-circuit used threats_open: 0 as the sole
condition to skip directly to Step 6 (write clean SECURITY.md). It
did not distinguish empty-by-all-mitigated from empty-by-no-planning.
Legacy phases authored before <threat_model> blocks were canonical
received a rubber-stamped clean SECURITY.md with no audit performed.

Fix:
  Step 2c: track register_authored_at_plan_time (true iff >=1 PLAN
           file contained a parseable <threat_model> block)
  Step 3:  two-condition short-circuit:
           - threats_open:0 AND register_authored_at_plan_time:true
             -> skip to Step 6 (legitimate, all mitigated)
           - threats_open:0 AND register_authored_at_plan_time:false
             -> retroactive-STRIDE mode in Step 5 (build register
                from implementation, then verify)
  Step 5:  auditor constraint varies by mode:
           planned     -> Verify mitigations exist, do not scan
           retroactive -> Build STRIDE register first, then verify

Suite: 7039/7039. Closes #3120.

* fix(lint+changeset): allow-test-rule, drop dead regex branches, fix pr field to 3142
This commit is contained in:
Tom Boucher
2026-05-05 15:02:19 -04:00
committed by GitHub
parent 7827e1ddee
commit 47ed26a01b
3 changed files with 76 additions and 2 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 3142
---
**`secure-phase` no longer rubber-stamps SECURITY.md for legacy phases with no `<threat_model>` blocks** — Step 3's short-circuit previously exited to Step 6 (write clean SECURITY.md) whenever `threats_open: 0`, regardless of whether zero threats meant "all mitigated" or "none were ever written". Legacy phases authored before `<threat_model>` blocks became canonical now trigger **retroactive-STRIDE mode** in Step 5: the auditor builds a register from implementation files before verifying mitigations. Step 2c now tracks `register_authored_at_plan_time` and Step 3 gates the skip on both `threats_open: 0 AND register_authored_at_plan_time: true`. Closes #3120.