feat(claudewatcher): client-name guard via RegisterRule + env
Pre-rollout guard. Source code stays clean — client identities come from CLAUDE_INGEST_CLIENT_BLOCK env (sourced from a SOPS-encrypted k8s secret in infra repo). Env value is a regex alternation; main wraps it with `(?i)\b(...)\b` so word-boundary matching avoids false hits inside longer identifiers (e.g. "Sebastian" doesn't trigger on "SEB"). DefaultRules (credential shapes) still take precedence so any leak that's BOTH a client mention AND a credential shape logs as the credential — strictly more dangerous, points triage at the right thing. Tests cover precedence + case variations + word-boundary respect + invalid-pattern rejection. Refs: infra#73 Track E.1 pre-rollout grill (option B). Bump-Type: minor
This commit is contained in:
@@ -256,6 +256,19 @@ func main() {
|
||||
logger.Error("CLAUDE_SESSIONS_DIR set but BRAIN_PG_DSN missing — claudewatcher needs the cursor table")
|
||||
os.Exit(1)
|
||||
}
|
||||
// Client-name guard. The env value is a regex alternation
|
||||
// (e.g. "SEB|Mastercard"); we wrap it with word boundaries
|
||||
// and case-insensitive flag so substrings inside longer
|
||||
// identifiers don't false-match. Sourced from a SOPS secret
|
||||
// so client identities never live in source.
|
||||
if clientBlock := os.Getenv("CLAUDE_INGEST_CLIENT_BLOCK"); clientBlock != "" {
|
||||
pattern := `(?i)\b(` + clientBlock + `)\b`
|
||||
if err := claudewatcher.RegisterRule("client-name", pattern); err != nil {
|
||||
logger.Error("claudewatcher client-block rule invalid", "err", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
logger.Info("claudewatcher client-block guard registered")
|
||||
}
|
||||
cursorStore, cerr := claudewatcher.NewCursorStore(ctx, pgDSN)
|
||||
if cerr != nil {
|
||||
logger.Error("claudewatcher cursor init", "err", cerr)
|
||||
|
||||
Reference in New Issue
Block a user