Files
buzz/crates/buzz-core
Duncan 5b1b2bf5bc fix(buzz-auth): Arc-shared verifier, IPv6 host normalization, fec0::/10 block
Four production-blocking defects identified in Carl's Pass 1 review, plus the
fourth issue (policy ID / JWKS config fields) reported as a contract question
requiring design decision before implementation:

1. Arc<S> forwarding IssuerKeySource impl: FederatedAssertionVerifier<S> consumed
   S by value, making ProductionJwksSource (not Clone) non-shareable across
   verifiers. Add sealed::Sealed blanket for Arc<S> and IssuerKeySource for
   Arc<S>, forwarding to the inner source. Shared-rotation regression test proves
   one long-lived verifier observes key rotation through a shared Arc cache; the
   mutation (no sharing) turns the test red.

2. IPv6 host normalization: fetch_jwks_inner called Url::host_str() which returns
   bracket-wrapped IPv6 literals (e.g. '[2606:4700::1]'). IpAddr::parse fails on
   bracketed form, falling to DNS; reqwest resolve() keyed on bracketed host
   doesn't match the URL authority, bypassing DNS pinning. Fix: use typed
   Url::host() and stringify Ipv6Addr without brackets. Tests verify loopback
   and fec0::1 site-local URIs are rejected as InvalidUri via the correct
   SSRF-check path.

3. fec0::/10 deprecated site-local: is_not_global_unicast didn't block fec0::/10
   (RFC 3879 deprecated IPv6 site-local). Add the predicate and table-driven tests
   confirming fec0::1 through feff::1 are blocked; verify boundary addresses.
   All three existing callers (JWKS, webhook, link-preview) inherit the stricter
   predicate by construction.

Issue 4 (IssuerPolicy::id() missing JWKS endpoint/timing fields): IssuerJwksConfig
fields live outside IssuerPolicy; folding them into the existing id() would change
the settled identity contract. Two minimal options reported to Paul for product
decision before implementing.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
2026-08-31 20:13:13 -04:00
..