diff --git a/STATE.md b/STATE.md index 4d92730..3101f14 100644 --- a/STATE.md +++ b/STATE.md @@ -1,69 +1,50 @@ # STATE — llm-security -**👉 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. +**👉 NESTE — START HER: v7.8.3 is CODE-COMPLETE LOCALLY but NOT released.** +10 commits on `main` (tip `4cffa30`), suite **2016/0**, gitleaks clean, NOT pushed/tagged/catalog-synced. +Two things gate "done", both need an operator decision: +1. **PUSH the release** (public mirror `open/llm-security`). Saturday → window open, but the commits + carry exploit descriptions; held for explicit go. Release mechanics (in order, per convention): + `git push origin` → `release-plugin.mjs llm-security --version 7.8.3 --create-tag` + → `release-plugin.mjs llm-security --version 7.8.3 --write --commit --push` → `check-versions.mjs` = 0 ERROR. + (release-plugin REFUSES until plugin.json == README badge == 7.8.3 AND the tag exists — both true now / by step 2.) +2. **`post-mcp-verify.mjs` uncommitted change** (in working tree, NOT in any commit): reads + `tool_response ?? tool_output` instead of only `tool_output` — a REAL latent bug (live PostToolUse + sends `tool_response`, so MCP-output injection scanning silently never fired live). +3 tests, green. + BUT it is **unattributed** (appeared pre-staged mid-session, not one of the 52 findings) and OUT of the + approved v7.8.3 scope. Decide: ship as a 48th fix (own commit), hold for a follow-up, or it's your edit. -**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: `~/.claude/projects/-Users-…-llm-security/ -f99dcf73-d344-4377-b8ba-09fe878f32a2/subagents/workflows/wf_99daed37-f8b/journal.jsonl`. +## What v7.8.3 is (the MEDIUM sweep) +52 open completion-review findings triaged (5 Opus verifiers) → **48 confirmed, 3 feature-requests, 1 non-defect**. +Corrected severity: **0 CRITICAL/HIGH, 21 MEDIUM, 27 LOW** (every review-claimed HIGH fell to MEDIUM). +Shipped: **47 fixes** (48 minus #27, deferred). Full triage: `docs/medium-triage.local.md` (gitignored). +Deferred to v8: **#11** persistence detector (kept only its doc fix), **#27** AST f-string recall, +**#34/#35/#37** missing AST09/AST10/MCP05 detectors (feature-requests), **#49** non-defect. -## 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` 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. +## The 10 commits (all HELD until tag) +`b224e18` yaml/workflow (#32,33,43) · `21c6c2b` normalization+SIG (#21,23,30,36,42,52,55) · +`8a59d61` hooks (#9,10,12,13,#11-doc) · `66e8ce2` misc (#20,22,50,54,56) · `76f190c` AST (#28,29) · +`196517f` supply-chain (#14-19,48) · `f3aaf54` trigger/toxic/policy (#26,38,39,40,41,57) · +`207385f` robustness/DoS (#24,25,31,51,53) · `1407016` tests (#58,59) · `4cffa30` docs+v7.8.3 bump (#8,44,45,46,47). +NOTE: `git log` order differs (parallel waves); hashes above are the authoritative finding→commit map. ## Ground truth (verified 2026-07-18) -- **`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). -- 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. +- `npm test` = **2016/0** (2013 release + 3 post-mcp-verify). Release tip `4cffa30` = 2013/0. +- plugin.json == package.json == README badge == **7.8.3**; test badge 2013; doc-consistency 30/30. +- #30 was scoped: `decodeEmbedded` is OPT-IN on `normalizeForScan` (default off — appending a decoded + copy double-counted content-extractor's per-match findings; caught by skill-scanner-narrative). Only + signature-scanner opts in. Do NOT re-broaden it into the shared normalizer. +- Scanner `VERSION` constants are frozen at 7.5.0 (not bumped since; left as-is — out of scope). -## Position taken (strategic anchor) -llm-security **consolidates**; growth at **family level** (security-commons + `llm-retrieval-guard` -sibling for the LLM08 write→retrieval seam). JS/TS AST-taint parity + post-clone CLAUDE.md-poisoning -stay here; research artifact-scanners (model/pickle, .ipynb, insecure-ML) relocate to commons/guard. -v8.0.0 = deprecation cleanup (LLM_SECURITY_* env-vars + riskScoreV1) + behaviour-preserving commons -extraction + verified fixes + docs consistency. +## Guardrails that bit this session (keep) +- **NEVER `git add -A`; stage explicit paths.** A pre-staged external change (post-mcp-verify) still + rode into commit J via a plain `git commit` — had to `reset --soft` + `restore --staged` to amend it out. + Check `git status` for surprise staged/modified files BEFORE every commit. +- A commit MESSAGE containing the pipe-to-shell literal is blocked by the voyage pre-bash hook — reword release notes. +- Test-pollution trap: `jetbrains-parser.test.mjs` asserts no `llmsec-jb-*` mkdtemp survives — use another prefix. ## 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:** 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`. +origin = Forgejo `open/llm-security` (PUBLIC, never GitHub). STATE.md is intentionally TRACKED +(`.gitignore:19-20` NOTE). Disclosure: fix committed, HELD until its release tag exists. Push window: +weekdays 20:00–23:00; weekends/holidays anytime. **DO NOT PUSH** the 3 untracked `docs/*.md` (parked v8 strategy). +Position (v8): llm-security consolidates; growth at family level (security-commons + `llm-retrieval-guard`).