Repository navigation
row 74 F25: a base URL's or token endpoint's userinfo rides as Authorization: Basic where 1.5.5 sent it; no plane receives it - #913
Open
MattJackson wants to merge 3 commits into
Conversation
…exactly where 1.5.5 sent it ARCHITECT ruling on #876: 1.5.5 wins. 1.5.5 built every provider request with reqwest over `{base_url}{path}` and its credential after (v1.5.5 crates/busbar/src/proxy/engine/mod.rs:1669-1670, proxy/engine/walk.rs:583-584, health.rs:313-314). reqwest 0.12.28's RequestBuilder::new (src/async_impl/request.rs:169-182) took the userinfo out (extract_authority :581-605, percent- decoded) and added it FIRST as a sensitive `authorization: Basic` (src/util.rs:4-25); `.headers(auth)` (replace_headers, src/util.rs:50-77) replaced it only when the credential named `authorization`. - busbar-kernel egress engine: UrlUserinfo, the Basic value a base URL's userinfo spells, opaque (no accessor outside the kernel, Debug is a marker, sensitive). present_url_userinfo, run by the owned client's send, puts it FIRST unless the request names `authorization`, and takes the extension off either way. - appbuild seats it on LaneInput (url_userinfo); the lane plane hands it back to the kernel hop as an opaque request extension on organic sends and probes. The plane never reads its bytes. - door path: ProviderRoute and MemberRoute carry it; far_end puts it first in the attempt head when the auth binding's fields name no `authorization`, and a plane field named `authorization` never doubles it. REDs: kernel unit cases for the reqwest decode/emptiness arms; the hop's order and condition; an h1 wire test through the owned client (x-api-key credential: Basic first; authorization credential: none); far_end (x-api-key binding: Basic first; Bearer binding: none); the lane's organic forward with a keyless lane hears the Basic at the stripped host; the probe with an authorization credential hears it alone.
…Authorization: Basic, as 1.5.5 did ARCHITECT ruling on #876 (2): 1.5.5 POSTed the client credentials with reqwest to token_url as written (v1.5.5 crates/busbar/src/egress_auth/oauth_client_credentials.rs:119-126, `.post(&self.token_url).form(..)`); reqwest 0.12.28's builder took a `user:pass@` out of the URL and sent it FIRST as `authorization: Basic` (src/async_impl/request.rs:169-182, extract_authority :581-605, util::basic_auth src/util.rs:4-25), and the form named no authorization, so it always rode. 1.6.0 refused it (the connector's carries_userinfo on the mint need's pinned target). - root OutboundAuths::serving (the one choke point both the door path and the in-process lane path bind through): a settings `token_url` spelling userinfo is stripped before the minting plugin's instance is opened (its need is pinned to, and its mint dials, the stripped endpoint) and before its binding's open_outbound (EndpointStripped); the instance is loaded over a connection table (PresentingConns) that leads every open naming no `authorization` with the endpoint's Basic value (kernel UrlUserinfo::leading). The instance key stays the settings as written, so endpoints differing only in userinfo never share an instance. - config_validate: the token_url refusals repeat it redacted (literals unchanged); the credential dry-run hands the plugin the stripped endpoint. - config sections destinations(): the carve-out keys on the stripped token_url too. REDs: the oauth member test runs over a plain and a userinfo token_url: the need is declared and the mint dialled at the stripped endpoint, the userinfo's mints lead with the Basic value, the plain one's carry no authorization; the config_validate planted scan covers the token_url refusals.
…des the mint as 1.5.5 did ARCHITECT ruling (stack on #876): (i) The in-process lane plane is handed token_url with its userinfo stripped (appbuild lane_token_endpoint); the Basic value it spelled rides beside it opaque (LaneInput.token_userinfo, kernel UrlUserinfo), handed back through kernel bound_credential::bind_presenting to the auth axis's new AuthAxis::serving_presenting (contract default: not presented), which the root implements over OutboundAuths::serving_with: the minting instance is loaded over the presenting connection table, so the token POST leads with `authorization: Basic` exactly as 1.5.5's reqwest POST to token_url did (v1.5.5 crates/busbar/src/egress_auth/oauth_client_credentials.rs:119-126). (ii) jwt-bearer: 1.5.5 POSTed the signed assertion with reqwest to the service account's token_uri as written (v1.5.5 crates/busbar/src/egress_auth/jwt_bearer.rs:175-181, `.post(&self.token_uri) .form(..)`), so the same userinfo -> Basic rule held (reqwest 0.12.28 request.rs:169-182, extract_authority :581-605, util.rs:4-25; the form names no authorization, so it always rode). The root's settings reader now covers token_uri beside token_url: the mint need is pinned to the stripped endpoint, the mint's target is stripped and led by the Basic value; the assertion's aud stays the plugin's own reading of the service account, as 1.5.5's did. vet_token_uri's refusals repeat it redacted (literals unchanged). (iii) Nothing wider: base_url, token_url and token_uri are the operator URLs 1.5.5 sent through that reqwest rule on these paths. REDs: kernel: the endpoint a plane is handed carries no userinfo and the axis receives the Basic beside the settings, never inside them; root: a service account token_uri spelling a userinfo seals, pins and dials the stripped endpoint and leads each mint with the Basic; the in-process shape (axis handed stripped settings plus the held Basic) leads the token POST with the Basic.
Collaborator
Author
|
FYI (queue rule 20): $ draft #870 also edits crates/busbar-kernel/src/config_validate/{mod.rs:1553 rate-refusal message, tests/tests.rs:4178-4216 rate test}; hunks are disjoint from yours and merge clean. |
This was referenced Oct 14, 2026
Collaborator
Author
|
row 155 B2 (#974) edits crates/busbar-kernel/src/bound_credential.rs at DeclaredBuilder::headers_for; disjoint from this PR's hunks. |
This was referenced Oct 14, 2026
Collaborator
Author
|
Rule 20 note from #1031 (row 155, draft): disjoint hunks in root/door_steps.rs (kernel_sections limits) and root/tests/door_steps.rs (one test appended). A test merge is clean. |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #876 (base
lane-row74-userinfo). That PR strips the userinfo; this one adds the ARCHITECT ruling on top: 1.5.5 wins onAuthorization: Basic, and a plane never receives a URL that carries userinfo.1.5.5 citations
{base_url}{path}, with the credential added after. See v1.5.5crates/busbar/src/proxy/engine/mod.rs:1669-1670,proxy/engine/walk.rs:583-584andhealth.rs:313-314.crates/busbar/src/egress_auth/oauth_client_credentials.rs:119-126,.post(&self.token_url).form(..).crates/busbar/src/egress_auth/jwt_bearer.rs:175-181,.post(&self.token_uri).form(..).RequestBuilder::new(src/async_impl/request.rs:169-182) takes the userinfo out (extract_authority,:581-605, percent-decoded) and adds it FIRST as a sensitiveauthorization: Basic(src/util.rs:4-25)..headers(auth)(replace_headers,src/util.rs:50-77) replaces it only when the credential is itself namedauthorization. A form POST has noauthorizationfield, so the mints always carried it.What
UrlUserinfo, an opaque value that never prints. Both send paths put it first unless the request already hasauthorization:far_end.OutboundAuths::serving_withstrips the URL before the auth plugin opens and before its binding. The plugin's connection table is wrapped so each mint leads with the Basic value.LaneInput.token_url). The Basic value goes alongside asLaneInput.token_userinfo, through the kernel'sbind_presenting, to the newAuthAxis::serving_presenting.audstays the plugin's own reading of the service account, as in 1.5.5.REDs
x-api-keycredential gets Basic first; anauthorizationcredential gets none.far_end: anx-api-keybinding gets Basic first; a Bearer binding gets none.authorizationcredential carries the credential alone.svcuserorhunter2.