chore(release): bump to v7.3.0

Batch C release. Closes 12 implementation tasks (E3, E8-E14, 8.4, 8.6,
8.7, 8.10) across four execution waves: A (bash + decoder), B (supply
chain + workflow scanner), C (MCP cumulative drift), D (code quality).

Wave E (9 new attack-simulator scenarios for the new defenses) deferred
to v7.3.1 — defenses are unit-tested per wave; the deferred work adds
attack-simulator regression coverage on top, not the primary safety net.

Tests: 1665+ → 1777 (Wave A-D cumulative, +112).

Version sync targets touched:
- package.json
- .claude-plugin/plugin.json
- CLAUDE.md (header)
- README.md (badge + new release-history row)
- scanners/ide-extension-scanner.mjs (VERSION constant)
- ../../README.md (marketplace root plugin entry)
- CHANGELOG.md (new [7.3.0] section per Keep a Changelog, all 12 task
  IDs covered individually under Added/Changed/Documentation/Tests/Notes)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-01 05:28:45 +02:00
commit c4183b8b4d
7 changed files with 186 additions and 7 deletions

View file

@ -4,6 +4,184 @@ 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.3.0] - 2026-05-01
Batch C release. Closes 12 implementation tasks (E3, E8-E14, 8.4, 8.6,
8.7, 8.10) across four execution waves: Wave A (bash evasion + decoder),
Wave B (supply chain + workflow scanner), Wave C (MCP cumulative drift),
Wave D (code quality). Wave E (9 new attack-simulator scenarios for the
new defenses) deferred to v7.3.1 — the defenses themselves are unit-tested
per wave; the deferred work adds attack-simulator regression coverage on
top.
### Added
- **E8 — T7 process-substitution normalization** in
`scanners/lib/bash-normalize.mjs`. Collapses `<(cmd)` and `>(cmd)`
process-substitution wrappers so the inner command name is surfaced
to downstream destructive-command name matchers in
`pre-bash-destructive.mjs`. Defends against split-command evasion.
Nested wrappers handled up to depth 3. Single-quoted literals
masked before T7 runs to avoid corrupting string content.
- **E10 — T9 eval-via-variable normalization** in
`scanners/lib/bash-normalize.mjs`. Substitutes one-level variable
assignments before destructive-name matching. One-level forward-flow
only: chained-var attacks intentionally not followed (documented
limit). Bare-form, curly-form, and double-quoted forms supported;
single-quoted literals preserved.
- **E9 — T8 base64-pipe-shell BLOCK rule** in
`hooks/scripts/pre-bash-destructive.mjs`. Direct match on the
base64-decode-pipe-into-shell loader idiom — blocks the
encoded-payload runner pattern that bypasses static name-matching by
delivering the destructive command as encoded text.
- **E3 — rot13 layer for hidden-imperative comment-block detection**
in `scanners/lib/injection-patterns.mjs`. The decoder is bounded
in length to keep accidental rot13-look-alike short strings out of
scope. Base64/hex/URL/HTML decoding is already done by
`normalizeForScan`; the rot13 pass is the only genuinely new layer.
- **E12 — `.gitattributes` filter/diff/merge driver advisory** in
`scanners/lib/git-clone.mjs`. New `scanGitAttributes(repoDir)`
exported helper plus post-clone integration in the `clone` CLI
branch — surfaces filter, diff, and merge driver directives as
MEDIUM advisories so downstream consumers see the supply-chain
surface that survives even a sandboxed clone.
- **E13 — npm scope-hopping typosquat detection** in
`hooks/scripts/pre-install-supply-chain.mjs`. New shared
`NPM_OFFICIAL_SCOPES` export from `scanners/lib/supply-chain-data.mjs`.
When an install targets `@<scope>/<name>` where `<scope>` is unknown
but `<name>` matches a popular unscoped package, the hook emits a
MEDIUM advisory. Allowlist of legitimate scopes drives suppression.
Configurable via `policy.json` `supply_chain.allowed_scopes`.
- **E11 — workflow-injection scanner** (`scanners/workflow-scanner.mjs`).
Scans `.github/workflows/*.{yml,yaml}` and `.forgejo/workflows/*.{yml,yaml}`
for dangerous expression interpolations inside `run:` step blocks.
23-field canonical blacklist (GHSL Security Lab 17 + GlueStack-class
6) targeting attacker-controlled fields. Sink-restricted: only
`run:` steps are shell sinks; `if:`, `with:`, `env:`, `name:`,
`runs-on:` are evaluated by the runner's expression engine, not the
shell, and are suppressed. Severity matrix: privileged triggers →
HIGH; semi-privileged → MEDIUM; safe fields (numeric / hex /
fixed-string) → INFO. State machine extracted to
`scanners/lib/workflow-yaml-state.mjs` for unit-level testability.
Re-interpolation tracking — env-block bindings sourced from
blacklisted fields, then read back inside `run:`, are flagged at
MEDIUM as the Appsmith GHSL-2024-277 stealth pattern. Auth-bypass
detection — `(github|forgejo).actor` compared against bot
identities in `if:` conditions flagged at MEDIUM (Synacktiv 2023
Dependabot spoofing class). New `WFL` prefix in
`scanners/lib/severity.mjs` OWASP map. Registered in
`scanners/scan-orchestrator.mjs`.
- **E14 — MCP cumulative-drift baseline** in
`scanners/lib/mcp-description-cache.mjs`. Sticky `baseline` slot per
tool plus a 10-event rolling `history` array (FIFO). Cumulative
drift = `levenshtein(current, baseline.description) / max(|current|,
|baseline|)`; when ratio ≥ `mcp.cumulative_drift_threshold`
(default 0.25), `post-mcp-verify.mjs` emits a MEDIUM
`mcp-cumulative-drift` advisory independent of the existing
per-update >10% drift signal — 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. New `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`. Migration logic
in `loadCache()` seeds `baseline` from existing entries on first
read post-upgrade.
- **8.7 — env-var deprecation warnings** in
`scanners/lib/policy-loader.mjs`. New `getPolicyValueWithEnvWarn(section,
key, envVarName, defaultValue)` helper. Env-var still wins per
existing Preferences, but when BOTH the env-var AND the
`policy.json` key are explicitly set, the helper emits a single
per-process stderr deprecation line pointing to v8.0.0 removal.
Module-scoped `Set` dedupes per env-var name across call-sites.
`DEFAULT_POLICY` gains `trifecta.escalation_window: 5` (closes the
gap where `LLM_SECURITY_ESCALATION_WINDOW` had no `policy.json`
equivalent). Wired through 4 hook call-sites:
`pre-prompt-inject-scan`, `post-session-guard` (×2), and
`audit-trail`. Env-only vars without `policy.json` equivalents are
unchanged.
### Changed
- **8.10 — CLAUDE.md hooks count corrected** from `## Hooks (8)` to
`## Hooks (9)`. Adds `pre-compact-scan.mjs` row to the hooks table
(PreCompact — transcript scan before context compaction). The hook
itself shipped in v6.2.0 but the count and table row drifted. New
`Hooks count consistency` `describe` block in
`tests/lib/doc-consistency.test.mjs` parses `hooks/hooks.json`,
reads the CLAUDE.md `## Hooks (\d+)` header and the table rows,
and asserts all three counts agree — locks in the fix and prevents
future drift.
### Documentation
- **8.4 — `riskScoreV1` annotated `@deprecated`** in
`scanners/lib/severity.mjs`. JSDoc explicitly tags v7.0.0 as the
introduction of the v2 model and v8.0.0 as the removal target for
v1, so library consumers see the deprecation in IDE tooling and
not just in release notes. The function remains exported and
functional for users who relied on it.
- **8.6 — sandbox-architecture rationale** in
`docs/security-hardening-guide.md` §7. Documents why
`lib/git-clone.mjs` and `lib/vsix-sandbox.mjs` remain separate
rather than being collapsed into a single shared sandbox helper.
Brief `Preferences` explicitly rejected the consolidation as
premature abstraction over safety-critical code; the rationale is
recorded so future maintainers see the deliberate decision.
### Tests
- 1665+ → 1777 (Wave A-D cumulative; ~+112 tests). Includes new
files (`tests/scanners/bash-normalize-t7-t9.test.mjs`,
`tests/lib/git-clone-gitattributes.test.mjs`,
`tests/scanners/workflow-scanner.test.mjs`,
`tests/lib/workflow-yaml-state.test.mjs`,
`tests/scanners/mcp-baseline-reset.test.mjs`) plus extensions to
`tests/lib/injection-patterns.test.mjs`,
`tests/hooks/pre-bash-destructive.test.mjs`,
`tests/hooks/pre-install-supply-chain.test.mjs`,
`tests/scanners/scan-orchestrator.test.mjs`,
`tests/lib/mcp-description-cache.test.mjs`,
`tests/hooks/post-mcp-verify.test.mjs`,
`tests/lib/severity.test.mjs`,
`tests/lib/policy-loader.test.mjs`,
`tests/lib/doc-consistency.test.mjs`. One pre-existing
size-cap timing flake at `tests/hooks/pre-compact-scan.test.mjs`
passes in isolation, fails sporadically under full-suite load —
unchanged across Wave A-D, not a Batch C blocker.
### Notes
- **Wave E deferred (red-team coverage).** The plan called for 9 new
attack-simulator scenarios covering every Wave A-D defense. The
work was deferred from v7.3.0 because two of the scenarios test
scanners (workflow-scanner, git-clone `scanGitAttributes`) that
don't fit the existing hook-spawn model used by attack-simulator
and would have required a new `scanner_test` execution mode.
Tracked for v7.3.1. Defenses are unit-tested per wave; this is
regression coverage on top of unit coverage, not the primary
safety net.
- **Hooks runtime behavior unchanged for existing setups.** Every
Wave A-D addition is either purely additive (new advisories at
MEDIUM) or layered before existing detection (T7/T9 normalize
before existing destructive-name matching; rot13 inside the
existing decoder loop; cumulative-drift independent of per-update
drift). Users who set neither the new `policy.json` keys nor the
new env-vars see identical behavior.
## [7.2.0] - 2026-04-29
Batch B release. Closes the remaining critical-review B-tier scanner