Skip to content

Security: fix plugin auth/access bypasses + fail-open controls#91

Merged
ndreno merged 1 commit into
mainfrom
security/plugin-bypass-fixes
Jun 30, 2026
Merged

Security: fix plugin auth/access bypasses + fail-open controls#91
ndreno merged 1 commit into
mainfrom
security/plugin-bypass-fixes

Conversation

@ndreno

@ndreno ndreno commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Hardening of the plugin layer (security review item #7). Secure-by-default: controls that previously failed open now fail closed, with an explicit opt-out. All touched plugins build and pass their tests; the SDK + workspace pass the clippy gate.

Fixes

  • SDK: new resolve_client_ip(req, trusted_proxies) — one trusted-proxy-aware client-IP resolver shared by the security middlewares (kills the per-plugin spoofable variants).
  • ip-restriction: IPv4 and IPv6 (std::net::IpAddr) so an IPv6 client is no longer silently let past a denylist; X-Forwarded-For/X-Real-IP honored only behind configured trusted_proxies; fail closed on an unparseable client IP when restrictions exist.
  • cors: a literal * with allow_credentials no longer allows an arbitrary origin (Fetch-spec violation); responses now reflect the validated origin with Vary: Origin (carried from on_request via context).
  • rate-limit / ai-token-limit: fail closed (503) when the host limiter is unavailable by default (fail_open: true to opt out); partition by the trusted-proxy-aware client IP so rotating X-Forwarded-For can't mint fresh buckets; ai-token-limit logs (instead of silently charging 0) an unparseable token count.
  • ai-prompt-guard: fail closed on an unparseable body or non-array messages (fail_open: true to opt out) so the guard can't be bypassed with a malformed body.
  • dispatchers (kafka, nats, lambda, s3, http-upstream, oauth2-auth, oidc-auth, ws-upstream): clamp the host-returned read length to the buffer before slicing — removes a panic vector.
  • ai-proxy: schema guidance to use secret references (env:///file://) for api_key (keys are not logged anywhere — verified).

New config (all backward-compatible, secure defaults)

Key Plugins Default
trusted_proxies ip-restriction, rate-limit, ai-token-limit [] (forwarded headers untrusted)
fail_open rate-limit, ai-token-limit, ai-prompt-guard false (fail closed)

Behavior changes (secure-by-default)

  • Spoofed X-Forwarded-For is ignored unless the immediate peer is a configured trusted proxy.
  • A rate/token limiter outage now rejects (503) instead of allowing traffic; set fail_open: true to keep the old behavior.
  • ai-prompt-guard rejects (400) a body it can't inspect instead of passing it through.

Tracked privately as #7. Remaining auth/crypto items (constant-time compare, OIDC alg binding, etc.) are issue #6.

Hardens the plugin layer per the security review. All touched plugins
build and pass their tests; the SDK and workspace pass the clippy gate.

- sdk: add `resolve_client_ip(req, trusted_proxies)` so security
  middlewares share one trusted-proxy-aware client-IP resolver instead
  of independently spoofable implementations.
- ip-restriction: support IPv6 (std::net::IpAddr) so an IPv6 client is
  no longer silently allowed past a denylist; honor X-Forwarded-For only
  behind configured `trusted_proxies`; fail closed on an unparseable IP
  when restrictions exist.
- cors: a literal `*` with credentials no longer allows arbitrary
  origins (Fetch-spec violation); reflect the validated origin with
  `Vary: Origin` on responses via request context.
- rate-limit / ai-token-limit: fail CLOSED (503) when the host limiter
  is unavailable by default (`fail_open` to opt out); partition by the
  trusted-proxy-aware client IP so rotating XFF can't mint fresh buckets;
  ai-token-limit logs (instead of silently zeroing) an unparseable token
  count.
- ai-prompt-guard: fail CLOSED on an unparseable body or non-array
  `messages` (`fail_open` to opt out) so the guard can't be bypassed
  with a malformed body.
- dispatchers (kafka, nats, lambda, s3, http-upstream, oauth2-auth,
  oidc-auth, ws-upstream): clamp the host-returned read length to the
  buffer before slicing, removing a panic vector.
- ai-proxy: schema guidance to use secret references for api_key (keys
  are not logged anywhere; verified).

New config: `trusted_proxies` (ip-restriction, rate-limit,
ai-token-limit), `fail_open` (rate-limit, ai-token-limit,
ai-prompt-guard).
@ndreno ndreno merged commit bf1d4f8 into main Jun 30, 2026
12 checks passed
@ndreno ndreno deleted the security/plugin-bypass-fixes branch June 30, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant