fix(ENG-2966): stackone-defender - fall back to Tier 1 and warn when the classifier can't load
Summary
When the Tier 2 model or ONNX runtime failed to load, the plugin scanned nothing and reported healthy (found in the SOL-423 audit, tracked as ENG-2966):
@stackone/defenderallows every result in that case and only warns on the console, which the daemon'sstdio: "ignore"dropped.enableTier1: falseleft no fallback, and the hook only writes output for blocked results.
This PR:
- Falls back to Tier 1. The daemon checks
isTier2Ready()after warmup; if not ready it rebuilds the defense with Tier 1 on and every field scanned. (Tier 1 only reads named "risky" fields likedescription/body, and plain tool output is wrapped as{ output }, so enabling Tier 1 alone would still have scanned nothing.) - Makes it visible.
tier2Readyis logged, written to the state file, shown by--status, and sent with each scan result. The hook tells Claude once per session that Defender is degraded and how to check, alongside any HIGH RISK note. A daemon from before this change omitstier2Readyand is treated as ready. - Fixes the README. The "falls back to a smaller MLP head" line described a fallback that doesn't exist in
@stackone/defender; it now describes the real behaviour. - Pins deps exactly (
@huggingface/transformers3.8.1,fasttext.wasm1.0.1,onnxruntime-node1.27.0), matching what the lockfile already resolves. A newer transformers mangles the tokenizer and makes Tier 2 skip silently, the same failure mode.
Decisions on the ticket's questions: degraded state is visible (1); Tier 1 is enabled only when Tier 2 is unavailable (2); requireTier2 is not used, since it makes every tool call wait out the 6 s spawn deadline and then fail open (3); README updated (4).
Test plan
-
node --test: 18/18, including the newtests/degraded-tier2.test.mjs(missing model blocks an overt injection via the fallback, benign prose allowed, notice once per session) - End-to-end hook run with a missing model path and an isolated
HOME: first scan returns the degraded notice plus the HIGH RISK note, benign output is silent, the second injection gets only the HIGH RISK note;--statusshowstier2Ready: false - End-to-end with the real model: no notice, Tier 2 flags the injection (0.963)
Not covered here: stackone-defender-antigravity likely has the same issue.
🤖 Generated with Claude Code
Summary by cubic
Fixes the @stackone/defender plugin so a failed Tier 2 model load no longer silently scans nothing and reports healthy. The daemon now falls back to Tier 1 pattern scanning over every field and surfaces the degraded state instead of dropping the load error.
Bug Fixes
- Falls back to Tier 1 with every field scanned, including empty-keyed ones, when the Tier 2 classifier isn't ready after warmup, and restarts after 10 minutes so a transient load failure retries Tier 2.
- Logs the classifier's load error and
tier2Ready, writes the latter to the state file, shows it in--status, sends it with each scan result, and tells Claude once per session when degraded, warning that pattern-only mode can flag files that merely discuss injection. - Pins
@huggingface/transformers,fasttext.wasm, andonnxruntime-nodeexactly to the versions the lockfile resolves, so a future compatible-range upgrade can't silently disable Tier 2. - README now describes this real fallback instead of a nonexistent "smaller MLP head".