feat(policy-loader): 8.7 — env-var deprecation warnings (v8.0.0 removal)

This commit is contained in:
Kjell Tore Guttormsen 2026-04-30 17:11:07 +02:00
commit ba5f2b64ad
8 changed files with 252 additions and 24 deletions

View file

@ -410,6 +410,22 @@ For deep scans (`/security scan --deep` or `/security deep-scan`), deterministic
The baseline survives the 7-day TTL purge so detection persists across the full window. After a legitimate MCP server upgrade, run `/security mcp-baseline-reset` (or `node scanners/mcp-baseline-reset.mjs --target <tool>`) to clear the stale baseline. The next call seeds a fresh baseline from the incoming description; description, firstSeen, lastSeen, and history are preserved across reset for audit. `LLM_SECURITY_MCP_CACHE_FILE` env var overrides the cache path for testing without polluting `~/.cache/llm-security/mcp-descriptions.json`.
### Env-var Deprecation Runway to v8.0.0 (D3, v7.3.0)
Hook configuration has historically been split between two equally valid surfaces: process env-vars (e.g. `LLM_SECURITY_TRIFECTA_MODE=warn`) and the team-distributable `.llm-security/policy.json` file. Env still wins per the original Preferences contract, but until v7.3.0 there was no signal when the two surfaces disagreed — a developer could set `policy.json` to `block` while their shell exported `warn`, and the warn-value would silently override.
`scanners/lib/policy-loader.mjs` now exports `getPolicyValueWithEnvWarn(section, key, envVarName, defaultValue)`. The helper resolves the value with the existing env-wins contract, but when both the env-var AND the `policy.json` key are set explicitly (heuristic: resolved policy value differs from `defaultValue`), it emits a single per-process stderr line:
```
[llm-security] Deprecation: env-var ${ENVVAR} will be removed in v8.0.0;
policy.json key ${section}.${key} also set — env wins for now.
Suppress with LLM_SECURITY_DEPRECATION_QUIET=1.
```
A module-scoped `Set` dedupes per env-var name across call-sites within the same hook process. Four overlapping pairs are wired through the helper today: `LLM_SECURITY_INJECTION_MODE``injection.mode` (in `pre-prompt-inject-scan.mjs`), `LLM_SECURITY_TRIFECTA_MODE``trifecta.mode` and `LLM_SECURITY_ESCALATION_WINDOW``trifecta.escalation_window` (in `post-session-guard.mjs`), and `LLM_SECURITY_AUDIT_LOG``audit.log_path` (in `scanners/lib/audit-trail.mjs`). `DEFAULT_POLICY` gains `trifecta.escalation_window: 5` to close the previously-unmappable gap. Env-only vars without policy equivalents (`LLM_SECURITY_UPDATE_CHECK`, `LLM_SECURITY_PRECOMPACT_*`, `LLM_SECURITY_IDE_ROOTS`, `LLM_SECURITY_MCP_CACHE_FILE`) are unchanged — there is nothing to deprecate yet.
The runway is intentionally long: v7.3.0 ships the warning, v8.0.0 removes the env-var read entirely. Teams running CI with both surfaces set should converge on `policy.json` over the v7.x window. Set `LLM_SECURITY_DEPRECATION_QUIET=1` for the transition period if the warnings are noisy in headless logs.
**Why deterministic?** LLMs are powerful at semantic analysis — understanding intent, detecting social engineering, assessing context. But they cannot reliably calculate Shannon entropy, measure Levenshtein distance between package names, trace taint flow across function boundaries, or detect individual Unicode codepoints. These scanners fill that gap.
**Shared library** (`scanners/lib/`): severity classification, string utilities (entropy, Levenshtein, base64 detection), output formatting, file discovery, and YAML frontmatter parsing.
@ -479,7 +495,7 @@ v6.0.0 adds an enterprise governance layer for standards-aware security operatio
| **SARIF 2.1.0 Output** | `--format sarif` flag on scan/deep-scan produces OASIS SARIF standard output for CI/CD integration (GitHub Advanced Security, Azure DevOps, SonarQube). |
| **Structured Audit Trail** | JSONL audit events (`audit-trail.mjs`) with ISO 8601 timestamps, OWASP category tags, and SIEM-ready schema. Configurable via `LLM_SECURITY_AUDIT_*` env vars. |
| **AI-BOM** | CycloneDX 1.6 Bill of Materials for AI components — models, MCP servers, plugins, knowledge files, hooks. `llm-security audit-bom <target>`. |
| **Policy-as-Code** | `.llm-security/policy.json` for distributable hook configuration. Teams can enforce consistent security thresholds without per-developer env var setup. |
| **Policy-as-Code** | `.llm-security/policy.json` for distributable hook configuration. Teams can enforce consistent security thresholds without per-developer env var setup. v7.3.0 (D3): when both an env-var and its overlapping `policy.json` key are set explicitly, hooks emit a one-time-per-process stderr line `[llm-security] Deprecation: env-var ${NAME} will be removed in v8.0.0; policy.json key ${section}.${key} also set — env wins for now. Suppress with LLM_SECURITY_DEPRECATION_QUIET=1.`. Affected pairs: `LLM_SECURITY_INJECTION_MODE``injection.mode`, `LLM_SECURITY_TRIFECTA_MODE``trifecta.mode`, `LLM_SECURITY_ESCALATION_WINDOW``trifecta.escalation_window` (new key in `DEFAULT_POLICY`), `LLM_SECURITY_AUDIT_LOG``audit.log_path`. Env still wins per existing contract; no behaviour change in v7.3.0 — the warning is the deprecation runway to v8.0.0. |
| **Standalone CLI** | `node bin/llm-security.mjs scan <target>` — runs scanners without Claude Code. Subcommands: `scan`, `posture`, `audit-bom`, `benchmark`. |
| **CI/CD Integration** | `--fail-on <severity>` for threshold-based exit codes, `--compact` for one-liner output. Pipeline templates for GitHub Actions, Azure DevOps, GitLab CI in `ci/`. Guide: `docs/ci-cd-guide.md`. |