chore: WIP marketplace doc adjustments across plugins

Pre-trekexecute snapshot of in-progress CLAUDE.md/SKILL.md edits and
extracted docs/ files. Captured as one commit so /trekexecute claude-design
can run against a clean working tree.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-18 12:04:02 +02:00
commit f460814fe9
26 changed files with 805 additions and 1078 deletions

View file

@ -0,0 +1,27 @@
# LLM Security — Defense philosophy (v5.0)
Imported from `CLAUDE.md` via `@docs/defense-philosophy.md`.
Prompt injection is **structurally unsolvable** with current architectures (joint paper, 14 researchers, 95-100% ASR against all 12 tested defenses). v5.0 does not claim to "prevent" injection. Instead, it implements **defense-in-depth**:
- **Broader detection** — MEDIUM advisory for obfuscation signals (leetspeak, homoglyphs, zero-width, multi-language), Unicode Tag steganography, bash expansion evasion
- **Increased attack cost** — Rule of Two detection (configurable block/warn/off for lethal trifecta; default `warn`, blocks on high-confidence trifectas in opt-in `block` mode; distributed trifectas across MCP servers are detected but not blocked by default), bash normalization before gate matching
- **Longer monitoring windows** — 100-call long-horizon alongside 20-call sliding window, slow-burn trifecta detection, behavioral drift via Jensen-Shannon divergence
- **Architectural constraints** — opportunistic byte-matching of truncated output fingerprints (first 200 bytes, SHA-256/16-hex tag; not semantic lineage; trivially bypassed by mutation or summarisation of tool output), sub-agent delegation tracking, HITL trap detection. Inspired by CaMeL (DeepMind, 2025), but this is a lightweight byte-fingerprint, not semantic capability tracking
- **Honest documentation** — Known Limitations section acknowledges what deterministic hooks cannot detect
**Bash evasion layers (T1-T6):** `bash-normalize.mjs` collapses six known obfuscation techniques before gate matching as a defense-in-depth layer. T1 empty quotes (`rm''-rf`), T2 `${}` parameter expansion, T3 backslash continuation, T4 tab/whitespace splitting, T5 `${IFS}` word-splitting, T6 ANSI-C hex quoting (`$'\x72\x6d'`). These layers complement — not replace — Claude Code 2.1.98+ harness-level protections. Full reference: `docs/security-hardening-guide.md`.
**Opus 4.7 system card alignment:**
- System card §5.2.1 (agentic safety evaluations) documents that multi-layer defenses outperform single-layer defenses against adaptive attacks. This plugin's posture (prompt-scan + pathguard + trifecta-guard + pre-compact-scan operating in depth) matches that guidance.
- System card §6.3.1.1 (instruction following and hierarchy) documents that Opus 4.7 interprets agent instructions more literally. Stacked imperatives (e.g., "MUST NOT do X") are therefore less useful than tool-level enforcement via `tools:` frontmatter. Agent files in this plugin have been updated accordingly.
- See `docs/security-hardening-guide.md` §5 for the full mapping.
**What v5.0 cannot do:**
- Prevent adaptive attacks from motivated human red-teamers (100% ASR per joint paper)
- Fix CLAUDE.md loading before hooks (platform limitation)
- Detect novel NL indirection without ML
- Prevent long-horizon attacks without detectable patterns
- Provide formal worst-case guarantees

View file

