# Changelog All notable changes to the LLM Security Plugin are documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [7.1.1] - 2026-04-29 Patch release. Closes the narrative-coherence gap that survived v7.0.0: the severity-dominated risk score corrected the numbers, but the agent prompt continued to emit raw signals and walk them back as "false positive" in prose, producing whiplash in the rendered report. v7.1.1 makes severity assignment context-first at the prompt level and adds a structural counter for suppressed signals. ### Fixed - **Agent prompt context-first severity** (`agents/skill-scanner-agent.md`). New Step 2.5 mandates that every signal has exactly one disposition — suppressed (counted only) or reported (full finding) — with the split happening before severity is assigned. The phrases "false positive", "legitimate framework", and "no action required" are forbidden in finding-body text and reserved for the new `## Suppressed Signals` section. Verdict Logic section was also updated to reference v2 tiers and cutoffs from `severity.mjs` (BLOCK ≥65, WARNING ≥15) — replaces the stale v1 sum-and-cap formula that had been left in place after the v7.0.0 numeric overhaul. - **Template v1 → v2 risk constants** (`templates/unified-report.md`). HTML-comment header at lines 55-66 now describes the v2 tiers and cutoffs the engine has been using since v7.0.0. Adds an `### Narrative Audit` block inside Executive Summary surfacing `summary.narrative_audit.suppressed_findings.{count, by_category}` for reviewer transparency. The block does NOT affect verdict computation. ### Added - **`tests/scanners/skill-scanner-narrative.test.mjs`** — 11 assertions against `tests/fixtures/skill-scan/hyperframes-like/`. Covers deterministic content-extractor (exactly 1 HIGH HITL trap, ≥ 2 framework env-var refs, has_injection true on any signal, has_critical_injection false), entropy scanner (calibration block present, ≤ 1 finding after suppression), inline co-monotonicity guard (`{ high: 1 }` → WARNING / High), and prompt-contract static assertions on `agents/skill-scanner-agent.md` and `templates/unified-report.md`. - **`tests/fixtures/skill-scan/hyperframes-like/`** — synthetic skill with HTML5 canvas / CSS keyframes / inline SVG data URI noise plus exactly one genuine HITL trap signal. Committed (not gitignored). `.llm-security-ignore` uses the canonical `SCANNER:glob` format (`ENT:**/*.md`). ### Tests - 1511 → 1522 tests (adds 11 new). Co-monotonicity sweep at `tests/lib/severity.test.mjs:252-303` unchanged and green. ### Why Hyperframes.com re-test on 2026-04-19 produced `risk_score 20 / WARNING / 1 HIGH` numerically (correct after v7.0.0) but the agent listed 8 findings in prose and walked 6 back as "false positive". v7.1.1 closes the structural gap that allowed this: severity is assigned ONCE, context-first, and suppressed signals are categorical telemetry rather than free-text walk-backs. ### Out of scope (flagged for Batch B) - `commands/scan.md:113-114` retains the v1 risk formula and acts as a third source of truth alongside agent prompt and severity.mjs. Will be unified in v7.2.0. ## [7.1.0] - 2026-04-29 Patch release closing the highest-impact items from the v7.0.0 adversarial review (`docs/critical-review-2026-04-20.md`, grade B-). Bug-fixes plus an honesty-sweep on documentation language. No new features and no behavioral changes outside the listed fixes. ### Fixed - **Pathguard regex hole — `.env.*.*.*` could be written without blocking** (`hooks/scripts/pre-write-pathguard.mjs`). The old `ENV_PATTERNS` only matched a single dotted segment after `.env`, so `.env.production.local.backup`, `.env.prod.local.bak`, etc. slipped through. Replaced with `/[\\/]\.env(\.[A-Za-z0-9._-]+)*$/` covering arbitrary multi-segment suffixes. `.envrc` continues to be allowed. Commit `751f119`. (Critical-review B1.) - **Distributed trifecta in BLOCK mode only warned** (`hooks/scripts/post-session-guard.mjs`). The previous block-gate required *both* `LLM_SECURITY_TRIFECTA_MODE=block` *and* a "concentrated" or "sensitive-path" qualifier, so a trifecta whose three legs landed on different MCP servers without a sensitive path was advisory-only. Removed the AND-gate; block mode now blocks any detected trifecta. Commit `36be963`. (Critical-review B2.) - **JSDoc/CHANGELOG arithmetic for `riskScore({critical: 4})`** (`scanners/lib/severity.mjs:23`, `CHANGELOG.md` v7.0.0 tier description). The actual computation has always been `70 + log2(5)*10 = 93.22 → round → 93`; only the docs said `90`. Fixed; pin test added. (Critical-review B4.) ### Changed - **Honesty-sweep on documentation language** (`CLAUDE.md`, `commands/ide-scan.md`, `knowledge/mitigation-matrix.md`, `docs/security-hardening-guide.md`). Critical-review §9 flagged a set of overclaim phrasings; rewritten while preserving accurate underlying claims: - "Trustworthy scoring (BREAKING)" → "Severity-dominated risk scoring (v2 model, BREAKING)" - "Context-aware entropy scanner" → "Rule-based entropy scanner with file-extension skip, 8 line-level suppression rules, and configurable policy" - "1487 tests" → "1511 unit and integration tests; mutation-testing coverage not published" - "Fully Schrems II compatible" → "Schrems II compatible in default offline mode. Optional OSV.dev enrichment is a separate compliance consideration" - "Rule of Two enforcement" → "Rule of Two detection (configurable; default warn; blocks on high-confidence trifectas in opt-in `block` mode)" - "Hardened ZIP extractor" → suffix " — no fuzz-testing results published to date" - "defense-in-depth" → preserved, but quantified in `docs/security-hardening-guide.md` §4: "three independent detection layers with documented bypass classes" - **CaMeL claims toned down** (`hooks/scripts/post-session-guard.mjs:646`, `CLAUDE.md:184`). Implementation is opportunistic byte-matching of truncated output fingerprints (first 200 bytes, SHA-256/16-hex tag) — trivially bypassed by mutation, summarisation, or re-encoding. Renamed framing from "CaMeL-inspired data-flow tagging (SHA-256 provenance tracking)" to "output fingerprint matching (inspired by CaMeL but not a CaMeL capability-tracking implementation)". (Critical-review B8.) - **Plugin version:** `7.0.0 → 7.1.0` across `package.json`, `.claude-plugin/plugin.json`, `scanners/ide-extension-scanner.mjs` (`VERSION`), README badge, CLAUDE.md header, marketplace root README. Test count `1487 → 1511` in marketplace root README. ### Tests - **+8 tests for B1 pathguard** (`tests/hooks/pre-write-pathguard.test.mjs`): 6 multi-segment BLOCK + 1 `.envrc` ALLOW + 1 sentinel. - **+1 test for B2 distributed trifecta** (`tests/hooks/post-session-guard.test.mjs`): three legs from different sources blocked under `block` mode. - **+15 sweep tests + 1 anchor test for verdict/riskBand co-monotonicity** (`tests/lib/severity.test.mjs`): asserts `(verdict, riskBand)` agree under v7.0.0 contract for representative count vectors. Catches future drift between scoring tiers, verdict cutoffs, and riskBand cutoffs. Anchor test pins `riskScore({critical: 4}) === 93` so doc/code drift fails loudly. - **Total: 1511 tests** (was 1487). All green. ### Why - Pathguard and trifecta-block bugs were live security holes — both fixed at the hook level so users on the default install get the fix automatically. - The honesty-sweep is a deliberate response to the critical-review CISO-perspective (§F): "Would a CISO install this?" — overclaim language was identified as a blocker for regulated environments. Toning it down does not weaken the actual defenses; it lets users trust the documentation. ## [7.0.0] - 2026-04-19 ### BREAKING CHANGES - **Risk-score formula rewritten** (`scanners/lib/severity.mjs`). The v1 sum-and-cap formula (`critical*25 + high*10 + medium*4 + low*1`, capped at 100) collapsed every non-trivial scan to 100/Extreme regardless of actual risk distribution. v2 is severity-dominated and log-scaled within tier: - Critical present → 70–95 (1=80, 2=86, 4=93, 10=95) - High only → 40–65 (1=48, 5=60, 17=65) - Medium only → 15–35 (1=20, 5=28, 50=33) - Low only → 1–11 (1=4, 10=11) - None → 0 Verdict cutoffs realigned to new bands: `BLOCK` if critical ≥1 or score ≥65, `WARNING` if high ≥1 or score ≥15. Legacy v1 formula kept as `riskScoreV1()` for reference only. CI pipelines with `--fail-on` thresholds may need recalibration — see `docs/security-hardening-guide.md` §6. - **Verdict/band cutoffs aligned for co-monotonicity.** Old cutoffs (BLOCK ≥61, WARNING ≥21) could produce "BLOCK / Medium band" or "ALLOW / High band" contradictions. New cutoffs (65, 15) are locked to the v2 `riskBand()` boundaries. ### Added - **Context-aware entropy scanner** (`scanners/entropy-scanner.mjs`). Skip-lists and line-level rules drastically reduce false positives in shader/CSS/HTML/SQL-heavy codebases: - File-extension skip: `.glsl, .frag, .vert, .shader, .wgsl, .css, .scss, .sass, .less, .svg` + compound `.min.js, .min.css, .map` - Line-level rules 11–18 in `isFalsePositive()`: GLSL keywords (`uniform`, `vec3`, `texture2D`...), CSS-in-JS templates (`styled.`), inline `` markup, ffmpeg `filter_complex` syntax, browser `User-Agent` strings, SQL DDL on dedicated lines (`^\s*(SELECT|INSERT|UPDATE|DELETE|CREATE|...)`), `throw new Error(\`…\`)` templates, markdown image syntax with external URLs (`![alt](https://cdn…)` — common in JSON content indexes) - Scanner envelope gains `calibration` block: `files_skipped_by_extension`, `files_skipped_by_path`, effective `thresholds`, and `policy_source` (`'default' | 'policy.json'`) - **Policy-driven entropy configuration** — `.llm-security/policy.json` `entropy` section accepts: - `thresholds.{critical,high,medium}.{entropy,minLen}` — override defaults per project - `suppress_extensions: string[]` — additional file extensions to skip - `suppress_line_patterns: string[]` — user-defined regexes for line suppression - `suppress_paths: string[]` — substring match against `relPath` to skip entire paths (e.g., `"vendored/"`) - **DEP typosquat allowlist expansion** (`knowledge/typosquat-allowlist.json`). 22 npm + 5 PyPI entries for short-name modern tools that tripped Levenshtein detection on nearly every real codebase: - npm: `knip`, `oxlint`, `tsx`, `nx`, `rimraf`, `glob`, `tar`, `zod`, `ky`, `ow`, `esm`, `ip`, `qs`, `url`, `prettier`, `vitest`, `vite`, `rollup`, `swc`, `turbo`, `bun`, `deno` - PyPI: `uv`, `ruff`, `rich`, `typer`, `anyio` - **Synthesizer "Scan Calibration" section** (`agents/deep-scan-synthesizer-agent.md`). Heuristic: omit if <5% files skipped, flag prominently if >80% skipped by path (signals over-aggressive user policy). Agent instructed to NEVER override scanner verdict with narrative opinion. - **26 new unit tests** (`tests/scanners/entropy-context.test.mjs`): A. File-extension skip (4), B. Line-level rules 11–18 (10), C. Policy overrides (3); plus expanded `tests/lib/severity.test.mjs` with v2 scoring/band/verdict tables (70 tests total, was 52). **Total: 1487 tests (was 1461).** ### Changed - `tests/lib/output.test.mjs:243` — "1 critical = score 80" under v2 (was 25 under v1). - `scanners/lib/file-discovery.mjs` — `TEXT_EXTENSIONS` now includes `.sass` and GPU shader source extensions (`.glsl, .frag, .vert, .shader, .wgsl`) so these files are discovered and explicitly counted as skipped by the entropy scanner instead of invisibly filtered out. - Plugin version: `6.6.0 → 7.0.0` across `package.json`, `.claude-plugin/plugin.json`, `scanners/ide-extension-scanner.mjs` (`VERSION`), README badge, CLAUDE.md header, marketplace root README. ### Why - **Real-world scan on `hyperframes.com` produced `BLOCK / Extreme / 100` with ~70% noise** (shader strings, CSS gradients, bundled JS, Levenshtein false positives). A scanner that cries "extreme" on every project destroys its own credibility — users learn to ignore findings, so genuine threats slip past. - **Trustworthiness comes from calibration, not from detecting everything.** v7.0.0 accepts that some detection heuristics are noisy in context (entropy on shaders, typosquat on 2–3 letter tool names) and gives users both built-in suppression and policy-driven override controls. - **Verdict/score/band co-monotonicity fixed.** A user can now correctly reason: "HIGH band → WARNING verdict" without reading the source. The v1 cutoffs allowed a mid-High score (42) to produce ALLOW and a low-Medium score (22) to produce WARNING. ## [6.6.0] - 2026-04-18 ### Added - **JetBrains/IntelliJ plugin scanning.** `/security ide-scan` extends beyond VS Code forks to cover the JetBrains IDE family: IntelliJ IDEA, PyCharm, GoLand, WebStorm, RubyMine, PhpStorm, CLion, DataGrip, RustRover, Rider, Aqua, Writerside, Android Studio. Fleet and Toolbox are intentionally excluded (different plugin model, out of scope) - **OS-aware JetBrains plugin discovery** in `lib/ide-extension-discovery.mjs` — macOS `~/Library/Application Support/JetBrains//plugins/`, Windows `%APPDATA%\JetBrains\...`, Linux `~/.config/JetBrains/...`. Regex excludes Fleet/Toolbox - **Zero-dep `META-INF/plugin.xml` + `META-INF/MANIFEST.MF` parsers** in `lib/ide-extension-parser-jb.mjs` with nested-jar extraction for the common `/lib/*.jar → META-INF/plugin.xml` layout - **7 JetBrains-specific checks** in `runJetBrainsChecks`: `checkThemeWithCodeJB`, `checkBroadActivationJB` (`application-components`), `checkPremainClassJB` (HIGH — javaagent retransform), `checkNativeBinariesJB`, `checkDependsChainJB` (long mandatory `` = supply-chain pressure), `checkTyposquatJB` (Levenshtein vs top JetBrains plugins), `checkShadedJarsJB` (advisory — many bundled jars) - **JetBrains Marketplace URL fetch.** Supports `https://plugins.jetbrains.com/plugin/-` (metadata resolves numericId → xmlId, then downloads) and `https://plugins.jetbrains.com/plugin/download?pluginId=[&version=]` (direct download). Host allowlist: `plugins.jetbrains.com` only - **`fetchJetBrainsPlugin`** in `lib/vsix-fetch.mjs` with the same safety envelope as VSIX fetch (50 MB cap, 30 s timeout, SHA-256, manual redirect host whitelist) - **`lib/jetbrains-fetch-worker.mjs`** — sub-process worker mirroring the VSIX worker's JSON-line IPC. Shares the sandbox primitives through parameterized `buildSandboxedWorker(dirs, workerPath)` - **`.kt`, `.groovy`, `.scala`** added to `scanners/taint-tracer.mjs` `CODE_EXTENSIONS` so Kotlin/Groovy/Scala plugin sources are covered by taint analysis - **Knowledge additions:** `knowledge/jetbrains-marketplace-api-notes.md`, expanded `knowledge/ide-extension-threat-patterns.md` with JetBrains sections, seeded `knowledge/top-jetbrains-plugins.json` (no longer a stub) with `loadJetBrainsBlocklist` helper - **8 new test files / suites** covering JetBrains data, parsers, discovery, checks, URL fetch (unit + integration), end-to-end scan against a real JetBrains-layout fixture tree, plus a deterministic fixture-jar builder (`tests/helpers/build-jetbrains-fixtures.mjs`) that produces byte-identical reproducible jars. Total: 1461 tests (was 1352) ### Changed - `buildSandboxedWorker(dirs)` → `buildSandboxedWorker(dirs, workerPath)` — parameterized so the same sandbox wrapper is reused for VSIX and JetBrains workers instead of copying the primitives a third time - `/security ide-scan` command description updated to reflect the JetBrains branch; "JetBrains is a v1.1 stub" wording removed - `CLAUDE.md` and plugin README updated: scanner bullet rewritten to document the JetBrains branch, the seven JB-specific checks, and the new knowledge files - Plugin version: 6.5.0 → 6.6.0 across `package.json`, `.claude-plugin/plugin.json`, `scanners/ide-extension-scanner.mjs` (`VERSION`), README badge, CLAUDE.md header, marketplace root README - `tests/scanners/git.test.mjs` — loosened `findings.length` caps (were too tight for organic repo growth; baseline already exceeded them) ### Why - Parity with the VS Code branch: organizations running IntelliJ-family IDEs get the same pre-install and installed-plugin coverage Koi-style supply-chain attacks now target across both platforms - Reuse of `lib/vsix-sandbox.mjs` honors the user-memory rule "don't copy a third sandbox" — one set of primitives, two workers, same kernel-enforced FS confinement - JetBrains-specific checks target the platform's real attack surface: `Premain-Class` javaagents (class retransform at JVM startup), `application-components` (global lifecycle hooks), nested-jar shading (dependency opacity), and typosquat on `com.intellij.*` / `org.jetbrains.*` namespaces ## [6.5.0] - 2026-04-17 ### Added - **OS sandbox for `/security ide-scan `.** VSIX fetch + extract now runs in a sub-process wrapped by `sandbox-exec` (macOS) or `bwrap` (Linux), reusing the same primitives proven by the `git clone` sandbox introduced in v5.1. Defense-in-depth: even if `zip-extract.mjs` has an undiscovered bypass, the kernel refuses any write outside the per-scan temp directory - **`scanners/lib/vsix-fetch-worker.mjs`** — Sub-process worker. Argv: `--url --tmpdir `. Emits a single JSON line on stdout (`{ok, sha256, size, finalUrl, source, extRoot}` or `{ok:false, error, code?}`). Exit 0 on success, 1 on failure. Silent on stderr - **`scanners/lib/vsix-sandbox.mjs`** — Wrapper. Exports `buildSandboxProfile`, `buildBwrapArgs`, `buildSandboxedWorker(tmpDir, args)`, `runVsixWorker(url, tmpDir, opts)`. 35 s timeout, 1 MB stdout cap, deterministic JSON-line protocol - **`scan(url, { useSandbox })` option.** Default `true` for CLI invocations; tests pass `false` to keep `globalThis.fetch` mocking working (mocks do not cross process boundaries). When sandbox unavailable on the platform (e.g., Windows), a warning is added to `meta.warnings` and the scan still completes via the in-process fallback - **`meta.source.sandbox`** — New envelope field: `'sandbox-exec' | 'bwrap' | 'none' | 'in-process'`. Tells the report which protection layer was actually active - **8 new tests** in `tests/scanners/vsix-sandbox.test.mjs` covering profile generation per platform, worker arg construction, and live worker exit behavior on invalid URLs (no network required) ### Changed - `fetchAndExtractVsixUrl` in `ide-extension-scanner.mjs` is now sandbox-aware (`useSandbox` option, default `true`). Existing in-process logic preserved as fallback path - Version bump: 6.4.0 → 6.5.0 across all files ### Why - Aligns the IDE-scan URL pipeline with the same defense-in-depth posture as the GitHub clone pipeline — kernel-enforced FS confinement instead of in-process validation alone - VSIX is untrusted bytes from a third-party registry; even with hardened parsing, an OS sandbox is the right blast-radius constraint for filesystem writes ## [6.4.0] - 2026-04-17 ### Added - **`/security ide-scan ` — pre-install verification.** The IDE extension scanner now accepts URLs as targets and fetches the VSIX before scanning. Supported sources: - VS Code Marketplace: `https://marketplace.visualstudio.com/items?itemName=publisher.name` - OpenVSX: `https://open-vsx.org/extension/publisher/name[/version]` - Direct VSIX download: `https://example.com/path/foo.vsix` (HTTPS only) - **`scanners/lib/vsix-fetch.mjs`** — HTTPS-only fetcher with 50 MB compressed cap, 30 s total timeout, SHA-256 streamed during download, manual redirect handling with per-source host whitelist (Marketplace gallerycdn, OpenVSX blob storage). No npm dependencies — uses Node 18+ `fetch` - **`scanners/lib/zip-extract.mjs`** — Zero-dependency ZIP parser + safe extractor. Rejects: zip-slip via `..` paths, POSIX absolute paths, Windows drive letters, NUL bytes, encrypted entries, ZIP64, multi-disk archives, unsupported compression methods, symlink entries (Unix `0xA000` mode bits in `external_attr`). Caps: 10 000 entries, 500 MB uncompressed total, 100× expansion ratio (sum-uncomp / sum-comp), depth 20. STORE + DEFLATE only - **Envelope `meta.source`** — When invoked with a URL, the scan envelope's `meta.source` field carries `{ type: "url", kind, url, finalUrl, sha256, size, publisher, name, version, requestedUrl }` so reports can attribute findings to the upstream artifact - **`knowledge/marketplace-api-notes.md`** — Reference notes for the (undocumented but stable) Marketplace direct-download endpoint and the (officially documented) OpenVSX endpoints used by `vsix-fetch.mjs` - **48 new tests** across `tests/scanners/zip-extract.test.mjs` (validateEntryName / isSymlink / extractToDir happy + adversarial), `tests/scanners/vsix-fetch.test.mjs` (detectUrlType / isAllowedHost / readBodyCapped), `tests/scanners/ide-extension-url.test.mjs` (URL flow integration with `global.fetch` mock — Marketplace, OpenVSX, direct VSIX, malformed VSIX, zip-slip VSIX, network failure, unsupported URL, GitHub URL). 1344 tests total (was 1296). Test helper: `tests/lib/build-zip.mjs` builds adversarial ZIPs that real `zip` tools refuse to emit ### Changed - `scanners/ide-extension-scanner.mjs` early-detects URL targets and routes through fetch + extract → temp dir → existing single-target scan path. Temp directory cleaned in `try/finally` regardless of success/error/abort - CLI help text in `bin/llm-security.mjs` and `commands/ide-scan.md` updated with URL examples and security model - Version bump: 6.3.0 → 6.4.0 across all files ### Not supported (intentional) - GitHub repo URLs — would require `npm install` + `vsce package` build step. Use the Marketplace, OpenVSX, or a direct `.vsix` URL instead - VSIX `.signature.p7s` verification — deferred to v6.5.0 (requires X.509 / PKCS#7 parsing) - ZIP64 archives — real-world VSIX never approaches the 4 GB threshold ## [6.3.0] - 2026-04-17 ### Added - **IDE extension prescan** — New `/security ide-scan` command and `scanners/ide-extension-scanner.mjs` (prefix IDE) discover and audit installed VS Code extensions across 6 roots (`~/.vscode/extensions`, `~/.vscode-insiders/extensions`, `~/.cursor/extensions`, `~/.windsurf/extensions`, `~/.vscode-oss/extensions`, `~/.vscode-server/extensions`, plus Linux `code-server`). OS-aware discovery via `scanners/lib/ide-extension-discovery.mjs`. Manifest parsing via `scanners/lib/ide-extension-parser.mjs`. Data loading via `scanners/lib/ide-extension-data.mjs`. JetBrains discovery is a v1.1 stub. - **7 IDE-specific detection categories** — Blocklist match (CRITICAL), theme-with-code (HIGH, Material Theme pattern), sideload `.vsix` (HIGH unsigned / MEDIUM signed), broad activation `*` / `onStartupFinished` (MEDIUM/LOW, suppressed for top-100 exact matches), Levenshtein typosquat ≤2 vs top-100 (HIGH distance-1 / MEDIUM distance-2 against top-50), extension-pack expansion ≥3 (MEDIUM), dangerous `vscode:uninstall` hooks referencing `child_process`/`curl`/`wget`/`rm`/`powershell` (HIGH/LOW) - **Per-extension scanner orchestration** — Each discovered extension runs through UNI, ENT, NET, TNT, MEM, SCR scanners with bounded concurrency (default 4). MEM gets a filtered file list (README.md / CHANGELOG.md / package.json) to catch prompt-injection in marketplace-rendered text - **New knowledge files** — `knowledge/ide-extension-threat-patterns.md` (10 categories with 2024-2026 case studies from Koi Security — GlassWorm, WhiteCobra, TigerJack, Material Theme, VS Code Cryptojacking, MaliciousCorgi), `knowledge/top-vscode-extensions.json` (top ~100 Marketplace IDs + blocklist), `knowledge/top-jetbrains-plugins.json` (stub) - **CLI integration** — `bin/llm-security.mjs` gains `ide-scan` subcommand with passthrough flags - 22 new tests in `tests/scanners/ide-extension-scanner.test.mjs` (fixtures under `tests/fixtures/ide-extensions/`). 1296 tests total (was 1274) ### Changed - Version bump: 6.2.0 → 6.3.0 across all files ## [6.2.0] - 2026-04-17 ### Added - **Bash-normalize T5 + T6** — `scanners/lib/bash-normalize.mjs` now collapses `${IFS}` word-splitting (T5) and ANSI-C hex quoting `$'\xHH'` (T6) before the denylist gate runs. Defense-in-depth layer complementing the Claude Code 2.1.98+ harness fixes. 4 new unit tests in `tests/scanners/bash-normalize.test.mjs` - **PreCompact hook** — `hooks/scripts/pre-compact-scan.mjs` scans the transcript tail (default 500 KB) for injection patterns before Claude Code compacts context. Prevents poisoned summaries from surviving into the next turn. Modes: `block` / `warn` / `off` via `LLM_SECURITY_PRECOMPACT_MODE`. 6 new tests in `tests/hooks/pre-compact-scan.test.mjs`. Brings total hooks to 9 - **Security hardening guide** — `docs/security-hardening-guide.md` documents environment variables (`CLAUDE_CODE_EFFORT_LEVEL`, `ENABLE_PROMPT_CACHING_1H`, `CLAUDE_CODE_SCRIPT_CAPS`, all `LLM_SECURITY_*` modes), sandboxing (`sandbox-exec` / `bwrap` / fallback), T1-T6 normalization table, Opus 4.7 system card §5.2.1 + §6.3.1.1 alignment, baseline production recommendations ### Changed - **Agent refactor for Opus 4.7 literal instruction following** — `agents/skill-scanner-agent.md` and `agents/mcp-scanner-agent.md` reframe stacked CANNOT/MUST NOT imperatives in favor of tool-level enforcement via `tools:` frontmatter. New Step 0 "Generaliseringsgrense" blocks (cite evidence path:line, mark speculation as speculation) and "Parallell Read-strategi" notes (prefer parallel Read calls for independent file reads) - **Defense Philosophy linked to Opus 4.7 system card** — `CLAUDE.md` §Defense Philosophy now cites Opus 4.7 system card §5.2.1 (multi-layer defenses) and §6.3.1.1 (instruction hierarchy → tool-level enforcement) - Version bump: 6.1.0 → 6.2.0 across all files ## [6.1.0] - 2026-04-10 ### Added - **`--fail-on ` flag** — CI-friendly exit codes: exit 1 when any finding at or above the specified severity exists (critical/high/medium/low). Configurable via `policy.json` `ci.failOn` - **`--compact` output mode** — One-liner per finding format (`[SEVERITY] scanner: title (file:line)`), reduces CI log noise. Configurable via `policy.json` `ci.compact` - **CI/CD pipeline templates** — Ready-to-use templates in `ci/`: GitHub Actions (`github-action.yml`), Azure DevOps (`azure-pipelines.yml`), GitLab CI (`gitlab-ci.yml`) with SARIF upload, Node 18 setup - **CI/CD integration guide** — `docs/ci-cd-guide.md` with 5-minute setup per platform, Schrems II/NSM compliance documentation, exit code reference - **npm publish preparation** — `files` whitelist in `package.json` (only `bin/` + `scanners/`), `.npmignore` safety net, `homepage` field - **Policy `ci` section** — New `ci: { failOn, compact }` section in `.llm-security/policy.json` for distributable CI configuration ### Changed - Version bump: 6.0.0 → 6.1.0 across all files ## [6.0.0] - 2026-04-10 ### Added - **Compliance mapping** — `knowledge/compliance-mapping.md` maps plugin capabilities to EU AI Act (Art. 9, 15, 17), NIST AI RMF (Map, Measure, Manage, Govern), ISO 42001 (Annex A), and MITRE ATLAS techniques (AML.T IDs) - **Norwegian regulatory context** — `knowledge/norwegian-context.md` covers Datatilsynet (DPIA for AI), NSM (basic security principles), and Digitaliseringsdirektoratet guidance - **SARIF 2.1.0 output** — `scanners/lib/sarif-formatter.mjs` converts scan output to OASIS SARIF standard format. Use `--format sarif` with scan/deep-scan commands - **Structured audit trail** — `scanners/lib/audit-trail.mjs` writes JSONL audit events with ISO 8601 timestamps, OWASP category tags, and SIEM-ready schema. Configurable via `LLM_SECURITY_AUDIT_*` env vars - **AI-BOM generator** — `scanners/ai-bom-generator.mjs` + `scanners/lib/bom-builder.mjs` produce CycloneDX 1.6 Bills of Materials for AI components (models, MCP servers, plugins, knowledge, hooks) - **Policy-as-code** — `scanners/lib/policy-loader.mjs` reads `.llm-security/policy.json` for distributable hook configuration. Integrated into all 8 hooks. Env vars always take precedence - **Standalone CLI** — `bin/llm-security.mjs` provides `npx llm-security` entry point. Subcommands: `scan`, `deep-scan`, `posture`, `audit-bom`, `benchmark` - **Posture compliance categories** — 3 new posture categories (14: EU AI Act, 15: NIST AI RMF, 16: ISO 42001). Advisory only — do not affect Grade A threshold - **Attack simulator benchmark mode** — `--benchmark` flag outputs structured pass/fail metrics for CI integration ### Changed - Version bump: 5.1.0 → 6.0.0 across all files - Knowledge base expanded from 13 to 15 files - Scanner count: 15 → 16 (AI-BOM generator added) - Posture scanner: 13 → 16 categories - All hooks now read policy from `.llm-security/policy.json` (backward-compatible — defaults match hardcoded values) ## [5.1.0] - 2026-04-07 ### Added - **Sandboxed remote cloning** — `git clone` for remote scans is now hardened with two defense layers: 1. Git config flags: `core.hooksPath=/dev/null`, `core.symlinks=false`, `core.fsmonitor=false`, all LFS filter drivers disabled, `protocol.file.allow=never`, `transfer.fsckObjects=true`. Environment: `GIT_CONFIG_NOSYSTEM=1`, `GIT_CONFIG_GLOBAL=/dev/null`, `GIT_ATTR_NOSYSTEM=1`, `GIT_TERMINAL_PROMPT=0` 2. OS-level filesystem sandbox: macOS `sandbox-exec` and Linux `bubblewrap` (bwrap) restrict file writes to only the specific temp directory. Even if `.gitattributes` filter drivers bypass git config, they cannot write outside the clone dir. bwrap probe-tests availability before use (graceful fallback on Ubuntu 24.04+ where AppArmor blocks it). Graceful fallback on Windows (git config flags only, WARN logged) - **Post-clone size check** — Repos exceeding 100MB after clone are rejected and cleaned up - **UUID-unique evidence filenames** — `fs-utils.mjs tmppath` now generates unique filenames with `crypto.randomUUID()` suffix, preventing race conditions between concurrent scans - **Evidence file cleanup** — `scan.md` and `plugin-audit.md` now clean up evidence files (content-extract, plugin-extract) after scanning - **Cleanup guarantee** — Both `scan.md` and `plugin-audit.md` have explicit cleanup guarantee: temp dir + evidence file are removed even if scan fails or errors ### Changed - `scanners/lib/git-clone.mjs` — complete rewrite of clone command with sandbox wrapping - `scanners/lib/fs-utils.mjs` — tmppath uses `crypto.randomUUID()` for unique names ## [5.0.0] - 2026-04-06 ### Added - **Prompt Injection Hardening (v5.0)** — 8-session defense-in-depth overhaul driven by 7 research papers (2025-2026). Defense philosophy: broader detection + increased attack cost + longer monitoring windows + architectural constraints + honest documentation - **MEDIUM advisory wiring** — `pre-prompt-inject-scan.mjs` emits advisory for MEDIUM-severity obfuscation signals (leetspeak, homoglyphs, zero-width, multi-language). Never blocks. `post-mcp-verify.mjs` includes MEDIUM in injection scan advisory - **Unicode Tag steganography** — `string-utils.mjs` decodes U+E0001-E007F (invisible ASCII encoding). CRITICAL if decoded content matches injection patterns, HIGH for bare presence. Integrated into `normalizeForScan()` pipeline - **BIDI override stripping** — Removes directional override characters before injection scanning - **Bash expansion normalization** — New `bash-normalize.mjs` strips `${}`, empty quotes, backslash splits before command matching. Applied in `pre-bash-destructive.mjs` and `pre-install-supply-chain.mjs` - **Rule of Two enforcement** — `post-session-guard.mjs` gains `LLM_SECURITY_TRIFECTA_MODE=block|warn|off` (default: warn). Block mode exits with code 2 for MCP-concentrated trifecta or sensitive path + exfiltration - **100-call long-horizon monitoring** — Extended window alongside 20-call sliding window. Slow-burn trifecta detection (legs >50 calls apart = MEDIUM). Behavioral drift via Jensen-Shannon divergence on tool-class distribution - **HITL trap detection** — HIGH patterns for approval urgency, summary suppression, scope minimization. MEDIUM for cognitive load (injection buried in verbose output) - **Sub-agent delegation tracking** — `post-session-guard.mjs` tracks Task/Agent tool usage. Escalation-after-input advisory when delegation occurs within 5 calls of untrusted input (DeepMind Agent Traps kat. 4) - **Natural language indirection** — MEDIUM patterns for "fetch this URL and execute", "send this data to", "read ~/.ssh". Strict false-positive tests for benign phrasing - **Hybrid attack patterns** — P2SQL (SQL keywords in injection text), recursive injection (injection containing injection), XSS in agent context (`