fix(coreapi): assert token_exchange_url path in validateExchangeURL · Entire
fix(coreapi): assert token_exchange_url path in validateExchangeURL
4a937e7→main·
toothbrush·4w ago·2 files·+10 added/-0 removed
exchangeSubjectToken strips oauthTokenPath and PostOAuthToken re-appends it, but validateExchangeURL only gated scheme and host. A hint whose path wasn't /oauth/token would make the strip a no-op and POST to a server-chosen path. Validate the path so the round-trip is honest.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
ea9e8dae6384View transcript
[?
Review PR Comments on Cross-Jurisdiction RoutingClaude Code·Opus 4.8[1m]·2 steps](/content/gh/entireio/cli/session/2d6d6a51-951b-44ac-be1b-df81b4957e30#timeline-ea9e8dae6384/index.html)
Changes
2
internal/coreapi
Mcross_juris_transport.go+7
Mcross_juris_transport_test.go+3
338 unmodified lines
339
340
341
342
343
344
345
346
347
348
11 unmodified lines
360
361
362
363
364
365
366
367
368
338 unmodified lines
// core's /oauth/token always lives on the same origin as its
// middleware; off-origin hints have no legitimate use and would
// let a misconfigured / hostile core exfiltrate the user's JWT.
// - Path must be exactly oauthTokenPath. exchangeSubjectToken strips
// that suffix and PostOAuthToken re-appends it; validating it here
// keeps that round-trip honest instead of silently POSTing to a
// server-chosen path.
func validateExchangeURL(raw string, requestURL *url.URL) error {
if raw == "" {
return errors.New("token_exchange_url missing")
11 unmodified lines
if exchange.Host != requestURL.Host {
return fmt.Errorf("token_exchange_url host %q must match response host %q", exchange.Host, requestURL.Host)
}
if exchange.Path != oauthTokenPath {
return fmt.Errorf("token_exchange_url path %q must be %q", exchange.Path, oauthTokenPath)
}
return nil
}
Minternal/coreapi/cross_juris_transport.go+7
415 unmodified lines
416
417
418
419
420
421
422
423
424
415 unmodified lines
if err := validateExchangeURL("http://localhost:1234/oauth/token", mustParse("http://localhost:1234/api")); err != nil {
t.Errorf("loopback http must pass: %v", err)
}
if err := validateExchangeURL("https://example.test/auth/x", mustParse("https://example.test/api")); err == nil {
t.Error("non-/oauth/token path must be refused")
}
}// TestRoundTripper_TokenCacheReusesExchanged confirms the per-origin