@ -0,0 +1,122 @@
# LLM Security — Scanner reference
Detailed scanner, CLI, CI/CD, knowledge-file and example documentation. Imported from `CLAUDE.md` via `@docs/scanner-reference.md`.
## Scanners
**Orchestrated (10):** Run via `node scanners/scan-orchestrator.mjs <target> [--fail-on <severity>] [--compact] [--output-file <path>] [--baseline] [--save-baseline]`.
`--fail-on <critical|high|medium|low>`: exit 1 if findings at/above severity, exit 0 otherwise. `--compact`: one-liner per finding format. Both configurable via `policy.json` `ci` section.
With `--output-file`: full JSON to file, compact aggregate to stdout. `--baseline` diffs against stored baseline. `--save-baseline` saves results for future diffs. Baselines stored in `reports/baselines/<target-hash>.json`.
10 scanners: unicode, entropy, permission, dep-audit, taint, git-forensics, network, memory-poisoning, supply-chain-recheck, toxic-flow.
Lib: `mcp-description-cache.mjs` — caches MCP tool descriptions in `~/.cache/llm-security/mcp-descriptions.json`, detects per-update drift via Levenshtein (>10% = alert), 7-day TTL. v7.3.0 (E14) adds a sticky baseline slot per tool plus a 10-event rolling history; cumulative drift = `levenshtein(current, baseline) / max(|current|,|baseline|)`. When ratio ≥ `mcp.cumulative_drift_threshold` (default 0.25), emits `mcp-cumulative-drift` advisory through `post-mcp-verify.mjs`. Baseline survives TTL purge so slow-burn drift is preserved across the 7-day window. `clearBaseline(tool?)` exposed for the `/security mcp-baseline-reset` command. `LLM_SECURITY_MCP_CACHE_FILE` env var overrides the cache path for testing.
Supply-chain-recheck (SCR) re-audits installed dependencies from lockfiles (package-lock.json, yarn.lock, requirements.txt, Pipfile.lock) against blocklists, OSV.dev batch API, and typosquat detection. Offline fallback available. Shared data module: `scanners/lib/supply-chain-data.mjs`.
Memory-poisoning (MEM) detects cognitive state poisoning in CLAUDE.md, memory files, and .claude/rules — injection patterns, shell commands, credential paths, permission expansion, suspicious URLs, encoded payloads.
Toxic-flow (TFA) is a post-processing correlator that runs LAST — detects "lethal trifecta" (untrusted input + sensitive data access + exfiltration sink) by correlating output from prior scanners.
Utility: `node scanners/lib/fs-utils.mjs <backup|restore|cleanup|tmppath> [args]`.
Lib: `sarif-formatter.mjs` — converts scan output to OASIS SARIF 2.1.0 format. Used by `--format sarif` flag.
Lib: `audit-trail.mjs` — writes structured JSONL audit events (ISO 8601, OWASP tags, SIEM-ready). Env: `LLM_SECURITY_AUDIT_*`.
Lib: `policy-loader.mjs` — reads `.llm-security/policy.json` for distributable hook configuration. Includes `ci` section (`failOn`, `compact`) for CI/CD defaults. Defaults match hardcoded values.
**Standalone (8):** `posture-scanner.mjs` — deterministic posture assessment, 16 categories (incl. EU AI Act, NIST AI RMF, ISO 42001), <50ms. NOT in scan-orchestrator (meta-level, not code-level).
Run: `node scanners/posture-scanner.mjs [path]` → JSON stdout. Scanner prefix: PST. Used by `/security posture` and `/security audit`.
`mcp-live-inspect.mjs` — NOT in scan-orchestrator. MCP servers are running processes, not files.
Run: `node scanners/mcp-live-inspect.mjs [target] [--timeout 10000] [--skip-global]`
Scanner prefix: MCI. OWASP: MCP03, MCP06, MCP09. Invoked by `mcp-inspect` and `mcp-audit --live`.
`watch-cron.mjs` — standalone cron wrapper. Reads `reports/watch/config.json`, scans all targets, writes `reports/watch/latest.json`. Run: `node scanners/watch-cron.mjs [--config <path>]`
`reference-config-generator.mjs` — generates Grade A reference config based on posture gaps. Detects project type (plugin/monorepo/standalone). Templates in `templates/reference-config/`. Run: `node scanners/reference-config-generator.mjs [path] [--apply]`
`dashboard-aggregator.mjs` — cross-project security dashboard. Discovers Claude Code projects under ~/ (depth 3) and ~/.claude/plugins/, runs posture-scanner on each, aggregates to machine-grade (weakest link). Cache in `~/.cache/llm-security/dashboard-latest.json` (24h staleness). Run: `node scanners/dashboard-aggregator.mjs [--no-cache] [--max-depth N]`
`attack-simulator.mjs` — red-team harness. Data-driven: 64 scenarios in 12 categories from `knowledge/attack-scenarios.json`. Payloads constructed at runtime (fragment assembly to avoid triggering hooks on source). Uses `runHook()` from test helper. Adaptive mode (`--adaptive`): 5 mutation rounds per passing scenario (homoglyph, encoding, zero-width, case alternation, synonym). Mutation rules in `knowledge/attack-mutations.json`. Benchmark mode (`--benchmark`): outputs structured pass/fail metrics. Run: `node scanners/attack-simulator.mjs [--category <name>] [--json] [--verbose] [--adaptive] [--benchmark]`
`ai-bom-generator.mjs` — AI Bill of Materials generator. Discovers AI components (models, MCP servers, plugins, knowledge, hooks) and outputs CycloneDX 1.6 JSON. Scanner prefix: BOM. Run: `node scanners/ai-bom-generator.mjs <target> [--output-file <path>]`
`ide-extension-scanner.mjs` — scans installed VS Code (and forks: Cursor, Windsurf, VSCodium, code-server, Insiders, Remote-SSH) extensions and JetBrains IDE plugins (IntelliJ IDEA, PyCharm, GoLand, WebStorm, RubyMine, PhpStorm, CLion, DataGrip, RustRover, Rider, Aqua, Writerside, Android Studio). Fleet + Toolbox excluded. OS-aware discovery via `lib/ide-extension-discovery.mjs` (`~/.vscode/extensions/` + `~/Library/Application Support/JetBrains/<IDE><version>/plugins/` on macOS, `%APPDATA%\JetBrains\...` on Windows, `~/.config/JetBrains/...` on Linux). Parses VS Code `package.json` via `lib/ide-extension-parser.mjs` and JetBrains `META-INF/plugin.xml` + `META-INF/MANIFEST.MF` (with nested-jar extraction) via `lib/ide-extension-parser-jb.mjs`. 7 VS Code checks: blocklist match, theme-with-code, sideload (vsix), broad activation (`*` / `onStartupFinished`), typosquat (Levenshtein ≤2 vs top-100), extension-pack expansion, dangerous `vscode:uninstall` hooks. 7 JetBrains checks: theme-with-code, broad activation (`application-components`), `Premain-Class` instrumentation (HIGH — javaagent retransform), native binaries (`.so`/`.dylib`/`.dll`/`.jnilib`), long `<depends>` chains, typosquat vs top JetBrains plugins, shaded-jar advisory. Both branches orchestrate reused scanners (UNI/ENT/NET/TNT/MEM/SCR) per extension with bounded concurrency (default 4). Scanner prefix: IDE. OWASP: LLM01, LLM02, LLM03, LLM06, ASI02, ASI04. Offline by default, `--online` opt-in for Marketplace/OSV.dev lookups. Knowledge: `knowledge/top-vscode-extensions.json`, `knowledge/top-jetbrains-plugins.json`, `knowledge/ide-extension-threat-patterns.md`, `knowledge/marketplace-api-notes.md`, `knowledge/jetbrains-marketplace-api-notes.md`.
**v6.4.0 — URL support.** Targets can be Marketplace, OpenVSX, or direct `.vsix` URLs. Pipeline: `lib/vsix-fetch.mjs` (HTTPS-only fetch with 50MB cap, 30s timeout, SHA-256, manual redirect host whitelist) → `lib/zip-extract.mjs` (zero-dep ZIP parser, rejects zip-slip/symlink/absolute/drive-letter/encrypted/ZIP64, caps: 10 000 entries, 500MB uncomp, 100x ratio, depth 20) → existing scan pipeline against extracted `extension/` subdir → temp dir always cleaned in `try/finally`. Envelope.meta.source = `{ type: "url", kind, url, finalUrl, sha256, size, publisher?, name?, version? }`.
**v6.5.0 — OS sandbox.** Fetch + extract for URL targets now spawns `lib/vsix-fetch-worker.mjs` in a sub-process wrapped by `sandbox-exec` (macOS) or `bwrap` (Linux) — same primitives reused from `git-clone.mjs`. Helper: `lib/vsix-sandbox.mjs` exports `buildSandboxProfile`, `buildBwrapArgs`, `buildSandboxedWorker`, `runVsixWorker`. Worker IPC: argv `--url <url> --tmpdir <dir>` → single JSON line on stdout (`{ok, sha256, size, finalUrl, source, extRoot}` or `{ok:false, error, code?}`). Defense-in-depth — if the in-process ZIP parser ever has a bypass, the kernel still refuses writes outside `<tmpdir>`. `scan(target, { useSandbox })` defaults to `true`; tests pass `false` since `globalThis.fetch` mocks do not cross process boundaries. Windows fallback: in-process with `meta.warnings` advisory. Envelope `meta.source.sandbox`: `'sandbox-exec' | 'bwrap' | 'none' | 'in-process'`.
**v6.6.0 — JetBrains Marketplace URL fetch + JetBrains branch.** URL targets can also be `https://plugins.jetbrains.com/plugin/<numericId>-<slug>` (metadata-resolved → xmlId download) or `https://plugins.jetbrains.com/plugin/download?pluginId=<xmlId>&version=<v>` (direct). `lib/vsix-fetch.mjs` gains `detectUrlType` JetBrains kinds, `fetchJetBrainsPlugin`, host allowlist `plugins.jetbrains.com`. `buildSandboxedWorker(dirs, workerPath)` now accepts a custom worker path — `lib/jetbrains-fetch-worker.mjs` reuses the same IPC contract. Envelope `meta.source.kind` can be `'jetbrains-marketplace' | 'jetbrains-download'`. Installed-plugin scan runs JB-specific checks (see scanner bullet above) and shares the UNI/ENT/NET/TNT/MEM/SCR orchestration. `.kt`, `.groovy`, `.scala` added to `taint-tracer` code extensions.
Run: `node scanners/ide-extension-scanner.mjs [target|url] [--vscode-only] [--intellij-only] [--include-builtin] [--online] [--format json|compact] [--fail-on <sev>] [--output-file <path>]`. Invoked by `/security ide-scan`.
## Token Budget (ENFORCED)
All commands total ~600 lines. All commands use registered subagent types.
- Commands are short dispatchers (~30-60 lines) — no inline report templates or format specs
- All agents use registered `subagent_type` — agent instructions are system prompt, never file reads
- Max 1-2 knowledge files per agent invocation (threat-patterns + secrets-patterns)
- OWASP files are NEVER passed by commands — agents reference them from their own system prompt
- Agents run sequentially to avoid burst rate limits
- `pre-install-supply-chain.mjs` queries OSV.dev for CVEs on every package install
## CLI
`bin/llm-security.mjs` — standalone CLI entry point. Works without Claude Code via `npx llm-security` or `node bin/llm-security.mjs`.
Subcommands: `scan`, `deep-scan`, `posture`, `audit-bom`, `benchmark`. Dispatches to scanner scripts via `child_process.spawn`.
`package.json` `bin` field: `"llm-security": "./bin/llm-security.mjs"`. `files` whitelist: only `bin/` + `scanners/` published to npm.
## CI/CD Integration
Pipeline templates in `ci/`: `github-action.yml`, `azure-pipelines.yml`, `gitlab-ci.yml`. Documentation: `docs/ci-cd-guide.md`.
All templates use `--fail-on high --format sarif --output-file results.sarif` with SARIF upload per platform.
Standalone CLI makes zero network calls in default mode. Schrems II compatible in default offline mode. Optional OSV.dev enrichment (`supply-chain-recheck --online`) transmits package identifiers to a Google-operated API and is a separate compliance consideration.
## Knowledge Files (20)
| File | Content |
|------|---------|
| `skill-threat-patterns.md` | 7 threat categories for skill/command scanning |
| `mcp-threat-patterns.md` | 9 MCP threat categories (MCP01-MCP10) |
| `secrets-patterns.md` | Regex patterns for 10+ secret types |
| `owasp-llm-top10.md` | OWASP LLM Top 10 (2025) with Claude Code mappings |
| `owasp-agentic-top10.md` | OWASP Agentic AI Top 10 (ASI01-ASI10) |
| `owasp-skills-top10.md` | OWASP Skills Top 10 (AST01-AST10) — skill-specific threats |
| `mitigation-matrix.md` | Threat-to-control mappings |
| `top-packages.json` | Known package lists for supply chain checks |
| `skill-registry.json` | Seed data for skill signature registry |
| `prompt-injection-research-2025-2026.md` | 7 research papers (2025-2026) with implications for hook defenses |
| `deepmind-agent-traps.md` | DeepMind AI Agent Traps — 6 categories, 43 techniques, coverage matrix |
| `attack-scenarios.json` | 64 red-team scenarios across 12 categories for attack simulation |
| `attack-mutations.json` | Synonym tables and mutation rules for adaptive red-team testing |
| `compliance-mapping.md` | EU AI Act, NIST AI RMF, ISO 42001, MITRE ATLAS mappings to plugin capabilities |
| `norwegian-context.md` | Norwegian regulatory landscape — Datatilsynet, NSM, Digitaliseringsdirektoratet |
| `ide-extension-threat-patterns.md` | 10 IDE-extension detection categories (VS Code + JetBrains) with 2024-2026 case studies |
| `top-vscode-extensions.json` | Top ~100 VS Code Marketplace extension IDs (typosquat seed) + blocklist entries |
| `top-jetbrains-plugins.json` | Top JetBrains plugin IDs (typosquat seed) + blocklist entries (v6.6.0) |
| `marketplace-api-notes.md` | VS Code Marketplace + OpenVSX API endpoints used by `lib/vsix-fetch.mjs` (v6.4.0) |
| `jetbrains-marketplace-api-notes.md` | JetBrains Marketplace API endpoints used by `fetchJetBrainsPlugin` (v6.6.0) |
## Reports
Scan reports are stored in `reports/` as `.docx` (for sharing) with `.md` source.
## Examples (runnable demonstrations)
Self-contained, deterministic threat-fixture mappes under `examples/`. Each mappe har `README.md`, fixture/script/transcript, `run-*.{sh,mjs}`, og `expected-findings.md`. Demonstrasjoner — ikke unit-tester.
| Mappe | Demonstrerer | Hooks/scanners | Sentinel |
|-------|--------------|----------------|----------|
| `malicious-skill-demo/` | Skill scanner end-to-end (UNI/ENT/PRM/DEP/TNT/NET + 7 LLM-kategorier) | `scan-orchestrator` + agents | BLOCK 100/100 |
| `prompt-injection-showcase/` | 61 payloads × 19 kategorier mot `pre-prompt-inject-scan`, `post-mcp-verify`, `pre-bash-destructive` | runtime hooks | per-kategori expected outcome |
| `lethal-trifecta-walkthrough/` | Rule-of-Two advisory på leg 3 (WebFetch → Read .env → Bash curl POST) + suppression | `post-session-guard` | advisory på stage 3 |
| `mcp-rug-pull/` | Cumulative drift-advisory (E14, v7.3.0) — 7 stadier under per-update-terskel, kumulativt over 25% baseline | `post-mcp-verify` + `mcp-description-cache.mjs` | advisory på stage 7 |
| `supply-chain-attack/` | PreToolUse-blokk på kompromittert pakke + scope-hop advisory + dep-auditor typosquats + postinstall curl-pipe | `pre-install-supply-chain` + `dep-auditor` + `supply-chain-data` | 6+ funn, 2 advisories, 1 BLOCK |
| `poisoned-claude-md/` | 6 detektorer (injection / shell / URL / credential paths / permission expansion / encoded payloads) inkl. E15 agent-fil-overflate | `memory-poisoning-scanner` | ≥18 funn fordelt på 2 filer |
| `bash-evasion-gallery/` | T1-T9 disguised destructive commands → normalisert + blokkert (defense-in-depth over Claude Code 2.1.98+) | `pre-bash-destructive` + `bash-normalize` | 10 BLOCK eksitkoder |
| `toxic-agent-demo/` | Single-component lethal trifecta — agent med [Bash, Read, WebFetch] uten hook-guards = CRITICAL TFA-finding | `toxic-flow-analyzer` (TFA) | 1 CRITICAL `Lethal trifecta:` |
| `pre-compact-poisoning/` | PreCompact-hook fanger injection + AWS-shaped credential i syntetisk transcript på tvers av off/warn/block-modus | `pre-compact-scan` | 9 pass: block exit 2 + reason; warn systemMessage; off skip; benign passes |
State-isolering: alle eksempler som muterer global state bruker run-script PID (post-session-guard via `${ppid}.jsonl`) eller env-overrides (`LLM_SECURITY_MCP_CACHE_FILE` for MCP-cache). Brukerens reelle `/tmp/llm-security-session-*.jsonl` og `~/.cache/llm-security/` røres aldri.

