# llm-security v3.0 Upgrade — Master Session Document > This document tracks the multi-session upgrade from v2.5.0 to v3.0.0. > Updated after each session. Read this at session start. ## Session Prompt Template At the start of each new session, paste this: ``` Jeg fortsetter llm-security v3-oppgraderingen. Les V3-UPGRADE.md i plugin-rooten for full kontekst, nåværende status, og hva neste sesjon skal gjøre. ``` --- ## Overall Status | Session | Version | Status | Date | Commit | |---------|---------|--------|------|--------| | S1 | v2.6.0 | DONE | 2026-04-02 | b36312c | | S2 | v2.7.0 | DONE | 2026-04-02 | 41d7493 | | S3 | v2.7.1 | DONE | 2026-04-02 | ec01163 | | S4 | v2.8.0 | DONE | 2026-04-02 | b004f46 | | S4+ | v2.8.1 | DONE | 2026-04-03 | — | | S5 | v2.9.0 | DONE | 2026-04-03 | 162a23a | | S6 | v2.9.1 | DONE | 2026-04-03 | 110032e | | S7 | v2.9.2 | DONE | 2026-04-03 | 3129e7a | | S8 | v3.0.0 | DONE | 2026-04-03 | 293dee5 | **Current:** Session 8 complete — v3.0.0 released **Status:** ALL SESSIONS DONE --- ## Competitive Context **Why v3:** Public release. Close gaps vs Snyk Agent Scan (toxic flow analysis, MCP live inspection, continuous scanning, skill registry) while keeping architectural advantages (100% local, pre-extraction defense, full lifecycle coverage). **Key differentiators to maintain:** - Pre-extraction layer (no competitor has this) - 7+ deterministic scanners + LLM analysis in same pipeline - 100% local, no cloud dependency - Full lifecycle: hooks + scanning + audit + threat modeling + remediation - Supply chain hook covering 7 package managers + OSV.dev **v3.0 target inventory:** - 9 scanners (was 7, now 9): +toxic-flow-analyzer (done), +mcp-live-inspect (done) - 7 hooks (was 6): +post-session-guard - 14 commands (was 10, now 14): +mcp-inspect (done), +diff (done), +watch (done), +registry (done) - 6 agents (all updated with new OWASP mappings) - 9 knowledge files (was 7): +owasp-skills-top10 (done), +skill-registry.json (done) --- ## Session 1: Enhanced Patterns + OWASP Mapping (v2.6.0) **Goal:** Foundation for all subsequent sessions. ### Tasks - [ ] **1a.** Add `MEDIUM_PATTERNS` tier to `scanners/lib/injection-patterns.mjs` - ~15-20 patterns: base64 payloads, leetspeak, multi-language mixing, markdown/HTML comment injection, homoglyph-obfuscated keywords, invisible Unicode separators - Update `scanForInjection()` to return severity level (not just boolean) - [ ] **1b.** Update OWASP mappings in `scanners/lib/severity.mjs` - Add ASI01-ASI10 (Agentic Top 10) prefix mappings - Add MCP1-MCP7 (MCP Top 10) prefix mappings - Add AST01-AST10 (Skills Top 10) prefix mappings - Add TFA scanner prefix - Update `owaspCategorize()` for all frameworks - [ ] **1c.** Create `knowledge/owasp-skills-top10.md` - AST01-AST10 definitions and mapping - [ ] **1d.** Update agent prompts with new OWASP references - `agents/skill-scanner-agent.md`: AST10 mapping - `agents/mcp-scanner-agent.md`: MCP Top 10 mapping - `agents/posture-assessor-agent.md`: ASI mapping - `agents/deep-scan-synthesizer-agent.md`: new scanner prefixes - [ ] **1e.** Update `CLAUDE.md` with new knowledge file - [ ] **1f.** Verify: `node scanners/scan-orchestrator.mjs .` passes, new OWASP IDs in output ### Files Modified - `scanners/lib/injection-patterns.mjs` — MEDIUM tier - `scanners/lib/severity.mjs` — ASI/AST/MCP/TFA mappings - `agents/skill-scanner-agent.md` — AST10 - `agents/mcp-scanner-agent.md` — MCP Top 10 - `agents/posture-assessor-agent.md` — ASI - `agents/deep-scan-synthesizer-agent.md` — new prefixes - `CLAUDE.md` — knowledge table update ### Files Created - `knowledge/owasp-skills-top10.md` ### Acceptance Criteria - `scanForInjection()` returns `{ found, severity, patterns }` instead of boolean - All 4 OWASP frameworks mapped in severity.mjs - `node scanners/scan-orchestrator.mjs .` runs clean - MEDIUM patterns detect base64 instruction payloads and homoglyph obfuscation --- ## Session 2: Toxic Flow Analysis (v2.7.0) — FLAGSHIP **Goal:** Detect lethal trifecta — when combinations of safe tools create exfiltration chains. ### Concept "Lethal trifecta" (Willison/Invariant Labs): 1. Agent exposed to **untrusted input** (prompt injection surface) 2. Agent has access to **sensitive data** via tools 3. An **exfiltration sink** exists (HTTP, email, file write) ### Tasks - [x] **2a.** Create `scanners/toxic-flow-analyzer.mjs` (~380 lines) - Phase 1: Component inventory from plugin frontmatter + MCP/hook detection - Phase 2: Trifecta leg classification with prior scanner enrichment - Phase 3: Trifecta detection (direct/cross-component/project-level) with mitigation downgrades - Scanner prefix: `TFA`, OWASP: ASI01, ASI02, ASI05 - [x] **2b.** Modify `scanners/scan-orchestrator.mjs` - TFA runs LAST after all 7 scanners - Pass accumulated scanner results to TFA via `requiresPriorResults` flag - [x] **2c.** Update `commands/scan.md` + `commands/deep-scan.md` to render TFA findings - [x] **2d.** Update `agents/deep-scan-synthesizer-agent.md` for TFA report section - [x] **2e.** Create test fixture: `test-fixtures/trifecta-plugin/` with known trifecta pattern - [x] **2f.** Update `CLAUDE.md` — version v2.7.0, scanner count 8 ### Key Design Decisions - **Post-processing correlator** — does NOT re-scan files, consumes existing scanner output - **Severity:** CRITICAL (2-hop + confirmed taint), HIGH (3+ hop or unconfirmed), MEDIUM (theoretical chain) - **Graph model:** Adjacency list, not full graph library (keep dependencies at zero) ### Files Modified - `scanners/scan-orchestrator.mjs` - `scanners/lib/severity.mjs` (TFA prefix already added in S1) - `commands/scan.md` - `agents/deep-scan-synthesizer-agent.md` - `CLAUDE.md` ### Files Created - `scanners/toxic-flow-analyzer.mjs` ### Acceptance Criteria - Test fixture with read+exfil tools produces TFA-001 CRITICAL finding - Scan-orchestrator runs 8 scanners with TFA last - `/security scan` on fixture shows chain description - `/security deep-scan` includes TFA section in report --- ## Session 3: Runtime Session Guard (v2.7.1) **Goal:** Real-time PostToolUse hook detecting lethal trifecta forming during a session. ### Tasks - [x] **3a.** Create `hooks/scripts/post-session-guard.mjs` (~200-250 lines) - Append tool calls to `/tmp/llm-security-session-${ppid}.jsonl` - Classify each tool: `input_source | data_access | exfil_sink | neutral` - Sliding window (20 calls) trifecta detection - Emit `systemMessage` warning (never block) - Cleanup state files >24h old - [x] **3b.** Update `hooks/hooks.json` — add PostToolUse entry - [x] **3c.** Update `CLAUDE.md` — hooks table - [x] **3d.** Test: simulate trifecta sequence, verify warning ### Files Modified - `hooks/hooks.json` - `CLAUDE.md` ### Files Created - `hooks/scripts/post-session-guard.mjs` ### Acceptance Criteria - Hook fires on every PostToolUse - Trifecta sequence (Read sensitive → Bash curl) triggers warning - State file is JSONL, keyed by ppid - Old state files cleaned up - No false positives on normal tool sequences --- ## Session 4: MCP Runtime Inspection (v2.8.0) **Goal:** Connect to running MCP servers, fetch live tool descriptions, scan for injection/poisoning/shadowing. ### Tasks - [x] **4a.** Create `scanners/mcp-live-inspect.mjs` (~350-400 lines) - Config discovery (6 locations, reuse mcp-scanner-agent logic) - Spawn servers, JSON-RPC 2.0 initialize + tools/list + prompts/list + resources/list - Scan descriptions with injection-patterns.mjs - Tool shadowing detection (same names across servers) - Description drift (live vs static config) - 10s timeout per server - [x] **4b.** Create `commands/mcp-inspect.md` (~40-50 lines) - [x] **4c.** Update `commands/mcp-audit.md` with `--live` flag - [x] **4d.** Update `agents/mcp-scanner-agent.md` for live inspection context - [x] **4e.** Update `CLAUDE.md` - [x] **4f.** Update `README.md` — badges, tables, version history - [x] **4g.** Update `plugin.json` version - [x] **4h.** Subtree push to public repo ### Files Modified - `commands/mcp-audit.md` - `agents/mcp-scanner-agent.md` - `CLAUDE.md` ### Files Created - `scanners/mcp-live-inspect.mjs` - `commands/mcp-inspect.md` ### Acceptance Criteria - Successfully connects to at least one MCP server and fetches tool list - Injection patterns detected in tool descriptions - Tool shadowing flagged when two servers expose same tool name - Servers that fail to start are skipped gracefully (10s timeout) --- ## Session 5: Report Diffing & Baseline (v2.9.0) **Goal:** Compare scan results over time. Show new/resolved/unchanged findings. ### Tasks - [x] **5a.** Create `scanners/lib/diff-engine.mjs` (~200-250 lines) - Baseline storage in `reports/baselines/.json` - Match findings by: scanner prefix + file path + line (fuzzy ±3) + pattern type - Categories: `new`, `resolved`, `unchanged`, `moved` - [x] **5b.** Update `scanners/scan-orchestrator.mjs` — add `--baseline` and `--save-baseline` flags - [x] **5c.** Create `commands/diff.md` (~40-50 lines) - [x] **5d.** Update `CLAUDE.md` - [x] **5e.** Update `README.md` — badges, tables, version history - [x] **5f.** Update `plugin.json` version - [x] **5g.** Subtree push to public repo ### Files Modified - `scanners/scan-orchestrator.mjs` - `CLAUDE.md` ### Files Created - `scanners/lib/diff-engine.mjs` - `commands/diff.md` - `reports/baselines/` (directory) ### Acceptance Criteria - `--save-baseline` stores results, `--baseline` loads and diffs - NEW findings flagged after adding a vulnerability - RESOLVED findings flagged after removing one - Fuzzy line matching handles ±3 line drift --- ## Session 6: Continuous/Background Scanning (v2.9.1) **Goal:** Automated periodic scanning with delta reporting. ### Tasks - [x] **6a.** Create `commands/watch.md` (~50-60 lines) - `/security watch [path] [--interval 6h]` - Uses /loop as execution engine - Runs scan-orchestrator with --baseline --save-baseline - Reports delta only - [x] **6b.** Create `scanners/watch-cron.mjs` (~150-200 lines) - Standalone Node.js script for cron/launchd - Config: `reports/watch/config.json` - Output: `reports/watch/latest.json` - [x] **6c.** Update `CLAUDE.md` - [x] **6d.** Update `README.md` — badges, tables, version history - [x] **6e.** Update `plugin.json` version - [x] **6f.** Subtree push to public repo ### Files Modified - `CLAUDE.md` ### Files Created - `commands/watch.md` - `scanners/watch-cron.mjs` - `reports/watch/` (directory) ### Acceptance Criteria - `/security watch .` creates baseline and shows "No changes" - After modification: shows delta with NEW findings - Cron wrapper runs standalone: `node scanners/watch-cron.mjs` --- ## Session 7: Skill Signature Registry (v2.9.2) **Goal:** Local database of known skill patterns and risk profiles. ### Tasks - [x] **7a.** Create `scanners/lib/skill-registry.mjs` (~300-350 lines) - Fingerprinting: SHA-256 of normalized SKILL.md content - `scanAndRegister(skillPath)` and `checkRegistry(fingerprint)` - Registry format: JSON with skill metadata + findings summary - [x] **7b.** Create `knowledge/skill-registry.json` (seed data) - [x] **7c.** Create `commands/registry.md` (~40-50 lines) - `/security registry` — stats - `/security registry scan ` — scan and register - `/security registry search ` — search - [x] **7d.** Integrate with `commands/scan.md` — check registry before full scan - [x] **7e.** Update `CLAUDE.md` - [x] **7f.** Update `README.md` — badges, tables, version history - [x] **7g.** Update `plugin.json` version - [x] **7h.** Subtree push to public repo ### Files Modified - `commands/scan.md` - `CLAUDE.md` ### Files Created - `scanners/lib/skill-registry.mjs` - `knowledge/skill-registry.json` - `commands/registry.md` ### Acceptance Criteria - Scan a skill → fingerprint added to registry - Re-scan same skill → registry hit, instant result - `/security registry search` returns matches --- ## Session 8: Polish & Public Release (v3.0.0) **Goal:** Quality pass, documentation, public release, announcement prep. ### Tasks - [x] **8a.** Full quality pass - 544/544 tests pass - Scan-orchestrator: 8/8 scanners OK (0 findings with ignore, ~190 suppressed) - All 14 commands verified (valid frontmatter) - All 8 hooks verified (parse without errors) - Scan-orchestrator: ~7.5s on plugin self-scan - [x] **8b.** Documentation - README.md: v3 badge, mermaid architecture diagram, TFA in scanner table, updated stats, v3.0.0 version history - CHANGELOG.md: full version history v1.0→v3.0 in Keep a Changelog format - package.json + plugin.json bumped to v3.0.0 - .llm-security-ignore updated with TFA suppressions - [x] **8c.** Public repo sync - Subtree push to `git.fromaitochitta.com/open/claude-code-llm-security` - [x] **8d.** Announcement prep - V3-ANNOUNCEMENT.md with feature comparison matrix (vs Snyk Agent Scan, Lasso Claude Hooks) - Key differentiators narrative (6 points) - Demo scenario with scan/diff/watch workflow ### Acceptance Criteria - `/security audit` on plugin itself scores A or B - All commands documented in CLAUDE.md - All hooks documented in CLAUDE.md - README has complete v3 feature list - Public repo updated and accessible --- ## Technical Notes ### Reusable Infrastructure (do not duplicate) - `scanners/lib/injection-patterns.mjs` — all injection pattern matching - `scanners/lib/output.mjs` — `finding()` and `scannerResult()` builders - `scanners/lib/severity.mjs` — risk scoring, OWASP mapping - `scanners/lib/file-discovery.mjs` — `discoverFiles()` and `readTextFile()` - `scanners/lib/string-utils.mjs` — entropy, Levenshtein, base64 detection - `scanners/content-extractor.mjs` — pre-extraction for remote repos ### Constraints - All code is Node.js (>=18), no external dependencies beyond Node stdlib - Hooks are separate processes per invocation (no shared memory) - Context budget: max 3 knowledge files per agent invocation - Intel Mac target (no Apple Silicon-specific features) - Plugin convention: commands ~30-60 lines, agents use registered subagent_type - CLAUDE.md updated in same commit as the change it documents - **README.md + plugin.json + subtree push are MANDATORY per session** — not optional, not deferred to S8. Every version bump must update: plugin.json version, README badges/tables/version history, then subtree push. Session is NOT done until public repo is current. ### Scanner Integration Pattern ```javascript // In scan-orchestrator.mjs, TFA scanner receives prior results: const tfaResults = await runTfaScanner(target, files, priorResults); // All other scanners: (target, files) signature unchanged ``` ### Hook State Pattern ```javascript // Session guard uses temp file for cross-invocation state: const stateFile = `/tmp/llm-security-session-${process.ppid}.jsonl`; // Append on each invocation, read sliding window for analysis ```