fix(ENG-2966): stackone-defender - fall back to Tier 1 and warn when the classifier can't load

Merged
HI
1mo ago
+286-28

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/defender allows every result in that case and only warns on the console, which the daemon's stdio: "ignore" dropped.
  • enableTier1: false left 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 like description / body, and plain tool output is wrapped as { output }, so enabling Tier 1 alone would still have scanned nothing.)
  • Makes it visible. tier2Ready is 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 omits tier2Ready and 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/transformers 3.8.1, fasttext.wasm 1.0.1, onnxruntime-node 1.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 new tests/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; --status shows tier2Ready: 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, and onnxruntime-node exactly 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".