feat(security): SafeProvider boundary wrapper (Wave 1) #1
Reference in New Issue
Block a user
Delete Branch "feat/security-wave1-safeprovider"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes the four firewall-bypass call sites flagged in the 2026-05-19 external audit by introducing a
security.SafeProviderdecorator at the provider boundary. Until this PR, only the engine'sbuildRequest()ranFirewall.ScanOutgoingMessages/ScanSystemPrompt; SLM classifier, summarizer, routerStreamer, and prompt hooks all sent raw payloads to providers.After this PR, every `provider.Provider` registered with the router (or handed to a non-engine consumer) is wrapped with `security.SafeProvider`. The wrapper resolves the active firewall via `FirewallRef` (an `atomic.Pointer[Firewall]`), which lets us preserve the current init order — wrappers are installed before `NewFirewall` runs, and become active the moment `fwRef.Set(fw)` fires. Pass-through is the safe default.
Plan: `docs/superpowers/plans/2026-05-19-security-wave1-safeprovider.md`
What's wrapped
routerStreamer and hook PromptExecutor inherit the fix because they dispatch through `router.Stream` → `arm.Provider.Stream`.
Elf engines inherit wrapped arms via `elf.Manager.Spawn`. `SpawnWithProvider` is dead code (no callers).
Out of scope (deferred)
Engine reconciliation
`engine.buildRequest()` still scans inline. The plan keeps this for one release as belt-and-suspenders; redaction is idempotent so the second pass is a no-op. Will revisit removing the engine-level scan once telemetry shows the boundary holds.
Test plan
Advisor flagged that engine.Config.Provider stayed raw, so the safety property was 'every call goes through buildRequest' instead of the stronger 'every Stream call routes through a SafeProvider.' Wrap it even though buildRequest still scans inline — at worst this costs one extra idempotent scan pass; it removes the 'someone adds a fifth engine Stream site that skips buildRequest' failure mode. Engine.SetProvider gets a doc comment establishing the wrap contract for callers. No active callers today, but documenting it now prevents the future bypass. Confirmed elf engines inherit the wrap automatically: - elf.Manager.Spawn passes arm.Provider (already *SafeProvider after W1-3a) - elf.Manager.SpawnWithProvider has no callers — dead code path Added the Wave 1 plan to TODO.md under active plans.