Files
vikingowl 8dcca64e41 feat(security): add SafeProvider boundary wrapper (W1-1)
Introduces internal/security/SafeProvider — a provider.Provider decorator
that scans outgoing messages and the system prompt through the firewall
before delegating to the inner provider. Tool-result redaction stays in
the engine because it needs per-tool context the boundary lacks.

FirewallRef provides a late-binding atomic.Pointer[Firewall] so the
wrapper can be installed before NewFirewall runs in main. A nil or
unset ref makes SafeProvider a pass-through — preserves the current
init order without lock contention or panics.

Wave 1 of the post-audit hardening plan
(docs/superpowers/plans/2026-05-19-security-wave1-safeprovider.md).
Closes the architectural critique that secret scanning only ran inside
engine.buildRequest(), leaving SLM/summarizer/hook/routerStreamer paths
to send raw payloads. This commit only ships the wrapper; W1-2 and W1-3
will wire it through main and the four bypass sites.
2026-05-19 22:28:46 +02:00

29 lines
920 B
Go

package security
import "sync/atomic"
// FirewallRef is a late-binding holder for *Firewall.
//
// Construction order in gnoma builds provider arms before the firewall
// exists. SafeProvider takes a *FirewallRef at construction time, then
// resolves the current *Firewall on each call. This lets the wiring be
// installed before NewFirewall runs without any locking on the hot path.
//
// A nil *FirewallRef or a *FirewallRef whose pointer has not been Set
// is interpreted by SafeProvider as "no firewall installed yet" —
// requests pass through unmodified.
type FirewallRef struct {
p atomic.Pointer[Firewall]
}
// Set installs fw as the active firewall. Safe for concurrent use.
func (r *FirewallRef) Set(fw *Firewall) {
r.p.Store(fw)
}
// Get returns the currently installed firewall, or nil if none has been
// Set. Safe for concurrent use.
func (r *FirewallRef) Get() *Firewall {
return r.p.Load()
}