llm-security/docs/version-history.md
Kjell Tore Guttormsen 55d01d1656 chore(llm-security): v7.8.1 — security patch release
Version bump + release notes for the auto-cleaner command-injection fix
(f083cda). Security patch only; no feature changes. 1865 tests, 0 fail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TcQyMTQfyrsAapaCMPxTtQ
2026-07-18 09:00:18 +02:00

22 KiB
Raw Blame History

LLM Security — Version history

Per-release notes for v7.0.0 onward. Imported from CLAUDE.md via @docs/version-history.md.

v7.8.1 — Auto-cleaner command-injection fix (CRITICAL)

Security patch for a CRITICAL defect introduced with the v7.8.0 auto-cleaner hardening pass. No feature changes; full suite green at 1865/0.

scanners/auto-cleaner.mjs validated candidate .mjs/.js/.cjs content by shelling out to node --check "${tmpPath}" via execSync. tmpPath is derived from the finding's file field — an untrusted scanned-repo filename. The F-2 containment guard that landed in v7.8.0 verifies the resolved path stays inside the scanned tree, but performs no shell-metacharacter handling, so a filename whose " terminates the interpolated quote injects a second command. Because /security clean runs in live mode by default, scanning a hostile repository was sufficient to execute attacker-chosen commands locally. The defect was reproduced with a live proof-of-concept before being fixed.

Both subprocess sites now use spawnSync with an argv array — the syntax check and the CLI's inline scan-orchestrator fallback — so no shell interprets a path. As defense-in-depth, applyFixes() additionally refuses any finding whose file carries shell or control metacharacters, surfacing it as skipped.

Regression coverage is deliberately split across both layers (tests/scanners/auto-cleaner-rce.test.mjs): one test drives validateContent directly to prove the shell is gone, because the metacharacter guard would otherwise stop the hostile input before it reached the sink and the test would pass without testing the fix.

v7.8.0 — Trigger, signature, and AST-taint scanners

Three new deterministic deep-scan scanners (TRG/SIG/AST), each with its own finding prefix, OWASP/AST mapping, policy block, fixtures, and graceful-skip behaviour. They target the skills/agents activation and code surface that the permission and shape-based scanners do not cover. Built behind a security-fix gate — the F-1/F-2/F-3 shell-injection and path-traversal fixes landed first. No existing scanner, hook, or command behaviour changes; full suite green (1863 tests, 0 fail).

  • TRG — trigger/activation-abuse (scanners/trigger-scanner.mjs). Inspects command/agent/skill name + description frontmatter for three activation-surface abuses: TRG-shadow (name collides with a built-in command/tool and intercepts it), TRG-baiting (description uses maximally-activating phrases — "anything", "always", "all files" — to bait indiscriminate invocation), and TRG-broad (a generic name plus a universal-applicability claim, so the unit auto-activates beyond its stated purpose). Skills/agents auto-activate on their description, so this is a skill-native surface not covered by the permission or taint scanners. Descriptions pass through the decode pipeline (zero-width strip → homoglyph fold → normalizeForScan) first, so obfuscated baiting still trips. Thresholds and the phrase/built-in lists are overridable via .llm-security/policy.json (trg). OWASP LLM06 (excessive agency); AST04.

  • SIG — known-bad-identity signatures (scanners/signature-scanner.mjs). A pure-Node signature engine for known-malware identity — webshells, reverse shells, cryptominers, hacktools — complementary to the shape-based entropy/taint scanners. Unlike a raw byte-matcher (e.g. YARA), every signature is tested against both the raw bytes and the project decode pipeline (base64/hex/url/entity/unicode decode, homoglyph fold, rot13), so obfuscated known-malware a byte-matcher misses is still caught. Rules ship in knowledge/signatures.json; families are policy-selectable. OWASP LLM03 (supply chain) primary; LLM02.

  • AST — Python taint analysis (scanners/ast-taint-scanner.mjs). Shells out to a PARSE-ONLY python3 helper (scanners/lib/py-ast-taint.py) for variable-level, scope-aware taint analysis of Python skill code — higher recall/precision than the ~70%-recall regex taint-tracer.mjs. The helper only ast.parses the target and never executes it, so analysing hostile code is side-effect-free. Falls back to the regex tracer whenever python3 is unavailable, so the scan never hard-fails. OWASP LLM01, LLM02; AST02.

Packaging/wiring: TRG/SIG/AST registered as valid finding prefixes (scanners/lib/output.mjs); all three wired into the scan orchestrator and policy loader; knowledge/ added to the package.json files whitelist.

v7.7.2 — Language consistency pass

Norwegian had crept into surface text across v7.5v7.7. Per the ~/.claude/CLAUDE.md convention (English for code and documentation, Norwegian for dialog only), this release translates: the HTML Report-step in all 18 skill commands, the canonical CLI renderer scripts/lib/report-renderers.mjs (display strings + JS comments), the playground UI strings, the skill-scanner-agent and mcp-scanner-agent system prompts, the playground architecture prose + Recent versions table in the plugin README.md, the v7.7.x highlights in the plugin CLAUDE.md, the llm-security entries in the marketplace root README.md + CLAUDE.md, and six table cells in docs/scanner-reference.md. Demo-state fixture content for the dft-komplett-demo project (intentional Norwegian persona) and regex alternations that match Norwegian-language report markdown (/^high|^høy/, /resolution|løsning/) were preserved. CHANGELOG and this version-history file were deferred per operator decision — they remain in the language they were written in. No scanner, hook, or behavior changes — purely surface text.

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 entropysection (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_MODEinjection.mode (in pre-prompt-inject-scan.mjs), LLM_SECURITY_TRIFECTA_MODEtrifecta.mode and LLM_SECURITY_ESCALATION_WINDOWtrifecta.escalation_window (in post-session-guard.mjs), LLM_SECURITY_AUDIT_LOGaudit.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).