View file

@ -0,0 +1,47 @@
# LLM Security — Version history
Per-release notes for v7.0.0 onward. Imported from `CLAUDE.md` via `@docs/version-history.md`.
## v7.0.0 — Severity-dominated risk scoring (v2 model, BREAKING)
Three changes target the false-positive cascade on real codebases (hyperframes.com gave `BLOCK / Extreme / 100`, ~70% noise):
1. **Risk-score v2 formula** (`scanners/lib/severity.mjs`) — severity-dominated, log-scaled within tier. Replaces v1 sum-and-cap that collapsed every non-trivial scan to 100/Extreme. Tiers: critical → 7095, high only → 4065, medium only → 1535, low only → 111. Verdict cutoffs realigned to new bands (BLOCK ≥65, WARNING ≥15). `info` findings are observability-only — counted in OWASP aggregates but contribute zero to risk_score, verdict, and riskBand (B3, v7.2.0 — was undocumented pre-7.2.0). See `severity.mjs` JSDoc for full contract.
2. **Rule-based entropy scanner with file-extension skip, 8 line-level suppression rules, and configurable policy** — extensions skipped (`.glsl/.frag/.vert/.shader/.wgsl/.css/.scss/.sass/.less/.svg/.min.*/.map`); line-suppression rules (GLSL keywords, CSS-in-JS, inline SVG, ffmpeg `filter_complex`, User-Agent strings, SQL DDL, `throw new Error(\`...\`)`, markdown image URLs). Configurable via `.llm-security/policy.json` `entropy` section (thresholds, `suppress_extensions`, `suppress_line_patterns`, `suppress_paths`). Envelope `calibration` block reports skip counters + effective thresholds + policy source.
3. **DEP typosquat allowlist expansion** — 22 npm + 5 PyPI entries for short-name tools that tripped Levenshtein detection on every modern codebase (`knip`, `oxlint`, `tsx`, `nx`, `rimraf`, `uv`, `ruff`, etc.).
See `docs/security-hardening-guide.md` §6 for the calibration story.
## v7.1.1 — Scan-rapport narrative coherence (patch)
Three coordinated edits address the whiplash symptom that survived v7.0.0 (numbers fixed, narrative still walked findings back as "false positive" in prose):
(a) `agents/skill-scanner-agent.md` Step 2.5 mandates context-first severity assignment — every signal has exactly one disposition (suppressed OR reported), no per-finding walk-back; (b) `templates/unified-report.md` gains a `### Narrative Audit` block in Executive Summary surfacing `summary.narrative_audit.suppressed_findings.{count, by_category}` from the agent's trailing JSON; (c) both files updated from stale v1 risk-formula constants to the v2 model that has been authoritative in `severity.mjs` since v7.0.0. Counter is distinct from the existing top-level `output.suppressed` (`.llm-security-ignore` rule integer). Out-of-scope but flagged: `commands/scan.md:113-114` retains the v1 formula; resolution deferred to Batch B.
## v7.3.0 — MCP cumulative-drift baseline (Wave C of Batch C)
Closes E14 from `docs/critical-review-2026-04-20.md`. The `mcp-description-cache.mjs` schema gains a sticky `baseline` slot per tool plus a 10-event rolling `history` array (FIFO). Cumulative drift = `levenshtein(current, baseline) / max(|current|, |baseline|)`; when the ratio crosses `mcp.cumulative_drift_threshold` (default 0.25), `post-mcp-verify.mjs` emits a separate MEDIUM `mcp-cumulative-drift` advisory. The existing per-update >10% drift signal is unchanged — both fire independently. Slow-burn rug-pulls that keep each update under the per-update threshold but cumulatively diverge from baseline are now caught. Baseline survives the 7-day TTL purge so detection persists across the full window. New `/security mcp-baseline-reset` slash command (plus `scanners/mcp-baseline-reset.mjs` CLI: `--list`, `--target <tool>`, or no-args clear-all) lets the user acknowledge a legitimate MCP server upgrade — clearing the baseline causes the next call to seed a fresh one from the incoming description; description, firstSeen, lastSeen, and history are preserved for audit. `LLM_SECURITY_MCP_CACHE_FILE` env var overrides the cache path for end-to-end testing without polluting the user's real `~/.cache/llm-security/mcp-descriptions.json`.
## v7.3.0 — Env-var deprecation warnings (D3 of Batch C, Wave D)
Closes 8.7 from `.claude/projects/2026-04-29-batch-c-scope-finalize/plan.md`. `scanners/lib/policy-loader.mjs` exports a new helper `getPolicyValueWithEnvWarn(section, key, envVarName, defaultValue)` — env still wins per Preferences (existing behaviour), but when both the env-var AND the `policy.json` key are explicitly set, the helper 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.` Module-scoped `Set` dedupes per env-var name across call-sites. Four overlapping vars are wired through the helper: `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`), `LLM_SECURITY_AUDIT_LOG``audit.log_path` (in `scanners/lib/audit-trail.mjs`). `DEFAULT_POLICY` gains `trifecta.escalation_window: 5` to close the gap noted in the plan revisions table (M10). Env-only vars without policy.json equivalents (`LLM_SECURITY_UPDATE_CHECK`, `LLM_SECURITY_PRECOMPACT_MODE`, `LLM_SECURITY_PRECOMPACT_MAX_BYTES`, `LLM_SECURITY_IDE_ROOTS`, `LLM_SECURITY_MCP_CACHE_FILE`) are unchanged — they emit no deprecation signal because there is nothing to deprecate yet.
## v7.5.0 — Playground (additive surface, no scanner/hook behavior changes)
Single-file SPA at `playground/llm-security-playground.html` (~10 200 lines) for onboarding, demo og workshop-bruk uten Claude Code-installasjon. Parser + renderer for alle 18 `produces_report=true`-kommandoer i `CATALOG`. State i IndexedDB primær (`llm-security-playground-v1`) med localStorage-fallback, sirkelfri Proxy + EventTarget store, microtask-batchet render. Theme-bootstrap med FOUC-prevention. 4 overflater: onboarding (5 grupper) → home (3 tracks) → catalog (20 kommandoer) ⇄ project (rapporter / oversikt / kontekst / eksport). Demo-state har tre prosjekter inline; `dft-komplett-demo` har alle 18 rapporter ferdig parsed for klikk-gjennom. Vendor-synket design-system under `playground/vendor/playground-design-system/` (sjekksum-låst via `MANIFEST.json`, redigeres aldri direkte). Test-fixtures under `playground/test-fixtures/` (én markdown-fil per kommando) er kontrakt-anker for parser-utvikling. Skjermdumper i `playground/screenshots/v7.5.0/`. Eksponerte vinduer-globaler for testing/automasjon: `__store`, `__navigate`, `__loadDemoState`, `__scheduleRender`, `__PARSERS`, `__RENDERERS`, `__CATALOG`, `__inferVerdict`, `__inferKeyStats`, `__renderPageShell`, `__handlePasteImport`. Inkluderer fix av `normalizeVerdictText` regex-rekkefølge: GO-WITH-CONDITIONS sjekkes før GO så betinget verdict ikke kollapser til ALLOW.
## v7.6.0 — Playground Tier 3-referanse-case (additive surface, no scanner/hook behavior changes)
Playgroundet er nå en visuelt og strukturelt fullført referanse-implementasjon for `shared/playground-design-system/` Tier 3-supplementet. 8 nye Tier 3-komponenter integrert i de 18 rapport-rendererne: `tfa-flow` + `tfa-leg` + `tfa-arrow` (lethal trifecta-kjede med `<button>`-elementer + ARIA-group/aria-label) i `renderScan` + `renderDeepScan`; `mat-ladder` + `mat-step` (5-trinns modenhets-stige med terskler 0/25/50/75/95% PASS) i `renderPosture`; `suppressed-group` (narrative-audit fra `summary.narrative_audit.suppressed_findings`) i `renderScan` + `renderDeepScan`; `codepoint-reveal` + `cp-tag`/`cp-zw`/`cp-bidi` (Unicode-steganografi side-ved-side reveal med U+200B-D|FEFF|2060|180E → `cp-zw`, U+202A-E|2066-9 → `cp-bidi`-detection) i `renderMcpInspect`; `top-risks` + `top-risk[data-severity]` (rangert top-funn-listing, semantisk `<ol>`, ekskluderer info-funn) i `renderScan`/`renderDeepScan`/`renderPluginAudit`/`renderPosture`/`renderAudit`; utvidet `recommendation-card[data-severity]` (severity-tinted advisory) på alle inline-bruk + nye per-bucket advisory-cards i `renderClean` + intro snapshot + diff-rows i `renderHarden` (action-mapping CREATE→positive / APPEND→medium / MERGE→low / SKIP→low); `risk-meter` (band-visualisering 0-100 med Low/Medium/High/Critical/Extreme bands) på 5 archetypes (scan, deep-scan, plugin-audit, audit, red-team); `card--severity-{level}` modifier på `findings__item`-cards. Wave 1 (Sesjon 2) la til `badge--scope-security` (identitets-chip), `verdict-pill-lg` med `__verdict`+`__sub` (erstatter custom verdict-pill på alle 18 rapport-typer), og DS Tier 3 `form-progress` + `fp-step` i onboarding-wizard. Wave 0 (Sesjon 1) slettet ~30 duplikat-CSS-deklarasjoner fra `<style>`-blokken (DS vinner cascade) og harmoniserte page-shell på alle 4 overflater. 5 nye DS-helpers: `renderToxicFlow`, `renderMatLadder`, `renderSuppressedGroup`, `renderCodepointReveal`, `renderTopRisks`. 2 nye normaliserings-helpers: `mapSeverityToCardLevel(input)` (severity + action-types til DS-konvensjoner) og `parseNarrativeAudit(md)`. 12 skjermdumper planlagt i `playground/screenshots/v7.6.0/`. A11Y-rapport oppdatert (`playground/A11Y-RAPPORT.md`) — WCAG 2.1 AA bekreftet, severity-soft fargepar verifisert, semantiske elementer (`<ol>`, `<button>`, `<section>`) erstatter generic `<div>`. Filendring totalt over 5 sesjoner: 10209 → 10677 linjer. Kjent begrensning: `parsed.findings` er tom for `deep-scan`/`audit` demo-fixturer (parser-begrensning, ikke fikset i v7.6.0 — sporet for v7.6.x patch).
## v7.6.1 — Playground visuell-patch (no scanner/hook behavior changes)
Seks bugs fanget av maintainer ved manuell verifisering i nettleser etter v7.6.0-release. Alle skyldtes mismatch mellom DS-klasser og hvordan playground-rendrere brukte dem (eller manglende DS-implementasjoner av klasser playground-rendrere antok eksisterte).
(1) `renderFindingsBlock` brukte `.findings` outer-class som DS har som 2-kolonners grid (`grid-template-columns: 360px 1fr`) for list+detail-panel-layout — playground brukte den uten detail-panel, headeren havnet i venstre 360px-kolonne, items i 1fr. Erstattet med `<section class="report-meta">` + `<h4>` + korrekt `findings__list > findings__group > findings__group-header + findings__items`-mønster.
(2) `.report-table` manglet helt i DS men brukes i 7+ rendrere (OWASP-kategorier, Supply chain, Scanner Risk Matrix, Plugin-meta, Permission-matrise, Live-meter, Siste runs, Godkjenninger, Mitigation roadmap) — lagt lokal CSS-implementasjon i playground-HTML `<style>`-blokk (border-collapse, zebra-hover, header-styling).
(3) `renderPreDeploy` traffic-lights brukte `.sm-card__grade` som er fast 28×28 px (designet for én A-F-bokstav) — kuttet "PASS" til "AS" og "PASS-WITH-NOTES" til "PASS-WITH-..." i alle traffic-light-cards. Erstattet med bredde-tilpasset status-pill via inline styling (severity-soft + on tokens).
(4) Threat-model matrix-bobler ikke klikkbare — `<span>` uten event-handler. Erstattet med `<button type="button" data-threat-id>` + `aria-label`. Click-handler scroller til tilsvarende rad i Trusler-tabellen og fremhever den i 1.6 sek.
(5) Radar-labels overlappet ved 6+ akser — alle brukte `text-anchor="middle"` med samme offset. Økt SVG-størrelse fra 280×280 til 380×380, radius fra 105 til 125, bytter `text-anchor` fra `middle` til `start`/`end` basert på horisontal-posisjon (`Math.cos(ang)` > 0.2 / < -0.2 / mellom).
(6) `recommendation-card__body` tekstoverflyt på lange single-line tekster (vilkår, owner-tags, dato) — lagt `overflow-wrap: anywhere; word-break: break-word` i lokal `<style>`-blokk.
4/4 fix-spesifikke smoke-tester passerer + 18/18 renderere produserer fortsatt komplett HTML mot `dft-komplett-demo` (regresjons-test). Filendring 10677 → 10753 linjer (+76 netto).