chore(llm-security): release v8.0.0 - version sync and changelog
Phase 6 of the approved v8.0.0 plan. HEAD was 45 commits past v7.8.3 with every version-bearing file still reading 7.8.3 and no v8 tag anywhere. Version sync: plugin.json, package.json, README badge, CLAUDE.md header. CHANGELOG: [Unreleased] becomes [8.0.0] - 2026-09-18. It covered 3 of the user-visible commits since v7.8.3; the other 18 fix/feat/refactor commits are now recorded from their commit bodies - the commons extraction (five tables, scanners/commons/ subtree, knowledge/signatures.json removed), conformance spec 1.1, the golden-gate coverage relabel, and eight fixes (script-tag recall, whole-table ReDoS gate and its own 8-of-45 blind spot, Berry and nested lockfile-v1 misses, per-occurrence strip attribution, legacy OpenAI keys, compileRules coercion, SIG self-flag, SARIF tags). Counts verified against the loaded modules: 83 injection patterns, 19 secret shapes, 7 SIG rules. SECURITY.md supported-versions table said 7.3.x Active; now 8.0.x Active, 7.0.x - 7.8.x best-effort. README gets the 8.0.0 row; CLAUDE.md gets the v8.0.0 highlights and the test floor moves 2045+ to 2250+. Tests: 2253 / 2247 pass / 0 fail / 6 skipped, exit 0, measured on HEAD before the edits. The post-edit full run had 3 red, all timing ceilings, under load average 60 from unrelated jobs on the machine; each passes alone (460 ms of 1000, 370 ms of 2000, 2127 ms) and doc-consistency is 45/45 alone. The diff is docs and version strings only. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
342acda5bc
commit
c14501e99c
6 changed files with 114 additions and 7 deletions
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "llm-security",
|
||||
"description": "Security scanning, auditing, and threat modeling for Claude Code projects. Detects secrets, validates MCP servers, assesses security posture, and generates threat models aligned with OWASP LLM Top 10.",
|
||||
"version": "7.8.3"
|
||||
"version": "8.0.0"
|
||||
}
|
||||
|
|
|
|||
104
CHANGELOG.md
104
CHANGELOG.md
|
|
@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||
|
||||
## [Unreleased]
|
||||
|
||||
## [8.0.0] - 2026-09-18
|
||||
|
||||
Major release. The breaking part is small and was announced in v7.3.0: four
|
||||
deprecated `LLM_SECURITY_*` env-vars and the long-dead `riskScoreV1()` are
|
||||
removed. The larger part is structural and behaviour-preserving: the detection
|
||||
tables (injection lexicon, codepoint carriers, OWASP map, secret shapes, malware
|
||||
signatures) are now built from a vendored, versioned data repository under
|
||||
`scanners/commons/` instead of source literals, each swap proven content-identical
|
||||
against a golden baseline. Several recall and correctness fixes ride along.
|
||||
2253 tests, 2247 pass, 6 skipped, 0 fail.
|
||||
|
||||
### Added
|
||||
|
||||
- **`llms.txt` at the repository root.** A minimal entry point for an AI agent
|
||||
|
|
@ -25,6 +36,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||
one stated exception for security-critical removal, and an explicit note that
|
||||
this is a notice period and not an SLA.
|
||||
|
||||
- **Commons conformance, spec 1.1.** The suite runs the vendored commons
|
||||
conformance corpus through the real entry points and publishes a
|
||||
schema-shaped declaration of the result. The set of commons tables this
|
||||
runtime implements is declared from the same constant the runner uses to
|
||||
accept a scope, so the declaration cannot drift from what actually ran. Cases
|
||||
scoped to a table this runtime does not implement are reported as
|
||||
*not-applicable* — enumerated by name, counted as skipped rather than passed,
|
||||
and kept in the denominator. An empty declaration fails the suite instead of
|
||||
turning every case not-applicable and passing with zero cases run. The
|
||||
declaration artifact is generated per run and gitignored.
|
||||
|
||||
### Removed — BREAKING
|
||||
|
||||
- **The four `LLM_SECURITY_*` configuration env-vars deprecated in v7.3.0.**
|
||||
|
|
@ -80,6 +102,88 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|||
migrated project would have dropped from PASS to PARTIAL. The PARTIAL
|
||||
finding now recommends the policy key instead of the removed env-var.
|
||||
|
||||
- **Detection tables are built from vendored commons data, not source
|
||||
literals.** `scanners/commons/` is a pull-only subtree of the
|
||||
`llm-security-commons` data repository, inside the published `scanners/`
|
||||
directory so it ships with the plugin. Five tables moved, each measured
|
||||
position-by-position against the literal it replaced before the swap, with the
|
||||
golden dump diffed record-for-record afterwards: the codepoint carriers
|
||||
(zero-width, bidi, Unicode Tag range, homoglyph map), `OWASP_MAP`, the 83
|
||||
injection patterns, the 19 fixed credential shapes in `pre-edit-secrets.mjs`,
|
||||
and the 7 SIG malware signatures. Published module surfaces are unchanged —
|
||||
the tables are re-exported from where they always lived.
|
||||
`knowledge/signatures.json` is **removed** rather than kept as a second,
|
||||
ungated copy of the SIG table; operator rules via `sig.custom_rules_path` are
|
||||
unaffected. The commons location is resolved from the plugin's own directory
|
||||
only — never from a scanned target's `policy.json`, which would let a hostile
|
||||
repository substitute an empty detection corpus. Loaded artifacts are
|
||||
deep-frozen. An unresolvable injection lexicon or SIG ruleset writes one line
|
||||
to stderr naming the disabled capability instead of scanning with zero
|
||||
patterns and reporting clean; it never throws, because a module-load throw in
|
||||
a hook would break the tool call rather than degrade the scan.
|
||||
|
||||
- **Golden gate `coverage` block relabelled to what it measures.** It probes
|
||||
payload strings against patterns in-process, which is static reachability, not
|
||||
what the reference run exercised. `coverage.kind = 'static-reachability'`;
|
||||
`patternsExercised` → `patternsReachable`, `uncoveredPatterns` →
|
||||
`unreachablePatterns`, `tablesExercised` → `corpusContains`. The gate pins the
|
||||
label so it cannot be dropped quietly.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **`<script>` injection recall.** The `hybrid-xss` script-tag pattern required a
|
||||
closing `</script>`, so `<script>alert(1)` and `<script src=x.js>` passed
|
||||
`scanForInjection()` with `found: false` while the closed form returned high.
|
||||
The opening tag alone is now the signal, matched linearly. Still open and
|
||||
documented: the bounded HTML patterns evade on more than 256 characters of
|
||||
attribute padding.
|
||||
|
||||
- **Whole-table ReDoS gate, and the gate's own blind spot.** Every exported
|
||||
injection pattern is now timed, not just the six the v7.8.3 defect was found
|
||||
on. The first version timed each pattern against hand-written inputs and so
|
||||
actually reached 8 of 45 prefix-bearing patterns — the rest failed on the
|
||||
first character and reported green having measured nothing, including both
|
||||
quadratic `hybrid-xss` rows. Attack inputs are now derived from each pattern's
|
||||
own literal prefix. The two quadratic rows (`script-tag` 1429 ms, `iframe-src`
|
||||
1161 ms against a 150 ms budget) are fixed in the vendored lexicon
|
||||
(`[^>]*` → `[^><]*`); all 45 probes now run in under 20 ms.
|
||||
|
||||
- **Supply-chain re-check missed pinned compromised packages in two lockfile
|
||||
shapes.** `supply-chain-recheck.mjs` did not recognise Yarn Berry's unquoted
|
||||
`version: x` form, so a Berry lockfile yielded zero dependencies; and the
|
||||
`lockfileVersion: 1` fallback did not recurse nested `dependencies`, so a
|
||||
transitive non-hoisted compromised copy was invisible. Both now mirror the
|
||||
install hook's parsers.
|
||||
|
||||
- **`content-extractor.mjs` could pass a second encoded injection unflagged.**
|
||||
Strip attribution was tracked per label, so once any occurrence of a label had
|
||||
been line-redacted, a second cross-line-only occurrence of the same label
|
||||
reached `sanitized_content` without `unstripped: true`. Attribution is now per
|
||||
label and line.
|
||||
|
||||
- **Bare legacy OpenAI keys passed `pre-edit-secrets.mjs`.** An unquoted legacy
|
||||
key with no label assignment and no `Bearer` prefix was not blocked. A pattern
|
||||
anchored on the `T3BlbkFJ` watermark closes it without matching the
|
||||
collision-prone bare `sk-` + 48 alphanumerics form.
|
||||
|
||||
- **A malformed SIG rule became a near-universal matcher instead of being
|
||||
dropped.** `new RegExp(pattern)` does not throw on a truthy non-string — it
|
||||
coerces it — so a rule whose `pattern` was an object compiled to a character
|
||||
class over the letters of `[object Object]`. `compileRules()` now requires a
|
||||
string. Applies to the built-in ruleset and `sig.custom_rules_path` alike.
|
||||
|
||||
- **SIG flagged the plugin's own vendored detection data.** `scanners/commons`
|
||||
joins the existing `knowledge/`, `tests/`, `docs/` exclusions. Stated cost:
|
||||
the exclusion matches that two-segment path anywhere in a target, so a payload
|
||||
planted under `vendor/scanners/commons/` in a hostile repository is invisible
|
||||
to SIG identity-matching (TRG, AST, entropy and supply-chain still read it). A
|
||||
test asserts the blind spot so it cannot be rediscovered by accident.
|
||||
|
||||
- **SARIF output dropped the second OWASP mapping.** A multi-mapping `owasp`
|
||||
string such as `MCP03, MCP06` (emitted by `mcp-live-inspect.mjs` and
|
||||
`ide-extension-scanner.mjs`) was wrapped as a single tag. It is now split into
|
||||
one tag per mapping, for both rule and result properties.
|
||||
|
||||
## [7.8.3] - 2026-07-18
|
||||
|
||||
Security and correctness patch. 47 verified fixes from the v7.8.1/v7.8.2
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
# LLM Security Plugin (v7.8.3)
|
||||
# LLM Security Plugin (v8.0.0)
|
||||
|
||||
Security scanning, auditing, and threat modeling for Claude Code projects. 5 frameworks: OWASP LLM Top 10, Agentic AI Top 10 (ASI, 2026 edition), Skills Top 10 (AST), MCP Top 10, AI Agent Traps (DeepMind). 2045+ unit, integration, and end-to-end tests (`tests/e2e/` covers the multi-hook attack chain, multi-session state simulation, and the full scan-orchestrator pipeline); mutation-testing coverage not published.
|
||||
Security scanning, auditing, and threat modeling for Claude Code projects. 5 frameworks: OWASP LLM Top 10, Agentic AI Top 10 (ASI, 2026 edition), Skills Top 10 (AST), MCP Top 10, AI Agent Traps (DeepMind). 2250+ unit, integration, and end-to-end tests (`tests/e2e/` covers the multi-hook attack chain, multi-session state simulation, and the full scan-orchestrator pipeline); mutation-testing coverage not published.
|
||||
|
||||
Release notes for v7.0.0 → v7.8.2: see `docs/version-history.md` — read on demand.
|
||||
|
||||
**v8.0.0 highlights** — Breaking cleanup + commons foundation. BREAKING: the four `LLM_SECURITY_*` env-vars deprecated in v7.3.0 (`INJECTION_MODE`, `TRIFECTA_MODE`, `ESCALATION_WINDOW`, `AUDIT_LOG`, plus `DEPRECATION_QUIET`) are removed — `.llm-security/policy.json` is the only source and a removed variable is **inert** (no warning, silently back to default); `riskScoreV1()` + `SEVERITY_WEIGHTS_V1` deleted (`riskScore()` v2 untouched). Structural, behaviour-preserving: five detection tables are now built from the pull-only vendored subtree `scanners/commons/` (`llm-security-commons`) instead of source literals — codepoint carriers (`scanners/lib/codepoints.mjs`), `OWASP_MAP` (`owasp-map.mjs`), the 83 injection patterns (`injection-lexicon.mjs`), the 19 fixed secret shapes (`secret-egress.mjs`), the 7 SIG rules (`malware-signatures.mjs`; `knowledge/signatures.json` removed). Each swap was measured position-by-position first and diffed record-for-record against the golden baseline (`scripts/golden-baseline.mjs`, `tests/golden/`). The commons root is resolved from the plugin directory only, **never** from a scanned target's policy (a substitutive override would let a hostile repo empty the detection corpus). **Loud failure is contract:** an unresolvable injection lexicon or SIG ruleset writes one stderr line and never throws — an empty table would be the v7.8.2 defect class (clean report, nothing run). Commons conformance spec 1.1 implemented (`tests/lib/conformance-corpus.test.mjs`; not-applicable verdict, derived declaration, anti-narrowing floor). Fixes: `<script>` recall hole + two quadratic `hybrid-xss` rows (whole-table ReDoS gate with inputs derived from each pattern's own prefix), Yarn Berry + nested lockfile-v1 misses in `supply-chain-recheck.mjs`, per-occurrence strip attribution in `content-extractor.mjs`, bare legacy OpenAI keys in `pre-edit-secrets.mjs`, `compileRules()` non-string coercion, SIG self-flag on vendored commons, SARIF multi-OWASP tags. Known open: bounded HTML patterns evade on >256 chars of attribute padding. 2253 tests.
|
||||
|
||||
**v7.8.3 highlights** — Security/correctness patch, no feature changes. 47 verified fixes from the v7.8.1/v7.8.2 completion-review MEDIUM tier (52 findings triaged; 3 missing-detector feature-requests and 1 non-defect scoped out; the #11 persistence detector and #27 AST-taint f-string recall deferred to v8). No CRITICAL/HIGH — every review-claimed HIGH downgraded to MEDIUM on re-verification. Supply-chain gate bypasses closed (npm bare-install blocklist skip, nested-key name derivation, yarn.lock false-BLOCK + Yarn Berry miss, `pip audit`→`pip-audit`); pathguard now covers `Edit`; HTML-pattern ReDoS (28s→4ms) and an MCP-stdout memory-exhaustion DoS fixed; toxic-flow/TRG false positives and a bare-`if:` Dependabot-spoof false negative fixed; YAML block-scalar key-leak and embedded-base64→SIG decode closed; docs/counts synced (14 orchestrated scanners, 16 posture categories, 72 red-team scenarios, 2013 tests).
|
||||
|
||||
**v7.8.2 highlights** — Security patch, no feature changes. Five defects from the v7.8.1 completion review, four sharing one failure mode: **the check reported success without running**. (1) `hooks/scripts/pre-bash-destructive.mjs` did not block `rm -rf /` or `rm -rf ~` — the target alternation `(?:\/|~|\$HOME)\b` ended in a word boundary that cannot hold after `/` or `~` at end-of-command, so the bare forms the rule is named for fell through to WARN (exit 0, command executed) while `/etc` and `$HOME` blocked normally, making the rule look functional from either end. (2) `scanners/entropy-scanner.mjs` matched its test/fixture suppression against the **absolute** path, so any ancestor directory named `test`/`spec`/`fixture`/`mock` silenced every entropy finding in the target while still returning status `ok`; it now keys off the relative path. (3) `scanners/ide-extension-scanner.mjs` guarded only `parseVSCodeExtension`'s bare-`null` failure signal, not `parseIntelliJPlugin`'s truthy `{ manifest: null, warnings }`, so any malformed JetBrains plugin dereferenced `manifest.hasSignature`; the TypeError escaped `mapConcurrent`'s unguarded `Promise.all` and aborted the scan of every other installed extension. Guard widened + per-extension fault isolation. (4) `scanners/content-extractor.mjs` — the remote-scan injection boundary — detected obfuscated injections but did not strip them: a decoded-only `match[0]` never occurs in the raw text, so the literal replace was a silent no-op and the payload reached the agent verbatim via `sanitized_content` alongside a finding announcing it. Removal is now line-level; unattributable multi-line payloads carry `unstripped: true`. This boundary had no direct test coverage before v7.8.2. (5) `scanners/lib/ide-extension-parser.mjs` emptied any plugin.xml field holding a character reference above `0x10FFFF` (`Number.isFinite` bounds nothing) — filed as HIGH, actually lower, since such a document is not well-formed XML.
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ Security scanning, auditing, and threat modeling for Claude Code projects. OWASP
|
|||
|
||||
*AI-generated: all code produced by Claude Code through dialog-driven development. Every change is human-directed, reviewed, and validated before commit. Per Anthropic Consumer Terms §4, ownership of outputs is assigned to the user; this plugin is licensed MIT.*
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
|
@ -730,6 +730,7 @@ demonstrations — each with `README.md`, fixture, run script, and
|
|||
|
||||
| Version | Date | Highlights |
|
||||
|---------|------|------------|
|
||||
| **8.0.0** | 2026-09-18 | **Breaking cleanup + detection tables on vendored commons data.** BREAKING: the four `LLM_SECURITY_*` mode env-vars deprecated in v7.3.0 are removed (`.llm-security/policy.json` is the only source; a removed variable is inert) and `riskScoreV1()` is deleted — see [Migrating to v8.0.0](#migrating-to-v800). The injection lexicon (83 patterns), codepoint carriers, `OWASP_MAP`, the 19 fixed secret shapes and the 7 SIG signatures are now built from the pull-only `scanners/commons/` subtree, each swap proven content-identical against a golden baseline; an unresolvable lexicon fails loudly on stderr instead of scanning with zero patterns. Fixes: `<script>` recall hole, two quadratic `hybrid-xss` patterns (whole-table ReDoS gate added), Yarn Berry + nested lockfile-v1 misses in `supply-check`, a second-occurrence strip miss in `content-extractor.mjs`, bare legacy OpenAI keys, a malformed-SIG-rule false-positive matcher, SARIF multi-OWASP tags. Commons conformance spec 1.1 implemented. 2253 tests. |
|
||||
| **7.8.3** | 2026-07-18 | **Completion-review MEDIUM sweep — 47 verified fixes, no CRITICAL/HIGH.** 52 findings triaged (48 confirmed; 3 feature-requests + 1 non-defect scoped out; the #11 persistence detector and #27 AST-taint f-string recall deferred to v8). Supply-chain gate bypasses (npm bare-install blocklist skip, nested-key name derivation, yarn.lock false-BLOCK + Yarn Berry miss, `pip audit` no-op). Hook coverage (pathguard now `Edit|Write`; trifecta window no longer diluted by markers; pipe-to-shell interposition; bare provider-key patterns). Scanner robustness (HTML-pattern ReDoS 28s to 4ms; MCP-stdout memory exhaustion; VSIX redirect loop; scalar-policy TypeError; atomic cache writes). False positives/negatives (toxic-flow substring trifectas, TRG scoped-phrase FPs, leading-BOM HIGH, bare-`if:` Dependabot-spoof FN, reflog `reset` FP, diff duplicate-fingerprint mislabel, hex double-report). Parser divergence (YAML block-scalar key leak + indicators, ANSI-C octal/unicode, embedded-base64 to SIG, `.env.local` discovery). Docs consistency (scanner count 14, posture 16, red-team 72, SARIF version, dangling `ROADMAP.md`). Plus a live-protocol fix: `post-mcp-verify` now reads the PostToolUse `tool_response` field, so MCP-output injection scanning fires in live sessions. 2013 tests, 0 fail. |
|
||||
| **7.8.2** | 2026-07-18 | **Silent-failure fixes from the completion review (HIGH).** Five defects, four sharing one failure mode: the check reported success without running. `hooks/scripts/pre-bash-destructive.mjs` did not block `rm -rf /` or `rm -rf ~` — the target alternation ended in a `\b` that cannot hold after a non-word character, so the bare forms the rule is named for fell through to WARN (exit 0, command executed) while `/etc` and `$HOME` blocked normally. `scanners/entropy-scanner.mjs` matched its test/fixture suppression against the **absolute** path, so any ancestor directory named `test`/`spec`/`fixture`/`mock` silenced every finding in the target and still returned status `ok`. `scanners/ide-extension-scanner.mjs` guarded only `parseVSCodeExtension`'s bare-`null` failure signal, not `parseIntelliJPlugin`'s truthy `{ manifest: null }`, so any malformed JetBrains plugin threw a TypeError that escaped `mapConcurrent`'s unguarded `Promise.all` and aborted the scan of every other extension. `scanners/content-extractor.mjs` detected obfuscated injections but did not remove them: a decoded-only `match[0]` never occurs in the raw text, so the literal replace was a no-op and the payload reached the LLM agent verbatim through `sanitized_content` — removal is now line-level, with unattributable multi-line payloads flagged `unstripped`. `scanners/lib/ide-extension-parser.mjs` emptied any plugin.xml field containing a character reference above `0x10FFFF`. No feature changes. 1901 tests, 0 fail. |
|
||||
| **7.8.1** | 2026-07-18 | **Auto-cleaner command-injection fix (CRITICAL).** `scanners/auto-cleaner.mjs` syntax-checked candidate `.mjs`/`.js`/`.cjs` content via ``execSync(`node --check "${tmpPath}"`)``, where `tmpPath` derives from the untrusted scanned-repo **filename**. The v7.8.0 F-2 guard checks path containment but does not strip or quote shell metacharacters, so a file named ``x";<command>;".mjs`` closes the interpolated quote and injects a command — and `/security clean` runs live by default, making a hostile repository sufficient for arbitrary local command execution. Reproduced with a live PoC before the fix. Both subprocess sites (syntax check + the CLI scan-orchestrator fallback) now use `spawnSync` with an argv array, so no shell parses a path. Defense-in-depth: `applyFixes()` refuses findings whose `file` carries shell/control metacharacters, reported as `skipped`. Regression coverage is split across both layers so the guard cannot mask a re-introduced shell in the sink. No feature changes. 1865 tests, 0 fail. |
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ need stronger guarantees (see [`CONTRIBUTING.md`](CONTRIBUTING.md)).
|
|||
|
||||
| Version | Status |
|
||||
|---------------|------------------------------------------------|
|
||||
| 7.3.x | **Active.** Bug + security fixes. Stabilization line. |
|
||||
| 7.0.x – 7.2.x | Best-effort security fixes only. Upgrade to 7.3.x recommended. |
|
||||
| 8.0.x | **Active.** Bug + security fixes. Stabilization line. |
|
||||
| 7.0.x – 7.8.x | Best-effort security fixes only. Upgrade to 8.0.x recommended. |
|
||||
| < 7.0 | End of life. No fixes. |
|
||||
|
||||
The project is in **stabilization mode** as of 2026-05-01. New features are
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "llm-security",
|
||||
"version": "7.8.3",
|
||||
"version": "8.0.0",
|
||||
"description": "Security scanning, auditing, and threat modeling for Claude Code projects",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue