diff --git a/STATE.md b/STATE.md index 28941f1..4d92730 100644 --- a/STATE.md +++ b/STATE.md @@ -1,50 +1,56 @@ # STATE — llm-security -**👉 NESTE — START HER (fresh session, Opus 4.8 xhigh) — v7.8.2: the ~6 HIGH:** -**v7.8.1 SHIPPED** — the CRITICAL auto-cleaner RCE is fixed, released, tagged, catalog-synced and -pushed. Next up is the HIGH tier from the same review. Iron Law per defect (regression test FIRST), -one commit per defect, then ship v7.8.2. All six are in the 59-finding review output (harvest source -below). Order them yourself; suggested by blast radius: -1. `rm -rf /` + `rm -rf ~` bypass the root-block — `hooks/pre-bash-destructive.mjs:24`. -2. Entropy scanner suppresses ALL findings when the ABSOLUTE path contains test/spec/fix — - `scanners/entropy-scanner.mjs:308` (a repo cloned to `/tmp/fix-me/` silently reports nothing). -3. Unparseable JetBrains plugin crashes the whole ide-scan + null-manifest deref — - `scanners/ide-extension-scanner.mjs:684` (likely 1 root cause / 2 findings). -4. Obfuscated injections are reported but NOT stripped from the LLM evidence package — - `scanners/content-extractor.mjs:119` (defeats the remote-scan injection defense). -5. Out-of-range numeric char-ref raises RangeError, breaking the no-throw contract — - `scanners/ide-extension-parser.mjs:147`. -Then the MEDIUM sweep (52 more) — likely its own release, not v7.8.2. +**👉 NESTE — START HER (fresh session, Opus 4.8 xhigh) — the MEDIUM sweep:** +**v7.8.2 SHIPPED** — all 5 HIGH from the completion review are fixed, released, tagged, +catalog-synced and pushed. Next is the MEDIUM tier (52 findings) from the same review. +Expect it to be its own release (v7.8.3 or v7.9.0), not a continuation of v7.8.2. +Same discipline that worked this session: **verify each finding against the code FIRST** +(STATE's own descriptions proved wrong 3x — see below), Iron Law per defect, one commit each. +Triage first: 52 MEDIUMs will contain duplicates and non-defects — do a verification pass and +report the real count before planning the release. **Review output (harvest source):** confirmed array + per-finding adversarial reasoning in task `w8s4rsaw8` output: `…/dfcab047-5467-4484-ab1a-5d6526439f78/tasks/w8s4rsaw8.output` (result.confirmed, -59 items). Durable journal (one result-line/agent): `~/.claude/projects/-Users-…-llm-security/ +59 items). Durable journal: `~/.claude/projects/-Users-…-llm-security/ f99dcf73-d344-4377-b8ba-09fe878f32a2/subagents/workflows/wf_99daed37-f8b/journal.jsonl`. +## Lesson from v7.8.2 — the review's own text is a premiss, not a fact +Three of five findings were described inaccurately in STATE/the review. Verify before acting: +- Paths were wrong: hooks live in `hooks/scripts/`, the parser in `scanners/lib/`. +- Blast radius was understated (the rm-block also missed `/*`, `-fr`, and sudo-prefixed forms). +- Severity was overstated once: the char-ref defect does NOT break the no-throw contract + (`safe()` catches it) and needs non-well-formed XML, so it is below HIGH. Said so in the commit. +- The old `pre-bash-destructive.test.mjs` had **encoded the bug as expected behaviour** with a + wrong root-cause NOTE. Green suites can be green because the test was shaped around the defect — + when a test comment explains why something *isn't* caught, treat it as a lead, not a spec. + +## ⚠️ NEVER `git add -A` in this repo +Did it this session and swept the 3 parked docs into a release commit aimed at a PUBLIC mirror. +Caught before push and amended out, but the guard is: **stage explicit paths, always.** + ## PARKED — v8 family strategy (behind an operator visibility decision, DO NOT PUSH) 4 of 6 deliverables written, LOCAL ONLY: `docs/JOBS-TO-BE-DONE.md`, `docs/FAMILY-MAP.md`, -`docs/commons-extraction-plan.md`, `docs/roadmap.md` [gitignored]. Remaining v8 docs (do NOT start until -visibility is decided): formal `docs/completion-review-2026-07-17.md` (findings table + -`severity.mjs` framework-coverage map + citations; roadmap B8/P3 reference it) and `V8-ANNOUNCEMENT.md`. -**⚠️ DO NOT PUSH the 3 untracked docs:** origin `open/llm-security.git` is a PUBLIC mirror; docs describe -cross-repo strategy + a sibling (`claude-code-llm-wiki`, "Private pending Anthropic ToS"). Do NOT `git add` -them without a visibility call (accept-public / keep-local `.local.md` / private family home). -NOTE: `docs/FAMILY-MAP.md:42` still says v7.8.0/1863 tests — stale, fix when the parking ends. +`docs/commons-extraction-plan.md`, `docs/roadmap.md` [gitignored]. Remaining v8 docs (do NOT start +until visibility is decided): formal `docs/completion-review-2026-07-17.md` and `V8-ANNOUNCEMENT.md`. +**DO NOT PUSH the 3 untracked docs:** origin `open/llm-security.git` is a PUBLIC mirror; they describe +cross-repo strategy + a sibling (`claude-code-llm-wiki`, "Private pending Anthropic ToS"). +NOTE: `docs/FAMILY-MAP.md:42` says v7.8.0/1863 tests — stale, fix when the parking ends. ## Ground truth (verified 2026-07-18) -- **`npm test` = 1865/0 green** at tip. Run tests with `npm test` — `node --test tests/` is NOT a valid - invocation (it fails as a harness error, not a regression); the script is `node --test 'tests/**/*.test.mjs'`. -- v7.8.1 released: tag `v7.8.1` pushed, catalog ref bumped, `check-versions.mjs` = **0 ERROR** +- **`npm test` = 1901/0 green** at tip (1865 + 36 new). Run with `npm test` — `node --test tests/` + is NOT valid; the script is `node --test 'tests/**/*.test.mjs'`. +- v7.8.2 released: tag `v7.8.2` pushed, catalog ref bumped, `check-versions.mjs` = **0 ERROR** (the one WARN is `okr`, pre-existing and unrelated). -- CRITICAL RCE: **reproduced live before the fix** (canary via `$CLEANER_RCE_CANARY` env expansion, - proving a shell parsed the filename), gone after. Both sinks in `scanners/auto-cleaner.mjs` are now - `spawnSync` argv-arrays; `execSync` is no longer imported there. -- Regression coverage is deliberately TWO tests (`tests/scanners/auto-cleaner-rce.test.mjs`): the belt - in `applyFixes` (metachar refusal) stops hostile input BEFORE the sink, so a single applyFixes-level - test would pass without proving the shell was gone. `validateContent` is exported for that reason — - do not "tidy" that export away. +- Test-pollution trap (hit and fixed): `jetbrains-parser.test.mjs` asserts globally that no + `llmsec-jb-*` dir survives in tmpdir. Any new test using that mkdtemp prefix fails it + order-dependently — passed one full run, failed the next. Pick a non-colliding prefix. +- Exported for testability, do not "tidy" away: `validateContent` (auto-cleaner, v7.8.1), + `stripInjection` (content-extractor, v7.8.2), `scanOneExtension` (ide-extension-scanner). + `content-extractor.mjs` now runs `main()` behind an `isMain` guard — CLI re-verified working. +- Known residual gap (documented, not a bug to re-file): `stripInjection` cannot attribute a + payload encoded ACROSS lines; those findings carry `unstripped: true` by design. Whole-file + redaction was considered and rejected (normalizeForScan base64-decodes any long blob). - Prior security: F-1/2/3/5/6 fixed; F-4 open (optional/LOW); B1/B2/E14 fixed. -- Stray `undefined/` (scan artifact, 2026-07-17): **removed**. ## Position taken (strategic anchor) llm-security **consolidates**; growth at **family level** (security-commons + `llm-retrieval-guard` @@ -56,6 +62,8 @@ extraction + verified fixes + docs consistency. ## Continuity origin = Forgejo `open/llm-security` (PUBLIC, never GitHub). Push window: weekdays 20:00–23:00; weekends/holidays anytime. STATE.md is intentionally TRACKED + committed (`.gitignore:19-20` NOTE). -**Disclosure convention (set this session):** a security fix to public code is committed but HELD until -its release tag exists, so the exploit description in the commit/notes never lands before the fixed -version users can upgrade to. Fix, bump, tag, catalog, push — one sequence. +**Disclosure convention:** a security fix to public code is committed but HELD until its release tag +exists, so the exploit description never lands before the fixed version. Fix, bump, tag, catalog, push. +**Release mechanics:** `catalog/scripts/release-plugin.mjs --version X.Y.Z` (dry-run by +default; `--create-tag`, then `--write --commit --push`). It refuses unless plugin.json == README +badge == target. Push the plugin repo's commits BEFORE `--create-tag`.