Repository navigation
[Security] Release transport: credential confinement across origins and redirects (PR-0A) #158
Copy link
Copy link
Closed
Labels
priority:P1Major user-facing defect or release blockerMajor user-facing defect or release blockertype:securitySecurity hardening or threat-model workSecurity hardening or threat-model work
Description
Problem
The release transport sends the provider token to any URL it is asked to fetch, and
urllib forwards Authorization across redirects — including cross-origin ones.
Concretely, today:
ReleaseApiProvider._get()attachesGITHUB_TOKEN/GH_TOKENto every request,
including an artifact URL (browser_download_url) taken from metadata served by a
customAINATIVE_UPDATE_URL. Any custom source can collect the user's GitHub token.- urllib's default redirect handler copies all request headers to the redirect
target. A302from an otherwise legitimate endpoint to any other origin leaks
the bearer token. AINATIVE_UPDATE_URLinherits provider credentials, although it is documented as
an anonymous custom source.
Source of the fix: Multi-Forge v1.3.2-final §10–14 (PR-0A), ADR-MF-03 addendum.
Expected outcome
Release transport enforces one credential rule: a provider credential is sent only
to the origin it belongs to (auth_origin), never to any other origin, never
across a redirect to another origin, and never for AINATIVE_UPDATE_URL.
Acceptance criteria
- A private urllib opener replaces urllib's default redirect forwarding; no
global urllib monkeypatch and no reliance on module-levelurlopen. - URL validation: HTTPS required; userinfo (
user@host) rejected; origins
compared normalized (scheme://host[:port], default port normalized, case
normalized). - Credential attached iff the request origin equals the endpoint's
auth_origin;auth_originis endpoint configuration, never derived from the
URL being fetched. - Metadata policy:
Authorization: Bearer+Accept: application/vnd.github+json
at the trusted GitHub API origin; same-origin metadata redirects followed;
cross-origin metadata redirects refused. - Artifact policy: bounded bytes;
302to the CDN followed anonymously with
every credential stripped; HTTPS maintained on every hop;HTTPS → HTTP
downgrade refused. - Arbitrary
browser_download_urlis never authenticated. GitHub private
release assets are fetched through the asset API URL (assets[].url) with
Accept: application/octet-streamand the bearer attached only at the API
origin. -
AINATIVE_UPDATE_URLis anonymous only: neverGITHUB_TOKEN,GH_TOKENor
GITLAB_TOKEN(documented as a breaking behavior change). - Redirect-loop bound enforced (explicit maximum hops); userinfo in a redirect
target rejected. - Oversized responses refused before parsing/returning.
- Characterization tests recorded before the production change, then security
tests: trusted GitHub API receives bearer; evil initial URL receives no
bearer;AINATIVE_UPDATE_URLreceives no bearer; userinfo URL rejected; HTTP
initial URL rejected where HTTPS is required; HTTPS→HTTP redirect rejected;
same-origin metadata redirect followed; cross-origin metadata redirect
rejected; asset API → CDN strips bearer; redirect loop rejected; oversized
response rejected. - Gate: zero provider credentials observed at non-approved origins across the
full metadata + artifact flow. - Existing lifecycle tests green; GitHub public release behavior preserved;
GitHub private asset fixture green; custom source behavior documented
(CHANGELOG security/breaking + update documentation).
Out of scope
release-providers.json, named providers, selector conflict policy (PR-4A per
ADR-MF-03).- GitLab provider (PR-5).
- Universal HTTP client, generic Forge SDK, unrelated provider redesign.
- Repository-local provider trust configuration.
Context
ainative/lifecycle/provider.py(ReleaseApiProvider._get,fetch,_select_asset).- ADR-0009 §6 — authority commands never depend on remote answers.
- Issue [Security] Add cryptographically verifiable release artifacts #24 (cryptographic release authority) remains independent.
Dependencies
- None. Blocks PR-4 ([Feature] Release V3: source resolver, manifest, GitHub + Local providers (PR-4) #165) and the ADR-MF-03 acceptance path.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
priority:P1Major user-facing defect or release blockerMajor user-facing defect or release blockertype:securitySecurity hardening or threat-model workSecurity hardening or threat-model work
Activity
Rwanbt commentedon Sep 16, 2026
Claiming this Issue for implementation.
fix(lifecycle): confine release credentials to the configured origin (#…
Rwanbt commentedon Sep 16, 2026
Reopened: this work was merged into
mainby mistake.mainhas been restored to its pre-merge state (9301253); the tested work now lives ondev(merge commit 0721237). The Issue stays open until the finaldev->mainpromotion, once the whole plan is implemented and tested.feat: Multi-Forge v1 — production promotion (dev -> main) (#176)
Rwanbt commentedon Sep 17, 2026
Done: the work is merged into
main(squash 8f319ad via PR #176, full CI matrix green including the Production Gate). Closed as completed.