Repository navigation
Auth: read the Authorization scheme in any case (BASIC is a credential) - #864
Conversation
…source Authorization schemes are case-insensitive (RFC 9110 §11.1), but the Basic source read only `Basic ` and `basic `: `Authorization: BASIC <credentials>` was no credential to the middleware, so behind a trusted-header realm's proxy the headers named the caller instead of the request's own password being checked. Add forge_core::auth::scheme, one helper that reads an Authorization value's credentials for a scheme in any case, and use it in the Basic source, the workload-identity source, the token exchange, the npm Bearer token on feeds and the registry's Bearer token, instead of their copies. Fixes #584 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012e6DJbqjKPPeknBMf5kEFg
|
The merge queue dropped this PR on a Check: merge-group run 41711313673, job Why it isn't this PR's: the test and the code it covers ( Cause (a race): Fix: none exists yet. I've left it out of this PR to keep it to #584. A fix would have This PR needs re-queuing; it's green on its head. Generated by Claude Code |
Fixes #584
What
Authorization schemes are case-insensitive (RFC 9110 §11.1). The Basic source in
crates/core/src/auth/token.rsread onlyBasicandbasic, soAuthorization: BASIC <credentials>was no credential to the middleware. Behind a trusted-header realm's proxy the headers then named the caller instead of the request's own password being checked. A wrong password ran as the network's account instead of getting a 401.How
New
forge_core::auth::schemewith one helper.credentials(value, scheme)returns the trimmed credentials when the scheme matches in any case.header_credentials(headers, scheme)reads them from the request and returnsNonewhen they are empty.The Basic source (
parse_basicand its scheme check) now uses it.The other readers switch from their own copies to it. These were already case-insensitive, so their behaviour does not change:
wif.rs, whose localstrip_prefix_ciis removed)bearer_tokenplugins.rs)bearer_token(images/dispatch.rs)I grepped all of
crates/, includingcrates/formats/*, for the other"Basic "/"Bearer "matches. None reads a request's credentials:WWW-Authenticatechallenges Forge sends;images/proxy.rs, already case-insensitive;crates/e2e/src/mock_basic_upstream.rs,crates/server/tests/client_passwords.rs).I left all of these unchanged.
Tests
auth::schemeunit tests:Basic/basic/BASIC/bAsIcandBearer/BEARER/bEaReRare read; another scheme or a scheme alone is not.token.rs:parse_basicreadsBASIC/bAsIc.BasicAuthSourcetreatsBASIC …/bAsIc …as its own credential, so an unreadable one isInvalidand neverNoCredentials.images/dispatch.rs:bearer_tokenreadsbearer/BEARER/bEaReR.crates/server/tests/trusted_header.rs,basic_in_another_case_is_the_requests_own_credential: with a trusted-header realm,BASIC/bAsIcwith a wrong password gets 401invalid credentials, not the account the headers name. With the right password it runs as the request's own account. Against the oldtoken.rsthis test fails:BASIC <wrong password>returned 200 as the headers' account.Checks run locally:
cargo fmt --all --checkcargo clippy -p forge-core -p forge-server --all-targets -- -D warningscargo test -p forge-core --lib auth::: 112 passedcargo test -p forge-server --libon the auth, dispatch, plugins and principal tests: 90 passedcargo test -p forge-server --test trusted_header: 12 passedcargo xtask changelog --checkChangelog
docs/changes/2.2.0/584.fixed.md. I filed it asfixedrather thansecurity. The trusted-header case gives the caller only the account the proxy already vouched for, not more access. The bug is that the request's own wrong password is not refused.Generated by Claude Code