v7.7.0 — HTML-rapport for alle 18 skill-kommandoer

Alle 18 /security-kommandoer som produserer rapport får nå en klikkbar file://-lenke til en self-contained HTML-versjon. Levert over 5 sesjoner (UX-arbeid + extract + CLI + wiring). Ingen scanner- eller hook-atferdsendringer — purely additive.

Sesjon 1 (0dc7ff4) — Playground katalog list-view + builder-pane. Katalog-overflaten fikk list-view (grid-toggle) + builder-pane med copy-knapp på alle 18 rapporter, så onboarding-flytene blir bredere/dypere uten å forlate playground-modusen.

Sesjon 2 (86d6ecd) — Playground prosjekt-surface opprydding. Stub-screen-håndtering (rapport ikke ferdig parsed → tydelig placeholder i stedet for tom panel), topbar-splitt (navigasjons-trinn vs. eksport-handlinger), generell DS-justering for projekt-overflate.

Sesjon 3 (fa5fb48) — scripts/lib/report-renderers.mjs extract. De 18 inline parserne + 18 inline rendererne i playground-HTML-fila flyttet til canonical ESM-modul (scripts/lib/report-renderers.mjs) med ren import { PARSERS, RENDERERS } from './...'-overflate. Playground beholder en inline-kopi (bit-identisk) fordi ESM import ikke fungerer fra file:// uten Chrome/Firefox-flags. Canonical kilden + playground inline = to overflater, samme atferd.

Sesjon 4 (db80854) — scripts/render-report.mjs CLI + 4 skills wired. Ny zero-dep Node-CLI tar commandId + --in/--out (stdin/file/stdout-modus), bruker kebab→camel-konvertering så alle 18 commandIds fungerer automatisk. Output er self-contained HTML (~140 KB): inlines 6 DS-stylesheets (tokens, base, components, tier2, tier3, tier3-supplement) + lokal .report-table-implementasjon. Fonter ikke inlined (ville blåst opp HTML 7x til ~1 MB) — tokens.css har -apple-system, BlinkMacSystemFont, system-ui som fallback. Absolutte file://-paths i stdout for Ghostty cmd-click. Default output reports/<command>-<YYYYMMDD-HHmmss>.html relativt til CWD. 4 skills wired: scan, audit, posture, deep-scan.

Sesjon 5 — 14 resterende skills wired + release. plugin-audit, mcp-audit, mcp-inspect, ide-scan, supply-check, dashboard, pre-deploy, diff, watch, registry, clean, harden, threat-model, red-team — alle har nå en avsluttende "HTML Report"-step i sin skill-fil som instruerer Claude å (1) compute temp md-path, (2) Write hele markdown-rapporten verbatim, (3) kjøre CLI, (4) appende > **HTML-rapport:** [Åpne i nettleser](file:///abs/sti.html) til respons. v7.7.0 release (versjonsbump på tvers av package.json, .claude-plugin/plugin.json, README badge + state, CLAUDE.md header + state-seksjon, marketplace-rot-README).

Pre-existing pre-compact-scan-perf-flake (1000ms terskel under last) gjenstår — defer til v7.7.x patch.

v7.7.1 — Playground UX-strip (no scanner/hook behavior changes)

Operatør-feedback umiddelbart etter v7.7.0-release: hjem-overflaten viste fortsatt tre prosjekt-tracks (Re-onboard / Nytt prosjekt / Command-katalog) selv om katalog-funksjonen var det operatøren ønsket å fremheve. Minimum- strip levert som tre atomic commits (b732eee + 2a6f73f + 81b7beb):

(1) renderActive()-router tvinger alltid activeSurface til 'catalog'. Onboarding/home/project-render-funksjonene + state-strukturen er bevart i kildekoden, men ikke rutbare før funksjonalitet legges til igjen. Init-default endret fra 'home' til 'catalog'. Konsekvens: playgrounden lander direkte i Command-katalog (alle 20 kommandoer i list-view med builder-pane + copy-knapp fra sesjon 1).

(2) Topbar Hjem og Re-onboard-knapper fjernet fra primær navigasjon. Katalog-knappen + Eksporter/Importer/tema-toggle beholdt. Project-state forblir i IndexedDB men ingen UI-vei dit.

(3) Topbar breadcrumb erstattet shared.organization.name (demo-state- orgnavn) med statisk llm-security som nøytralt scope-anker. Crumb- parameter (f.eks. Katalog) beholdt som suffix.

Fix: hardkodet versjons-streng 'Plugin v7.6.1' på linje 6933 i renderHome (template-string-litteral som v7.7.0-grep-en ikke fanget) synket til v7.7.1.

Versjonsbump i 9 filer (package.json, .claude-plugin/plugin.json, plugin README.md badge + Recent versions-tabell, plugin CLAUDE.md header + state-seksjon, docs/version-history.md, playground/llm-security-playground.html, rot README.md plugin-entry, rot CLAUDE.md plugin-katalog, CHANGELOG.md [7.7.1]-seksjon). Onboarding-konseptet dokumentert som v7.8.0-kandidat (per-kommando kontekst-injeksjon) i ROADMAP.md.