ktg-plugin-marketplace/plugins/llm-security/examples/poisoned-claude-md/expected-findings.md
Kjell Tore Guttormsen ca5a8cec67 feat(llm-security): add 3 more runnable threat examples [skip-docs]
Three new self-contained, runnable threat demonstrations under
examples/, continuing the batch started in 583a78c. Each example
has README.md + run-*.mjs + expected-findings.md and uses
state-isolation discipline so the user's real cache/state files
are never polluted.

- examples/supply-chain-attack/ — two-layer demonstration:
  pre-install-supply-chain (PreToolUse) blocks compromised
  event-stream version 3.3.6 and emits a scope-hop advisory for
  the @evilcorp scope; dep-auditor (DEP scanner, offline) flags
  5 typosquat dependencies plus a curl-piped install-script
  vector in the fixture package.json. Maps to LLM03/LLM05/ASI04.

- examples/poisoned-claude-md/ — all 6 memory-poisoning detectors
  fire on a deliberately poisoned CLAUDE.md plus a fixture
  agent file under .claude/agents (E15/v7.2.0 surface):
  detectInjection, detectShellCommands, detectSuspiciousUrls,
  detectCredentialPaths, detectPermissionExpansion,
  detectEncodedPayloads. No agent runtime needed — scanner
  imported directly. Maps to LLM01/LLM06/ASI04.

- examples/bash-evasion-gallery/ — one disguised variant per
  T1 through T9 evasion technique fed through pre-bash-destructive,
  verified BLOCK after bash-normalize strips the evasion. T8
  base64-pipe-shell uses its own BLOCK_RULE. The canonical
  destructive form uses a path token rather than the bare slash
  (regex word-boundary requires it). Source-string fragmentation
  pattern reused from the e2e attack-chain test. Maps to
  LLM06/ASI01/LLM01.

Plugin README "Other runnable examples" section + plugin
CLAUDE.md "Examples" table + CHANGELOG Unreleased/Added
all updated. Marketplace root README unchanged
([skip-docs] for marketplace-level gate — plugin's outward
coverage is unchanged, only demonstrations were added).
2026-05-05 15:01:20 +02:00

2.3 KiB

Expected Findings — Memory-Poisoning Walkthrough

This is the testable contract. run-memory-poisoning.mjs exits 0 only when each detector category has at least one finding.

Per-detector contract

Detector Min findings Severity floor OWASP
detectInjection 1 MEDIUM LLM01
detectShellCommands 1 LOW LLM06
detectSuspiciousUrls 1 HIGH LLM02
detectCredentialPaths 1 HIGH LLM02
detectPermissionExpansion 1 CRITICAL LLM06 / ASI06
detectEncodedPayloads 1 MEDIUM LLM01

Total: at least 6 unique findings, severity-weighted such that the highest tier in any single file is CRITICAL.

File-level expectations

File Min findings
CLAUDE.md 12
.claude/agents/health-checker.md 3

The agent file count is lower because the fixture is intentionally shorter — its purpose is to prove E15 (v7.2.0) coverage of the agent-file surface, not to exhaustively replicate every CLAUDE.md signal.

Bucket-mapping logic (in run-memory-poisoning.mjs)

Findings are bucketed in priority order:

  1. permission expansion (most specific) — matches "permission expansion" or allowed-tools / bypassPermissions / dangerously / skip-permissions
  2. credential paths — matches "credential path" or .ssh / .aws / kubeconfig / wallet / service-account-key
  3. suspicious URLs — matches "suspicious exfiltration url/domain" or webhook.site / requestbin
  4. encoded payloads — matches "base64" or "encoded payload"
  5. shell commands — matches "shell command" or curl / wget / eval
  6. injection (broadest, last) — matches "injection" / "ignore previous" / "spoofed"

The order matters because some findings carry "directive" or "override" wording that would otherwise fall into the injection bucket — by checking permission-expansion first we avoid double-counting.

Side effects

  • No file is modified
  • No network call (scanner is fully offline)
  • Discovery uses scanners/lib/file-discovery.mjs::discoverFiles()
  • Memory-poisoning-scanner only inspects files matching MEMORY_FILE_PATTERNS — the fixture deliberately uses CLAUDE.md and .claude/agents/health-checker.md to ensure the scanner picks them up