chore(release): v8.1.0 — antivirus surface
Version sync to 8.1.0: package.json, .claude-plugin/plugin.json, README badge + Recent versions row + self-scan counts, CLAUDE.md header + highlights (and the GitHub-only remote-scan wording git-clone no longer has), CHANGELOG [8.1.0] covering S0-S3c. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6d0f3c31fc
commit
7c5514e5dd
5 changed files with 106 additions and 7 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"name": "llm-security",
|
"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.",
|
"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": "8.0.0"
|
"version": "8.1.0"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
96
CHANGELOG.md
96
CHANGELOG.md
|
|
@ -6,6 +6,102 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [8.1.0] - 2026-09-22
|
||||||
|
|
||||||
|
Antivirus surface. A Windows user should be able to clone the repository and
|
||||||
|
install the plugin without Defender or a similar product quarantining a file,
|
||||||
|
and the plugin should still find what it finds today. No payload now sits on
|
||||||
|
disk as a file that would run it: test fixtures and the malicious-plugin demo
|
||||||
|
are stored encoded or built from fragments, and exist only in a temp directory
|
||||||
|
while a test or the demo runs. Two scope fixes ride along: a scanned repository
|
||||||
|
no longer controls its own suppression or detection thresholds. Detection
|
||||||
|
tables untouched; golden baseline identical. 2286 tests, 2280 pass, 6 skipped,
|
||||||
|
0 fail.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **`tests/av-surface.test.mjs`** — a gate that fails the suite if a payload
|
||||||
|
comes back to disk. Five probes over `git ls-files`: (a) files that match
|
||||||
|
the webshell / reverse-shell / cryptominer signatures in a file type that
|
||||||
|
would run them, plus (a2) the same families as literals in test sources;
|
||||||
|
(b) base64 blobs that decode to a shell command; (c) Unicode Tag and
|
||||||
|
zero-width characters outside the conformance corpus; (d) whole malicious
|
||||||
|
fixture trees on disk; (e) runnable payload literals in the files Claude
|
||||||
|
Code loads every session (agents, commands, skills, hooks, knowledge).
|
||||||
|
|
||||||
|
- **README § Antivirus false positives** — what the repository contains and
|
||||||
|
why, how to restore a quarantined file, where to report a false positive to
|
||||||
|
Microsoft, and a sparse-checkout recipe for users who only want the scanners
|
||||||
|
and hooks. No exclusion is recommended.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **Test payloads are built at test time, never stored.** The poisoned
|
||||||
|
fixtures for the signature, memory-poisoning and trigger scanners are
|
||||||
|
written into a temp directory from split strings or rot13; zero-width test
|
||||||
|
lines use `String.fromCodePoint`. `tests/fixtures/signature-scan/poisoned/`
|
||||||
|
is gone. The sha256 of every removed file is asserted against what the
|
||||||
|
builders produce, so the content is provably unchanged.
|
||||||
|
|
||||||
|
- **The malicious-skill demo is stored encoded.** `examples/malicious-skill-demo/`
|
||||||
|
and `examples/poisoned-claude-md/` ship as encoded archives and are written
|
||||||
|
to a temp directory when run (`materialize.mjs` prints the demo's path).
|
||||||
|
The README and the demo's security assessment describe the base64 payloads
|
||||||
|
instead of showing decodable lines.
|
||||||
|
|
||||||
|
- **`tests/fixtures/ast-scan/*.py` are materialized at test time** (all five).
|
||||||
|
|
||||||
|
- **Skill-scanner detection lists moved** from `agents/skill-scanner-agent.md`
|
||||||
|
to `knowledge/skill-threat-patterns.md` § Detection Rules, a file the four
|
||||||
|
scanning commands already hand the agent. A quarantined agent file would
|
||||||
|
break the plugin for the user; a knowledge file would not. Runnable
|
||||||
|
one-liners in `knowledge/*.md` are written as descriptions where that costs
|
||||||
|
no precision.
|
||||||
|
|
||||||
|
- **Two hook rule names split** so no download-into-shell literal sits on disk
|
||||||
|
in `hooks/scripts/pre-bash-destructive.mjs`. Hook stderr is byte-identical.
|
||||||
|
|
||||||
|
- **`.llm-security-ignore` now covers the MEM, SIG, AST, TRG and UNI
|
||||||
|
prefixes**, and the README's self-scan numbers are measured, not claimed:
|
||||||
|
a fresh clone scans WARNING 61/100 with 58 findings (the earlier "0 findings
|
||||||
|
(ALLOW)" was false).
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **`.llm-security-ignore` is honoured only for the caller's own working
|
||||||
|
tree.** A scanned repository with `**` in its own ignore file turned
|
||||||
|
WARNING (1 finding) into ALLOW (0). The file is now read only when the
|
||||||
|
target is the current working directory or below it, and never when the
|
||||||
|
target is under the system temp directory (where remote clones land).
|
||||||
|
Otherwise it is ignored with one stderr line.
|
||||||
|
|
||||||
|
- **`.llm-security/policy.json` — including `sig.custom_rules_path`,
|
||||||
|
`ast.python_path` and `ci.failOn` — is honoured only for the caller's own
|
||||||
|
working tree**, by the same rule (`scanners/lib/own-working-tree.mjs`). A
|
||||||
|
foreign target could raise its own entropy thresholds out of reach or add
|
||||||
|
signature rules. Entropy's `calibration.policy_source` now says `defaults`
|
||||||
|
when the file was present but ignored.
|
||||||
|
|
||||||
|
**Behaviour change:** running a scanner with a target outside the current
|
||||||
|
working directory no longer applies that target's `policy.json`. Hooks,
|
||||||
|
and CI that runs `scan .` from the repository root, are unaffected.
|
||||||
|
|
||||||
|
- **`scanners/lib/git-clone.mjs` accepts HTTPS repository URLs on any host**,
|
||||||
|
not only GitHub, so the README's Forgejo example works. SSH URLs remain
|
||||||
|
GitHub-only.
|
||||||
|
|
||||||
|
### Known open
|
||||||
|
|
||||||
|
- "Own working tree" means the target is the working directory or below it.
|
||||||
|
A foreign clone that sits under the user's working directory (for example
|
||||||
|
with the working directory at `$HOME`, or a cloned vendor folder inside a
|
||||||
|
project) still has its `policy.json`, ignore file and custom SIG rules read.
|
||||||
|
Clones under the temp directory and targets outside the working directory
|
||||||
|
are covered.
|
||||||
|
- The README's self-scan suppression count drifts by one per commit:
|
||||||
|
`git-forensics` looks at the last 50 commits, and each new commit pushes one
|
||||||
|
older finding out of that window.
|
||||||
|
|
||||||
## [8.0.0] - 2026-09-18
|
## [8.0.0] - 2026-09-18
|
||||||
|
|
||||||
Major release. The breaking part is small and was announced in v7.3.0: four
|
Major release. The breaking part is small and was announced in v7.3.0: four
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,11 @@
|
||||||
# LLM Security Plugin (v8.0.0)
|
# LLM Security Plugin (v8.1.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). 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.
|
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.
|
Release notes for v7.0.0 → v7.8.2: see `docs/version-history.md` — read on demand.
|
||||||
|
|
||||||
|
**v8.1.0 highlights** — Antivirus surface: a Windows user can clone and install without Defender quarantining a file, and detection is unchanged. No payload sits on disk as a file that would run it — test fixtures (signature, memory-poisoning, trigger, `ast-scan/*.py`) are built in a temp dir at test time from split strings/rot13, the malicious-skill demo and `poisoned-claude-md` ship as encoded archives (`materialize.mjs`), zero-width test lines use `String.fromCodePoint`; sha256 of every removed file asserted. Gate: `tests/av-surface.test.mjs`, probes (a)/(a2) SIG payloads, (b) base64→shell command, (c) Tag/zero-width outside the conformance corpus, (d) malicious trees on disk, (e) runnable literals in session-loaded files. Skill-scanner detection lists moved from `agents/skill-scanner-agent.md` to `knowledge/skill-threat-patterns.md` § Detection Rules (a quarantined agent file breaks the plugin). README § Antivirus false positives (restore, report, sparse checkout; no exclusions recommended). Scope fixes: `.llm-security-ignore` and `.llm-security/policy.json` (incl. `sig.custom_rules_path`, `ast.python_path`, `ci.failOn`) are honoured only for the caller's own working tree (`scanners/lib/own-working-tree.mjs`: target = cwd or below, never under tmpdir) — a foreign target's file is ignored with one stderr line. `git-clone.mjs` accepts HTTPS on any host. Signature tables and golden baseline untouched. Known open: a foreign clone *under* cwd still counts as own tree. 2286 tests.
|
||||||
|
|
||||||
**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.
|
**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.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).
|
||||||
|
|
@ -25,7 +27,7 @@ Release notes for v7.0.0 → v7.8.2: see `docs/version-history.md` — read on d
|
||||||
| Command | Description |
|
| Command | Description |
|
||||||
|---------|-------------|
|
|---------|-------------|
|
||||||
| `/security` | Router — lists sub-commands |
|
| `/security` | Router — lists sub-commands |
|
||||||
| `/security scan [path\|url]` | Scan skills/MCP/directories/GitHub repos (+ `--deep` for deterministic scanners) |
|
| `/security scan [path\|url]` | Scan skills/MCP/directories/remote git repos (+ `--deep` for deterministic scanners) |
|
||||||
| `/security deep-scan [path]` | 14 deterministic Node.js scanners (incl. supply chain, memory poisoning, toxic flow + trigger/signature/AST-taint) |
|
| `/security deep-scan [path]` | 14 deterministic Node.js scanners (incl. supply chain, memory poisoning, toxic flow + trigger/signature/AST-taint) |
|
||||||
| `/security audit` | Full project audit, A-F grading |
|
| `/security audit` | Full project audit, A-F grading |
|
||||||
| `/security plugin-audit [path\|url]` | Plugin trust assessment (local or GitHub URL) |
|
| `/security plugin-audit [path\|url]` | Plugin trust assessment (local or GitHub URL) |
|
||||||
|
|
@ -78,7 +80,7 @@ Defense philosophy (v5.0), Opus 4.7 alignment, known limitations: see `docs/defe
|
||||||
|
|
||||||
## Remote Repo Support
|
## Remote Repo Support
|
||||||
|
|
||||||
`scan` and `plugin-audit` accept GitHub URLs directly. The command clones to a temp dir via `scanners/lib/git-clone.mjs`, scans locally, then cleans up. Use `--branch <name>` for non-default branches.
|
`scan` and `plugin-audit` accept remote git URLs directly (HTTPS on any host, SSH on GitHub only). The command clones to a temp dir via `scanners/lib/git-clone.mjs`, scans locally, then cleans up. Use `--branch <name>` for non-default branches.
|
||||||
|
|
||||||
**Clone sandboxing (v5.1):** Two layers of defense against `git clone` filter/smudge driver attacks:
|
**Clone sandboxing (v5.1):** Two layers of defense against `git clone` filter/smudge driver attacks:
|
||||||
1. **Git config flags (all platforms):** `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`.
|
1. **Git config flags (all platforms):** `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`.
|
||||||
|
|
|
||||||
|
|
@ -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.*
|
*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.*
|
||||||
|
|
||||||

|

|
||||||

|

|
||||||

|

|
||||||

|

|
||||||
|
|
@ -661,7 +661,7 @@ you start:
|
||||||
npm test # node --test 'tests/**/*.test.mjs'
|
npm test # node --test 'tests/**/*.test.mjs'
|
||||||
```
|
```
|
||||||
|
|
||||||
Running `node scanners/scan-orchestrator.mjs .` on a fresh clone produces **WARNING 61/100 with 58 findings** (0 critical, 5 high, 15 medium, 2 low, 36 info; measured 2026-09-22), after `.llm-security-ignore` suppresses 383. Without the ignore file the same run reports BLOCK 95/100 with 441 findings. A security plugin that documents attack patterns, ships a malicious demo, and tests against deliberately evil code triggers its own scanners; every suppression is explained in the file. What is left is reported on purpose: high-entropy strings in docs and the HTML report renderer, example and research domains in docs, three CLI tools that write to a path given on the command line, and memory findings on `CLAUDE.md` — it documents the hooks, but it is the file a poisoning would target, so it is not suppressed. Remove the ignore file and re-run to see the unsuppressed picture.
|
Running `node scanners/scan-orchestrator.mjs .` on a fresh clone produces **WARNING 61/100 with 58 findings** (0 critical, 5 high, 15 medium, 2 low, 36 info; measured 2026-09-22), after `.llm-security-ignore` suppresses 380. Without the ignore file the same run reports BLOCK 95/100 with 438 findings. A security plugin that documents attack patterns, ships a malicious demo, and tests against deliberately evil code triggers its own scanners; every suppression is explained in the file. What is left is reported on purpose: high-entropy strings in docs and the HTML report renderer, example and research domains in docs, three CLI tools that write to a path given on the command line, and memory findings on `CLAUDE.md` — it documents the hooks, but it is the file a poisoning would target, so it is not suppressed. Remove the ignore file and re-run to see the unsuppressed picture.
|
||||||
|
|
||||||
The `examples/malicious-skill-demo/` directory contains a deliberately malicious "Project Health Dashboard" plugin and a [full security assessment](examples/malicious-skill-demo/security-assessment.md). The combined LLM + deterministic pipeline produced **85 findings** (24 critical, 24 high, 20 medium, 6 low, 11 info) and verdict **BLOCK 100/100** — both layers independently maxed the risk score. A human reviewing the plugin's `README.md` and `SKILL.md` would likely miss most of them; the Unicode Tag steganography is literally invisible.
|
The `examples/malicious-skill-demo/` directory contains a deliberately malicious "Project Health Dashboard" plugin and a [full security assessment](examples/malicious-skill-demo/security-assessment.md). The combined LLM + deterministic pipeline produced **85 findings** (24 critical, 24 high, 20 medium, 6 low, 11 info) and verdict **BLOCK 100/100** — both layers independently maxed the risk score. A human reviewing the plugin's `README.md` and `SKILL.md` would likely miss most of them; the Unicode Tag steganography is literally invisible.
|
||||||
|
|
||||||
|
|
@ -779,6 +779,7 @@ line if you run those. A sparse checkout of your own clone does not change what
|
||||||
|
|
||||||
| Version | Date | Highlights |
|
| Version | Date | Highlights |
|
||||||
|---------|------|------------|
|
|---------|------|------------|
|
||||||
|
| **8.1.0** | 2026-09-22 | **Antivirus surface.** Clone and install on Windows without Defender quarantining a file, with detection unchanged. No payload sits on disk as a file that would run it: test fixtures are built in a temp directory at test time, the malicious-skill demo ships encoded and is materialized on demand, and `tests/av-surface.test.mjs` fails the suite if a payload comes back. Skill-scanner detection lists moved from the agent file to `knowledge/`. New section [Antivirus false positives](#antivirus-false-positives). Scope fixes: a scanned repository's own `.llm-security-ignore` and `.llm-security/policy.json` are honoured only when it is your own working tree, so a foreign clone cannot suppress its findings or raise its thresholds. Remote scans accept HTTPS URLs on any host. Signature tables and golden baseline untouched. 2286 tests. |
|
||||||
| **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. |
|
| **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.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.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. |
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "llm-security",
|
"name": "llm-security",
|
||||||
"version": "8.0.0",
|
"version": "8.1.0",
|
||||||
"description": "Security scanning, auditing, and threat modeling for Claude Code projects",
|
"description": "Security scanning, auditing, and threat modeling for Claude Code projects",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue