fix: harden OCI Bearer authentication against SSRF and credential forwarding - #1276
Open
Kaniska (Kaniska244) wants to merge 6 commits into
Open
fix: harden OCI Bearer authentication against SSRF and credential forwarding#1276Kaniska (Kaniska244) wants to merge 6 commits into
Kaniska (Kaniska244) wants to merge 6 commits into
Conversation
Kaniska (Kaniska244)
marked this pull request as ready for review
August 11, 2026 09:28
There was a problem hiding this comment.
Pull request overview
This PR hardens OCI Feature/Template registry authentication by validating Bearer realm challenges, restricting credential forwarding, preventing redirects during token exchange, encoding challenge parameters, and tightening registry authority parsing to mitigate SSRF and credential leakage.
Changes:
- Added Bearer token realm admission rules and separated realm admission from credential-forwarding rules (including Docker Hub/GHCR/AzureCR allowances).
- Disabled redirects specifically for Bearer token endpoint requests and rebuilt anonymous fallback requests to avoid replaying credentials.
- Added registry authority syntax validation and restored legacy single-label Feature ID behavior to avoid unintended OCI probing.
Show a summary per file
| File | Description |
|---|---|
| src/spec-configuration/httpOCIRegistry.ts | Adds Bearer realm validation, credential-forwarding policy, redirect-free token requests, and safer token request construction. |
| src/spec-utils/httpRequest.ts | Introduces a no-redirect request helper for token endpoints via configurable redirect limits. |
| src/spec-configuration/containerCollectionsOCI.ts | Validates registry authority syntax and preserves legacy single-label Feature fallback while allowing explicit localhost/IP registries. |
| src/test/httpOCIRegistry.test.ts | Adds focused tests for realm admission, credential forwarding, redirect blocking, and query encoding. |
| src/test/container-features/containerFeaturesOCI.test.ts | Extends tests for registry parsing/validation and legacy single-label behavior. |
| .github/workflows/dev-containers.yml | Runs the new focused OCI auth test suite exactly once by adding it to the matrix and excluding it from the catch-all shard. |
Review details
Tip
Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- Files reviewed: 6/6 changed files
- Comments generated: 2
- Review effort level: Lite
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
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.
Summary
This PR hardens OCI Feature and Template authentication against registry-controlled Bearer token endpoints. The origin of this PR is this security issue.
Previously, an OCI registry could return a
WWW-Authenticate: Bearerchallenge containing an arbitraryrealm. The CLI would request that URL without validating its scheme or authority, potentially:serviceandscopevalues to inject additional query parameters or fragments.This PR validates Bearer realms before credential lookup or token endpoint I/O, restricts credential forwarding separately from endpoint admission, disables token-request redirects, encodes challenge parameters, and validates OCI registry authority syntax.
Security classification
The vulnerable flow can be reached through Feature or Template identifiers supplied by an untrusted workspace's
devcontainer.json.Threat model
A victim opens or builds an untrusted repository containing an attacker-selected OCI Feature or Template reference.
The Dev Container CLI may run directly through commands such as
devcontainer upanddevcontainer build, or transitively through the VS Code Dev Containers extension, Codespaces, or prebuild tooling.The attacker controls the registry response and its
WWW-Authenticatechallenge. Before this change, that challenge could direct the victim's machine to an arbitrary Bearer token endpoint.Changes
Validate Bearer token realms
requestEnsureAuthenticated()now validates the challengerealmbefore:Malformed, relative, or disallowed realms terminate authentication without contacting the token endpoint.
The following realm policy is applied:
localhostauthority and matching porthttps://auth.docker.iowithout a custom porthttps://ghcr.iowithout a custom port*.azurecr.ioHTTPS authority without a custom portlocalhostportauth.docker.io.attacker.exampleAuthority comparisons are case-insensitive, and ports remain part of exact-authority matching.
Separate endpoint admission from credential forwarding
Token endpoint admission and credential forwarding are intentionally separate policies.
An endpoint may be safe to contact anonymously without being allowed to receive credentials stored for the registry. This prevents an attacker-controlled registry from naming a known token service and causing credentials for the attacker registry to be forwarded there.
Basic credentials and refresh tokens may be sent only when:
localhost, including the same port.Docker Hub requires a compatibility exception because its registries authenticate through a separate authority:
docker.io,registry.docker.io, orregistry-1.docker.iotohttps://auth.docker.io.When credentials cannot be forwarded, the CLI logs a warning and attempts anonymous token acquisition.
Rebuild anonymous fallback requests
A credentialed token request that receives
401or403is retried anonymously.The anonymous retry is constructed as a fresh GET request rather than reusing the credentialed request. This ensures that neither of the following can be replayed:
Authorizationheader.Anonymous requests are not redundantly retried after
401or403.Disable redirects for Bearer token requests
A dedicated
requestResolveHeadersNoRedirects()wrapper now sends Bearer token requests withmaxRedirects: 0.This applies to:
This prevents an accepted token endpoint from redirecting the request to another authority.
Normal HTTP operations retain their existing redirect behavior. Redirect suppression is limited to token requests whose validated authority forms a security boundary.
Encode
serviceandscopeBearer token GET requests now use
URLandURL.searchParams.set()instead of interpolating challenge values into a URL string.This:
serviceandscope.serviceandscopeparameters deterministically.Refresh-token POST bodies continue to use
URLSearchParams.Validate registry authority syntax
getRef()andgetCollectionRef()now validate registry authority syntax before constructing an OCI reference.Accepted registry forms include:
1through65535.localhost:5000.Rejected forms include:
https://.user@registry.-.This validation checks authority shape only. It does not impose a global network trust policy on user-configured registries.
Preserve legacy Feature resolution
The original implementation contained an early check that prevented single-label identifiers from being resolved as OCI registries. Removing it caused legacy Feature identifiers such as:
to trigger a request to:
This caused existing tests and configurations to fail with errors such as:
The compatibility behavior has therefore been restored at manifest resolution time.
Single-label references continue to fall back to the legacy GitHub Releases Feature flow, except for explicitly supported local or IP-literal registries:
localhostThe authority parser can still validate single-label authorities, but OCI manifest probing preserves the historical interpretation of ambiguous three-segment Feature identifiers.
Remove the MCR substring workaround
The previous special case:
has been removed.
Substring matching is not a valid security boundary and could also match attacker-controlled hosts such as:
MCR realms are now governed by the same parsed URL, protocol, authority, redirect, and credential-forwarding policies as other registries.
Deviations from the original remediation
Exact-authority localhost HTTP remains supported
The original recommendation proposed rejecting every non-HTTPS realm and requiring an explicit opt-in for localhost.
This PR retains HTTP only when:
localhost.This preserves established local Feature and Template development workflows using registries such as:
The exception does not permit:
Local, private, and IP-literal registries remain supported
The original recommendation proposed rejecting loopback, private-network, and IP-literal registries at the source.
This PR intentionally performs syntax validation rather than globally blocking those registries because the CLI supports:
Rejecting these authorities globally would be a breaking policy change. It would also not completely prevent DNS-based SSRF without resolving addresses and enforcing network policy across every connection and redirect hop.
The fix is instead applied at the vulnerable secondary-request boundary: a registry-controlled Bearer challenge cannot pivot token acquisition to an arbitrary internal or attacker-selected authority.
No
--allow-insecure-registryoption was introducedThe original recommendation suggested adding an explicit insecure-registry option.
This PR does not add a new CLI configuration surface. It preserves the existing exact-authority localhost workflow while rejecting remote HTTP Bearer realms.
A broader insecure-registry policy can be considered separately if explicit configuration becomes necessary.
Known token services remain available
The realm policy allows standard HTTPS endpoints used by Docker Hub, GHCR, and Azure Container Registry:
auth.docker.ioghcr.ioazurecr.ioThis follows the compatibility allowance in the original recommendation.
These endpoints may receive anonymous token requests, but credential forwarding is evaluated independently. An attacker-controlled registry therefore cannot use the endpoint allow-list to send its stored credentials to one of these services.
Registry validation does not reject every single-label authority
The authority validator accepts syntactically valid single-label names because they can represent valid local or enterprise authorities.
However, during Feature manifest probing, ambiguous single-label identifiers continue to use the historical legacy GitHub Releases fallback. This avoids breaking existing Feature references such as
codspace/myfeatures/helloworld.Workspace trust behavior is unchanged
The original report suggested considering an explicit workspace-trust prompt before resolving Features from non-default registries.
This PR does not change workspace trust or introduce interactive prompts. Such a change would affect broader product behavior beyond OCI authentication and should be evaluated independently.
Tests
A focused OCI authentication suite was added covering:
serviceandscope.OCI reference tests were extended to cover:
localhostregistries.codspace/myfeatures/helloworld.CI integration
src/test/httpOCIRegistry.test.tshas a dedicated entry in the GitHub Actions test matrix.It is excluded from the catch-all test entry so it runs exactly once and receives an independent matrix result.
The existing parser and legacy Feature regression tests continue to run through the catch-all test shard.
Validation
Local validation completed successfully:
yarn type-checkyarn lintyarn packageFiles changed
src/spec-configuration/httpOCIRegistry.tssrc/spec-utils/httpRequest.tssrc/spec-configuration/containerCollectionsOCI.tssrc/test/httpOCIRegistry.test.tssrc/test/container-features/containerFeaturesOCI.test.ts.github/workflows/dev-containers.ymlSecurity outcome
After this change, a malicious OCI registry cannot use its Bearer challenge to:
serviceorscope.The CLI continues to support legitimate local, private, Docker Hub, GHCR, Azure Container Registry, and legacy Feature workflows.
References