feat(llm-security)!: v8 Phase 3 complete - riskScoreV1, posture heuristic, docs

Closes Phase 3 (B11) of the v8.0.0 plan. Three parts, all with the failing
test written first.

riskScoreV1 removed. scanners/lib/severity.mjs drops riskScoreV1() and its
SEVERITY_WEIGHTS_V1 table - @deprecated since v7.0.0, kept for diff/comparison,
zero callers in code or tests (re-verified, not taken from the plan). The v1
weights are recorded in CHANGELOG so an old score stays re-derivable. riskScore
(v2) is untouched; a test pins that one critical still lands in the 70-95 tier
and that 50 lows score below it, which is exactly the case v1 collapsed to 100.

Posture category 12 no longer keys off an identifier name. The check was
/TRIFECTA_MODE/i over the session-guard source, which measured what a constant
was CALLED rather than whether enforcement was configurable. With the env-var
gone, that regex would have dropped every correctly-migrated project from PASS
to PARTIAL - the gate punishing the migration it exists to encourage. It now
matches getPolicyValue('trifecta', 'mode', ...) and still accepts a pre-v8
vendored guard reading the old env-var, because a third-party project carries
its own hook copy and is equally configurable either way; the evidence line
says which of the two was found. The PARTIAL finding recommended setting an
env-var that v8 ignores; it now names the policy key. The grade-a fixture hook
moves to the policy-era form.

Two never-implemented env-vars deleted from the docs. LLM_SECURITY_SCR_OFFLINE
(ci-cd-guide) and LLM_SECURITY_OFFLINE (supply-chain-attack example) were
documented as OSV.dev / npm-audit kill-switches. No code has ever read either -
verified by grep across scanners, hooks and scripts, which finds them only in
markdown. A promised kill-switch that does nothing is worse than a documented
absence: it is trusted precisely when the run is meant to be air-gapped. The
docs now say there is none and that egress must be blocked at the network
layer. The LLM_SECURITY_AUDIT_* wildcard is narrowed to the one real key.

Docs. Migration section in README + CHANGELOG with the env-var -> policy-key
table, the detection commands (env + shell rc + .envrc + workflows), and the
explicit warning that a removed variable is now INERT rather than an error -
which is the failure mode that loses a project its configuration silently. The
hardening-guide env table splits into surviving vars and a removed-vars
migration table; its "promote to block" runbook named two variables that no
longer exist. Also swept: CLAUDE.md hook table, scanner-reference, ci-cd-guide,
both lethal-trifecta example docs, mitigation-matrix, injection-research.

Test counts in README/CLAUDE.md synced 2034 -> 2045.

Suite 2045 tests, 0 fail (2039 + 4 posture-trifecta + 2 riskScoreV1). The two
known parallel-load flakes did not recur this run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BB4vXvwvtW4dxbPRd6vsez
This commit is contained in:
Kjell Tore Guttormsen 2026-08-09 10:25:03 +02:00
commit fdec4b36ad
16 changed files with 333 additions and 62 deletions

View file

@ -6,6 +6,61 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased]
### Removed — BREAKING
- **The four `LLM_SECURITY_*` configuration env-vars deprecated in v7.3.0.**
`.llm-security/policy.json` is now the only source. A removed variable is
**inert**: it neither warns nor configures, so a project relying on one
silently returns to the default. See the Migration section in `README.md`
for the detection commands.
| Removed env-var | Policy key | Default |
|-----------------|------------|---------|
| `LLM_SECURITY_INJECTION_MODE` | `injection.mode` | `block` |
| `LLM_SECURITY_TRIFECTA_MODE` | `trifecta.mode` | `warn` |
| `LLM_SECURITY_ESCALATION_WINDOW` | `trifecta.escalation_window` | `5` |
| `LLM_SECURITY_AUDIT_LOG` | `audit.log_path` | unset |
| `LLM_SECURITY_DEPRECATION_QUIET` | *(none)* | dies with the warning it silenced |
Env-vars with no policy equivalent are unaffected:
`LLM_SECURITY_PRECOMPACT_MODE`, `LLM_SECURITY_PRECOMPACT_MAX_BYTES`,
`LLM_SECURITY_UPDATE_CHECK`, `LLM_SECURITY_MCP_CACHE_FILE`,
`LLM_SECURITY_IDE_ROOTS`.
`getPolicyValueWithEnvWarn` and its one-shot stderr deprecation line are
deleted from `scanners/lib/policy-loader.mjs`; the four call sites collapse
to `getPolicyValue`. Hook strings that advertised a removed variable as the
escape hatch now name the policy key — a blocked user following the old text
would have set a variable that does nothing.
- **`riskScoreV1()`** in `scanners/lib/severity.mjs`, plus its
`SEVERITY_WEIGHTS_V1` table. `@deprecated` since v7.0.0, kept for
diff/comparison, with zero callers in code or tests. The v1 weights are
recorded here for anyone re-deriving an old score:
`critical 25, high 10, medium 4, low 1, info 0`, summed and capped at 100.
`riskScore()` (v2) is untouched, so no score, band, or verdict moves.
- **Two documented env-vars that were never implemented.**
`LLM_SECURITY_SCR_OFFLINE` (`docs/ci-cd-guide.md`) and `LLM_SECURITY_OFFLINE`
(`examples/supply-chain-attack/README.md`) were promised as OSV.dev / npm-audit
kill-switches; no code has ever read either. The docs now say plainly that
there is no kill-switch and that an air-gapped run must block egress at the
network layer. The `LLM_SECURITY_AUDIT_*` wildcard phrasing (README,
`docs/scanner-reference.md`, `docs/security-hardening-guide.md`) is narrowed
to the single real key.
### Changed
- **Posture category 12 (Rule of Two) no longer keys off the identifier
`TRIFECTA_MODE`.** The check was `/TRIFECTA_MODE/i` over the session-guard
source, which measured what a constant was *named* rather than whether
enforcement was configurable at all. It now matches
`getPolicyValue('trifecta', 'mode', …)`, and still accepts a pre-v8 vendored
guard reading `LLM_SECURITY_TRIFECTA_MODE` — third-party projects carry their
own hook copy and are equally configurable. Without this, every correctly
migrated project would have dropped from PASS to PARTIAL. The PARTIAL
finding now recommends the policy key instead of the removed env-var.
## [7.8.3] - 2026-07-18
Security and correctness patch. 47 verified fixes from the v7.8.1/v7.8.2