Skip to content

fix(security): rate-limit keying, header-injection + SSRF guards (AU2/AU5/AU4)#64

Merged
russellromney merged 1 commit into
mainfrom
fix/security-hardening
Jun 26, 2026
Merged

fix(security): rate-limit keying, header-injection + SSRF guards (AU2/AU5/AU4)#64
russellromney merged 1 commit into
mainfrom
fix/security-hardening

Conversation

@russellromney

Copy link
Copy Markdown
Owner

Defense-in-depth security hardening from the adversarial review (ADVERSARIAL_REVIEW_2026-06-25.md).

AU2 — rate limiter keyed on raw socket IP

Behind a reverse proxy every request shares the proxy's IP (one global bucket → trivial DoS / accidental throttle), and over IPv6 each address in a /64 is a fresh bucket (trivially bypassed). New keying:

  • Take the client IP from a trusted forwarded-for header only when RIPCLONE_TRUST_FORWARDED_FOR=1 (off by default — the header is client-spoofable; rightmost entry = what the immediately-trusted proxy saw).
  • Collapse IPv6 to its /64 so an attacker can't rotate within their allocation.

AU5 — CRLF header injection via token / auth_template

A token or auth_template with \r\n could smuggle extra headers into the upstream git HTTP request. ProviderInstance::auth_header now refuses to emit a value containing CR/LF/NUL.

AU4 — SSRF guard on provider host

Reject a configured provider host that's a link-local address (incl. the cloud metadata endpoint 169.254.169.254) or the unspecified address, plus malformed hosts (userinfo/control/empty). Loopback and private LAN are intentionally allowed (same-box/on-prem self-host is legitimate; providers are operator-configured, not attacker-supplied). Not full DNS-rebinding protection.

Tests for all three. Full cargo test green; cargo fmt --check + clippy --all-targets -D warnings clean.

New env var: RIPCLONE_TRUST_FORWARDED_FOR=1 (enable only with a trusted reverse proxy directly in front).

🤖 Generated with Claude Code

…uard (AU2/AU5/AU4)

AU2: the rate limiter keyed on the raw socket IP — useless behind a reverse
proxy (every request shares the proxy IP → one global bucket) and bypassable
over IPv6 (each address in a /64 is a fresh bucket). Key derivation now: take
the client IP from a trusted forwarded-for header when
`RIPCLONE_TRUST_FORWARDED_FOR=1` (rightmost entry = what the immediately-trusted
proxy saw; off by default since the header is client-spoofable), and collapse
IPv6 to its /64 so an attacker can't rotate within their allocation.

AU5: a token or `auth_template` containing CR/LF could smuggle extra headers
into the upstream git HTTP request. `ProviderInstance::auth_header` now refuses
to emit a value containing CR/LF/NUL. (HTTP-header request tokens are already
CRLF-free by the http layer; this covers the configured-token / template path.)

AU4: defense-in-depth SSRF guard — reject a configured provider host that is a
link-local address (incl. the cloud metadata endpoint 169.254.169.254) or the
unspecified address, plus malformed hosts (userinfo, control chars, empty).
Loopback and private LAN are intentionally allowed (same-box/on-prem self-host
is legitimate and providers are operator-configured, not attacker-supplied);
this is not full DNS-rebinding protection.

Tests for all three.

Refs: ADVERSARIAL_REVIEW_2026-06-25.md AU2/AU5/AU4

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@russellromney russellromney merged commit 34ec175 into main Jun 26, 2026
17 of 19 checks passed
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