Follow OCI distribution spec for artifactType and annotations#2269
Merged
Subserial merged 2 commits intoApr 20, 2026
Conversation
Per the OCI distribution spec (distribution-spec#395), the artifactType on a descriptor should be set to the manifest's artifactType if present, falling back to config.mediaType unconditionally (not only for non-standard config types as was done previously). Additionally, annotations from the manifest are now copied to the descriptor. Current wording from the spec: > Pushing Manifests with Subject > > When processing a request for an image manifest with the subject field, > a registry implementation that supports the referrers API MUST respond > with the response header OCI-Subject: <subject digest> to indicate to > the client that the registry processed the request's subject. > > When pushing a manifest with the subject field and the OCI-Subject header was not set, the client MUST: > 1. Pull the current referrers list using the referrers tag schema. > 2. If that pull returns a manifest other than the expected image index, the client SHOULD report a failure and skip the remaining steps. > 3. If the tag returns a 404, the client MUST begin with an empty image index. > 4. Verify the descriptor for the manifest is not already in the referrers list (duplicate entries SHOULD NOT be created). > 5. Append a descriptor for the pushed manifest to the manifests in the referrers list. The value of the artifactType MUST be set to the artifactType value in the pushed manifest, if present. If the artifactType is empty or missing in a pushed image manifest, the value of artifactType MUST be set to the config descriptor mediaType value. All annotations from the pushed manifest MUST be copied to this descriptor. > 6. Push the updated referrers list using the same referrers tag schema. The client MAY use conditional HTTP requests to prevent overwriting a referrers list that has changed since it was first pulled. With this change, we correctly follow step 5. Changes: - Add ArtifactType field to v1.Manifest and v1.IndexManifest structs - Update fetchManifest to prefer manifest artifactType, fall back to config.mediaType, and copy manifest annotations to the descriptor - Apply the same artifactType logic in remote/index.go, remote/write.go, and partial/with.go
Test the OCI distribution spec behavior for artifactType resolution and annotation copying: - TestGet_ArtifactTypeAndAnnotations: end-to-end test via Get() covering fallback to config.mediaType, explicit artifactType precedence, and annotation copying to the descriptor - TestGet_NonManifestMediaType: unparseable manifests produce no artifactType or annotations - TestArtifactType_Fallback: partial.ArtifactType falls back to config.mediaType for standard, OCI, and custom config types - TestDescriptor_ArtifactType_Fallback: partial.Descriptor does the same - TestArtifactType_ExplicitArtifactType: explicit manifest artifactType takes precedence over config.mediaType - TestArtifactType_NilManifest: nil manifest returns empty string
Subserial
approved these changes
Apr 20, 2026
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #2269 +/- ##
===========================================
- Coverage 71.67% 53.01% -18.67%
===========================================
Files 123 165 +42
Lines 9935 11226 +1291
===========================================
- Hits 7121 5951 -1170
- Misses 2115 4556 +2441
- Partials 699 719 +20 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Subserial
pushed a commit
to Subserial/go-containerregistry
that referenced
this pull request
May 15, 2026
…#2269) * Follow OCI distribution spec for artifactType and annotations Per the OCI distribution spec (distribution-spec#395), the artifactType on a descriptor should be set to the manifest's artifactType if present, falling back to config.mediaType unconditionally (not only for non-standard config types as was done previously). Additionally, annotations from the manifest are now copied to the descriptor. Current wording from the spec: > Pushing Manifests with Subject > > When processing a request for an image manifest with the subject field, > a registry implementation that supports the referrers API MUST respond > with the response header OCI-Subject: <subject digest> to indicate to > the client that the registry processed the request's subject. > > When pushing a manifest with the subject field and the OCI-Subject header was not set, the client MUST: > 1. Pull the current referrers list using the referrers tag schema. > 2. If that pull returns a manifest other than the expected image index, the client SHOULD report a failure and skip the remaining steps. > 3. If the tag returns a 404, the client MUST begin with an empty image index. > 4. Verify the descriptor for the manifest is not already in the referrers list (duplicate entries SHOULD NOT be created). > 5. Append a descriptor for the pushed manifest to the manifests in the referrers list. The value of the artifactType MUST be set to the artifactType value in the pushed manifest, if present. If the artifactType is empty or missing in a pushed image manifest, the value of artifactType MUST be set to the config descriptor mediaType value. All annotations from the pushed manifest MUST be copied to this descriptor. > 6. Push the updated referrers list using the same referrers tag schema. The client MAY use conditional HTTP requests to prevent overwriting a referrers list that has changed since it was first pulled. With this change, we correctly follow step 5. Changes: - Add ArtifactType field to v1.Manifest and v1.IndexManifest structs - Update fetchManifest to prefer manifest artifactType, fall back to config.mediaType, and copy manifest annotations to the descriptor - Apply the same artifactType logic in remote/index.go, remote/write.go, and partial/with.go * Add tests for artifactType and annotation handling Test the OCI distribution spec behavior for artifactType resolution and annotation copying: - TestGet_ArtifactTypeAndAnnotations: end-to-end test via Get() covering fallback to config.mediaType, explicit artifactType precedence, and annotation copying to the descriptor - TestGet_NonManifestMediaType: unparseable manifests produce no artifactType or annotations - TestArtifactType_Fallback: partial.ArtifactType falls back to config.mediaType for standard, OCI, and custom config types - TestDescriptor_ArtifactType_Fallback: partial.Descriptor does the same - TestArtifactType_ExplicitArtifactType: explicit manifest artifactType takes precedence over config.mediaType - TestArtifactType_NilManifest: nil manifest returns empty string
This was referenced May 19, 2026
Closed
jimmidyson
added a commit
to mesosphere/mindthegap
that referenced
this pull request
May 19, 2026
Bump google/go-containerregistry from v0.21.5 to v0.21.6 to include google/go-containerregistry#2302 (transport: allow bearer realm at same host:port as registry). The realm-URL validation introduced in go-containerregistry#2243 (shipped in v0.21.5) rejects realms whose host resolves to a private, loopback, or link-local IP. This is the right default for the cross-host SSRF case (a malicious registry pointing the token endpoint at 169.254.169.254 or a sister internal service), but it breaks `mindthegap push bundle` (and downstream `nkp push bundle`) against on-prem registries that colocate the registry and bearer- token endpoint on the same private IP. e.g. for an internal Harbor at https://10.162.182.23:5000/library the push aborts with: invalid realm in www-authenticate: realm host "10.162.182.23" is a private or link-local address v0.21.6 keeps the cross-host SSRF block but adds a same-host:port exception: when the realm URL host AND port match the registry, the private-IP check is skipped. The realm cannot escape a trust boundary the user already crossed by passing the registry reference. The transitive bumps (docker/cli, moby/moby, docker/go-connections, klauspost/compress, golang.org/x/{crypto,mod,net,sys,term,text, tools}) are pulled in by go-containerregistry v0.21.6 go.mod via MVS resolution. The containerd/stargz-snapshotter/estargz and vbatts/tar-split indirect dependencies are dropped because v0.21.6 removed estargz support (go-containerregistry#2288). Test expectations in images/manifest_test.go are updated for the unrelated OCI-spec compliance change in google/go-containerregistry#2269: mutate.AppendManifests now sets the index entry ArtifactType to the image Config.MediaType when the image manifest does not itself set artifactType. For the Docker schema2 fixture in TestManifestListForImage_RemoteImage this becomes "application/vnd.docker.container.image.v1+json" (types.DockerConfigJSON).
jimmidyson
added a commit
to mesosphere/mindthegap
that referenced
this pull request
May 19, 2026
…223) (#1046) ## Summary - Bump `github.com/google/go-containerregistry` v0.21.5 → v0.21.6 to pick up [google/go-containerregistry#2302](google/go-containerregistry#2302) — transport: allow bearer realm at same host:port as registry. - Fixes [NCN-114223](https://jira.nutanix.com/browse/NCN-114223): `mindthegap push bundle` (and downstream `nkp push bundle`) regression against on-prem registries that colocate the registry and bearer-token endpoint on the same private IP. e.g. for an internal Harbor at `https://10.162.182.23:5000/library` the push aborted with `invalid realm in www-authenticate: realm host "10.162.182.23" is a private or link-local address`. - Add regression test `TestPushDockerArchive_BearerAuthSameHostLoopbackRealm` in `cmd/mindthegap/push/imagearchive/push_test.go` that reproduces the exact NCN-114223 error against v0.21.5 and passes against v0.21.6. ## Background The realm-URL validation introduced in [go-containerregistry#2243](google/go-containerregistry#2243) (shipped in v0.21.5) rejects realms whose host resolves to a private, loopback, or link-local IP. This is the right default for the cross-host SSRF case (a malicious registry pointing the token endpoint at `169.254.169.254` or a sister internal service), but it broke legitimate on-prem deployments that serve their own token endpoint at the same host:port as the registry. [#2258](google/go-containerregistry#2258) tracked the discussion; [#2302](google/go-containerregistry#2302) implements the agreed fix: keep the cross-host SSRF block, but skip the private-IP check when the realm URL host AND port match the registry host:port. ## Transitive dependency changes Pulled in by `go-containerregistry` v0.21.6's `go.mod` via MVS resolution: - `docker/cli` v29.4.0 → v29.4.3 - `moby/moby/api` v1.54.1 → v1.54.2 - `moby/moby/client` v0.4.0 → v0.4.1 - `docker/go-connections` v0.6.0 → v0.7.0 - `klauspost/compress` v1.18.5 → v1.18.6 - `golang.org/x/{crypto,mod,net,sys,term,text,tools}` — minor bumps Dropped (v0.21.6 removed estargz support in [go-containerregistry#2288](google/go-containerregistry#2288)): - `containerd/stargz-snapshotter/estargz` (indirect) - `vbatts/tar-split` (indirect) ## Test fixture update `images/manifest_test.go` is updated for the unrelated OCI-spec compliance change in [go-containerregistry#2269](google/go-containerregistry#2269): `mutate.AppendManifests` now sets the index entry `ArtifactType` to the image's `Config.MediaType` when the image manifest does not itself set `artifactType`. For the Docker schema2 fixture in `TestManifestListForImage_RemoteImage` this becomes `"application/vnd.docker.container.image.v1+json"` (`types.DockerConfigJSON`). ## Test plan - [x] `go test -count=1 ./...` passes (155 tests). - [x] New `TestPushDockerArchive_BearerAuthSameHostLoopbackRealm` fails on v0.21.5 with the exact NCN-114223 error (`invalid realm in www-authenticate: realm host "127.0.0.1" is a private or link-local address`) and passes on v0.21.6 — bisected to confirm it is not a tautology. - [x] `go build ./...` passes. - [x] `golangci-lint run ./...` clean. - [ ] Manual verification against an on-prem Harbor with realm host == registry host on an RFC1918 IP (e.g. the reproducer environment in NCN-114223). ## Out of scope The two pre-existing govulncheck stdlib findings (`GO-2026-4982`, `GO-2026-4980`, `GO-2026-4971`, `GO-2026-4918` — all `go1.25.x` fixed in `go1.25.10`) are unrelated to this change.
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.
Per the OCI distribution spec (distribution-spec#395), the artifactType
on a descriptor should be set to the manifest's artifactType if present,
falling back to config.mediaType unconditionally (not only for
non-standard config types as was done previously). Additionally,
annotations from the manifest are now copied to the descriptor.
Current wording from the spec:
With this change, we correctly follow step 5.
Changes:
config.mediaType, and copy manifest annotations to the descriptor
and partial/with.go