chore(llm-security): v7.8.2 — security patch release
Bumps package.json, .claude-plugin/plugin.json and the README badge to 7.8.2; adds the release entry to CHANGELOG.md, docs/version-history.md, the README recent-versions table and the CLAUDE.md highlights block. Also fixes test pollution introduced with the ide-extension regression suite: its temp roots used an `llmsec-jb-plugin-` prefix, and jetbrains-parser.test.mjs asserts globally that no `llmsec-jb-*` directory survives anywhere in tmpdir. The shared prefix made that assertion fail depending on test order — it passed on the first full run and failed on the next. Prefix is now `llmsec-nullmanifest-`. npm test: 1901/1901 green, three consecutive runs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TcQyMTQfyrsAapaCMPxTtQ
This commit is contained in:
parent
566c281b56
commit
f4c65070ff
7 changed files with 146 additions and 6 deletions
|
|
@ -2,6 +2,73 @@
|
|||
|
||||
Per-release notes for v7.0.0 onward. Imported from `CLAUDE.md` via `@docs/version-history.md`.
|
||||
|
||||
## v7.8.2 — Silent-failure fixes from the completion review (HIGH)
|
||||
|
||||
Security patch covering five defects found in the v7.8.1 completion review. No
|
||||
feature changes; full suite green at 1901/0 (1865 + 36 new regression tests).
|
||||
|
||||
The common thread in four of the five is **silent failure**: the scanner or
|
||||
hook reported success while the check it is named for did not run. That is the
|
||||
worst failure mode for a security tool, because a clean report is
|
||||
indistinguishable from a clean target.
|
||||
|
||||
- **`hooks/scripts/pre-bash-destructive.mjs`** — the rule named "Filesystem
|
||||
root destruction (rm -rf /)" did not block `rm -rf /`. Its target
|
||||
alternation `(?:\/|~|\$HOME)\b` ended in a word-boundary assertion, which
|
||||
cannot hold after `/` or `~` at end-of-command. Measured: `rm -rf /`,
|
||||
`rm -rf ~`, `rm -rf /*`, `rm -fr /` and `sudo rm -rf /` all fell through to
|
||||
WARN — advisory only, exit 0, command executed. `rm -rf $HOME` and
|
||||
`rm -rf /usr` were blocked, which is why the gap survived: the rule looked
|
||||
functional from either end. The `\b` now sits on the `$HOME` alternative
|
||||
alone, so `$HOMEDIR` is still not swallowed. The prior test file had encoded
|
||||
the defect as expected behaviour, with a NOTE attributing it to merged `-rf`
|
||||
flags — a misdiagnosis, since `rm -rf /etc` blocks fine with merged flags.
|
||||
|
||||
- **`scanners/entropy-scanner.mjs`** — the "test fixtures intentionally contain
|
||||
example secrets" suppression matched against the **absolute** path. Any
|
||||
ancestor directory name containing `test`, `spec`, `fixture` or `mock`
|
||||
therefore suppressed every entropy finding in the whole target: a repository
|
||||
cloned into a CI workspace, or checked out beneath a folder named `testing`,
|
||||
reported zero secrets with status `ok`. The rule now keys off the path
|
||||
relative to the scan root, which was already computed and passed alongside
|
||||
it. Genuine fixture suppression (a `*.test.mjs` file, a relative `tests/`
|
||||
directory) is unchanged.
|
||||
|
||||
- **`scanners/ide-extension-scanner.mjs`** — `parseVSCodeExtension` signals
|
||||
failure as bare `null`; `parseIntelliJPlugin` signals it as a **truthy**
|
||||
`{ manifest: null, warnings }`. Only the former was guarded, so all five
|
||||
JetBrains failure paths (no `lib/`, `lib/` not a directory, `lib/`
|
||||
unreadable, no jars, no extractable jar) reached `manifest.hasSignature` and
|
||||
threw. `mapConcurrent` awaited without a per-item catch, so `Promise.all`
|
||||
rejected and the entire ide-scan aborted — one malformed plugin directory
|
||||
took down the scan of every other installed extension, including, in an
|
||||
audit context, a plugin that is malformed precisely because it is hostile.
|
||||
|
||||
- **`scanners/content-extractor.mjs`** — this is the remote-scan indirection
|
||||
layer: agents are supposed to see a sanitized evidence package, never raw
|
||||
hostile content. `stripInjection` scanned the raw text *and* its decoded
|
||||
form, but removed matches with `sanitized.replace(match[0], …)` against the
|
||||
raw text only. A decoded-only `match[0]` does not occur in the raw text by
|
||||
construction, so the replace silently did nothing: the payload was handed to
|
||||
the agent verbatim through `sanitized_content` while the report announced a
|
||||
critical injection. Removal now redacts the offending source line, since
|
||||
decoding is not length-preserving and decoded offsets cannot be mapped back.
|
||||
A payload split across several lines still cannot be attributed; those
|
||||
findings carry `unstripped: true` instead of failing quietly. Whole-file
|
||||
redaction was rejected — `normalizeForScan` base64-decodes any long blob, so
|
||||
a benign asset could blank a file's entire evidence.
|
||||
|
||||
- **`scanners/lib/ide-extension-parser.mjs`** — `decodeEntities` guarded
|
||||
`parseInt` with `Number.isFinite`, which bounds nothing, so a character
|
||||
reference above `0x10FFFF` raised `RangeError` in `String.fromCodePoint`.
|
||||
Contrary to how this was filed, the documented no-throw contract held: the
|
||||
per-field `safe()` wrapper catches it. The real effect was quieter — the
|
||||
field was replaced with `''`, so a `<name>` carrying one such reference
|
||||
parsed as an empty name and name-based checks (JetBrains typosquat
|
||||
detection) ran against nothing. Severity is below HIGH: a document with a
|
||||
code point above `0x10FFFF` is not well-formed XML, so IntelliJ would reject
|
||||
the plugin as well. Undecodable references are now left literal.
|
||||
|
||||
## v7.8.1 — Auto-cleaner command-injection fix (CRITICAL)
|
||||
|
||||
Security patch for a CRITICAL defect introduced with the v7.8.0 auto-cleaner
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue