You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: security and correctness hardening across credentials, API, storage, and tooling (#45)
* fix: harden security and correctness across credentials, API, and storage
Critical:
- audit.go: tighten JSONLFileSink file perms 0o644 -> 0o600 (metadata)
- api/server.go: store and call bgCancel in Shutdown (context leak);
add validateAuthConfig loopback auth gate on ListenAndServe
- guardrails.go: add AddRuleSafe/NewGuardrailsSafe (error-returning
variants) for untrusted rule sources; fix ApplyRedactions to use
FindAllStringIndex positions so the correct match instance is
redacted instead of the first occurrence via strings.Index
- cache/backend.go: bound RESP bulk-string allocation at 64 MB to
prevent memory-exhaustion DoS from a malicious Redis
- budgets.go: chmod budget DB to 0o600 (stores plaintext API keys)
High:
- conversation/engine.go: defer-close the final StreamResult on all
exit paths; add nil-provider guard in streamAndSave
- client/stream.go: sort OpenAI tool calls by Index before emitting
(was non-deterministic map iteration); nil args -> {_raw} fallback
- credentials/keyring_platform.go: only map not-found errors to
ErrNotFound; real backend errors now pass through so LookupSecret
logs them at Warn instead of silently classifying as 'no secret'
- openai_proxy.go: surface streaming errors as SSE data event before
[DONE]; raise body limit to 10 MiB for multi-turn conversations
Medium:
- provider_registry.go: bound resolveEnvSecret with 10s timeout ctx
- retry.go: time.After -> time.NewTimer + Stop (avoid timer leak)
- sqlite.go: escape backslash in GetNodeByPrefix LIKE query
- config/provider_env.go: ValidateBaseURL uses url.Parse instead of
os.Stat; config dir perms 0o755 -> 0o700
* fix: eliminate timer leaks in ratelimit, add security headers to API server
- ratelimit.go: replace time.After with time.NewTimer+Stop in both
token-bucket wait paths (minInterval enforcement and token-deficit
wait) to prevent runtime timer leaks on ctx cancellation
- adaptive_ratelimit.go: same fix in RPM and TPM throttle paths
- internal/api/server.go: wrap handler chain with securityHeaders
middleware (X-Content-Type-Options, X-Frame-Options, Cache-Control)
matching the hawk daemon's security posture
* fix: eliminate timer leak in router retry backoff
- router/retry.go: replace afterFunc = time.After with newTimer =
time.NewTimer to avoid leaking the timer in the runtime when ctx
is cancelled before the delay elapses
- router/router.go: update call site to use newTimer + timer.Stop()
* refactor: centralize HTTP utilities, bound ctx in keyring lookups
- internal/httputil: new package with canonical ConstantTimeEqual,
DecodeJSONBody, WriteJSON, SecurityHeaders, IsLoopbackHost,
ValidateAuthConfig, ExtractBearerToken — eliminates duplication
across internal/api/server.go (previously had local copies)
- internal/api/server.go: delegate to httputil; remove duplicated
constantTimeEqual, decodeJSONBody, writeJSON, securityHeaders,
validateAuthConfig, isLoopbackHost
- internal/api/auth_test.go: update to use httputil.ConstantTimeEqual
- config/runtime.go: bound envValue keyring lookup with 5s timeout
(was unbounded context.Background())
- runtime/runtime.go: bound CredentialTargets keyring probes with 5s
timeout each (was unbounded context.Background())
* fix: address review findings in security hardening PR
- budgets.go: chmod WAL/SHM sidecar files to 0o600 in addition to the
main DB file (WAL holds uncheckpointed pages including plaintext keys)
- httputil.go: make ExtractBearerToken case-insensitive per RFC 7235
and adopt it in Server.auth (was dead code with duplicated logic)
- server.go: use httputil.ExtractBearerToken instead of inline duplicate
0 commit comments