Skip to content

feat(oci): support OCI auth hardening from reference v0.89.0 - #24

Merged
aledbf merged 4 commits into
mainfrom
feat/reference-v0.89.0-oci-auth-hardening
Sep 21, 2026
Merged

aledbf merged 4 commits into
mainfrom
feat/reference-v0.89.0-oci-auth-hardening

Conversation

@aledbf

@aledbf aledbf commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Bumps the pinned reference CLI from v0.88.0 to v0.89.0 and implements the observable surface that release added (devcontainers/cli#1278).

What changed

New global flags --oci-auth-hardening and --allow-cross-origin-auth-host, with the oracle's validation: the allow list requires hardening, and each entry is a <registry-host>=<auth-host> pair of bare authorities. Error messages and exit codes match the reference verbatim.

New output field ociAuthDiagnostics on up, build and read-configuration, reporting the three compatibility flags so the hardening's impact can be measured before enabling it.

Hardening behavior: bearer realms pinned to the registry authority or a trusted auth host (including the built-in Docker Hub and GitLab mappings), and token endpoints refused a redirect.

Two smaller v0.89.0 changes: scheme on the feature ref in read-configuration output, and the generated feature Dockerfiles defaulting the base-image ARG to scratch instead of placeholder.

The policy is built once per invocation and carried on the command context, so every OCI client of a command shares its settings and feeds the same diagnostics. exec parses its own flags (DisableFlagParsing) and therefore applies the validation itself; features test re-invokes this binary and forwards the flags to the up it spawns.

Divergence

The hardening is enforced in a transport above oras-go, which already refuses to forward credentials to a cross-origin challenge (GHSA-vh4v-2xq2-g5cg). Hardening off is therefore already stricter here than upstream. Recorded in docs/DIVERGENCES.md; the diagnostics still report what hardening would change, so the flag remains a faithful compatibility probe.

Test-gap fix

TestOracleFlagCoverage only inspected per-command options, which is why the two new global flags slipped through. It now checks the oracle's global options too, and TestFlagInventoryParity pins them to the root command.

TestResolveBuildAuthFromOCIEnvAndGitHubToken read the developer's real ~/.docker/config.json and asserted against whatever the host was logged into (printing that credential on failure). Fixed in its own commit.

Validation

Command Result
task lint, task build, task reference pass
task test:race pass
task spec:compliance pass
task parity:contract 73 matched, 0 failed (was 68)
task parity:semantic 0 failed (was 1)
task parity:network 13 matched, 0 failed (was 1 failed)
task parity:publish, task test:integration pass
task vuln no vulnerabilities
task test:e2e pass
task parity:runtime 181 matched, 0 failed (was 67 failed)

Five new parity cases cover the flags end to end; new unit tests cover the parser, the realm policy, each diagnostic, the hardening refusals, an end-to-end fetch through the real client against a fake registry, and the CLI-level flag validation, output field, context plumbing and flag forwarding.

Two environment notes from the local run, neither related to this change: the runtime lane still reports 13 inconclusive compose cases where both CLIs fail identically (the local daemon's runc cannot start a container joining another container's network namespace), and the e2e plus a few runtime cases need BUILDX_BUILDER=default on a host whose default buildx builder uses the docker-container driver.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

aledbf and others added 4 commits September 20, 2026 21:40
ResolveBuildAuth resolves credentials through the docker config and, when
that file is missing, through the platform credential helper. The tests
passed no DOCKER_CONFIG, so on a developer machine they resolved the real
ghcr.io/docker.io credentials, asserted against whatever the host was
logged into, and printed that credential in the failure message.

Point them at a DOCKER_CONFIG holding credentials for an unrelated
registry: present, so the platform-helper fallback stays out, and
irrelevant to every registry under test.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Bump the pinned reference CLI to v0.89.0 and implement the observable
surface it added (PR devcontainers/cli#1278):

- `--oci-auth-hardening` and `--allow-cross-origin-auth-host` as global
  flags, with the same validation the oracle applies: the allow list
  requires hardening, and each entry is a '<registry-host>=<auth-host>'
  pair of bare authorities.
- `ociAuthDiagnostics` in the `up`, `build` and `read-configuration`
  output, reporting what hardening would change.
- Bearer realms pinned to the registry authority or a trusted auth host
  (including the built-in Docker Hub and GitLab mappings), and token
  endpoints refused a redirect, when hardening is on.
- `scheme` on the feature ref in `read-configuration` output, and the
  generated feature Dockerfiles defaulting the base-image ARG to
  `scratch` rather than `placeholder`.

The policy is built once per invocation and carried on the command
context, so every OCI client of a command shares its settings and feeds
the same diagnostics. `exec` parses its own flags and therefore applies
the validation itself; `features test` re-invokes this binary and
forwards the flags to the `up` it spawns.

The hardening is enforced in a transport above oras-go, which already
refuses to forward credentials to a cross-origin challenge. That makes
hardening-off stricter here than upstream; the divergence is documented
and the diagnostics still report what hardening would change.

TestOracleFlagCoverage only inspected per-command options, so the two
new global flags went unnoticed; it now checks the oracle's global
options as well, and TestFlagInventoryParity pins them to the root
command.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CodeQL reported "Workflow does not contain permissions" for every job of
go-cli.yml except lint-and-test: without a `permissions` block the token
inherits the repository default, which may include write scopes the jobs
never need.

Declare `contents: read` once at the workflow level. Every job only checks
out the repo and reads the API (setup-task's repo-token), and artifact
upload/download uses the Actions runtime token, so read is sufficient; a
future job that needs more widens it for itself.

TestWorkflowsDeclarePermissions pins the rule for all workflows (top-level
or per-job), and TestGoCLIWorkflowIsReadOnlyByDefault pins this grant, so a
job added later cannot silently inherit write access. Both fail without the
block: the first names all seven unprotected jobs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@aledbf
aledbf merged commit fdb9e71 into main Sep 21, 2026
15 checks passed
@aledbf
aledbf deleted the feat/reference-v0.89.0-oci-auth-hardening branch September 21, 2026 01:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant