diff --git a/manifest/manifestlist/manifestlist.go b/manifest/manifestlist/manifestlist.go index 3303c6be..0d5c9af1 100644 --- a/manifest/manifestlist/manifestlist.go +++ b/manifest/manifestlist/manifestlist.go @@ -89,9 +89,6 @@ type ManifestList struct { // Config references the image configuration as a blob. Manifests []ManifestDescriptor `json:"manifests"` - - // Annotations contains arbitrary metadata for the image index. - Annotations map[string]string `json:"annotations,omitempty"` } // References returns the distribution descriptors for the referenced image diff --git a/manifest/manifestlist/manifestlist_test.go b/manifest/manifestlist/manifestlist_test.go index 6909895b..720b911b 100644 --- a/manifest/manifestlist/manifestlist_test.go +++ b/manifest/manifestlist/manifestlist_test.go @@ -111,7 +111,12 @@ func TestManifestList(t *testing.T) { } } -// TODO (mikebrow): add annotations on the index and individual manifests +// TODO (mikebrow): add annotations on the manifest list (index) and support for +// empty platform structs (move to Platform *Platform `json:"platform,omitempty"` +// from current Platform PlatformSpec `json:"platform"`) in the manifest descriptor. +// Requires changes to docker/distribution/manifest/manifestlist.ManifestList and .ManifestDescriptor +// and associated serialization APIs in manifestlist.go. Or split the OCI index and +// docker manifest list implementations, which would require a lot of refactoring. var expectedOCIImageIndexSerialization = []byte(`{ "schemaVersion": 2, "mediaType": "application/vnd.oci.image.index.v1+json", @@ -128,10 +133,25 @@ var expectedOCIImageIndexSerialization = []byte(`{ ] } }, + { + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "size": 985, + "digest": "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b", + "annotations": { + "platform": "none" + }, + "platform": { + "architecture": "", + "os": "" + } + }, { "mediaType": "application/vnd.oci.image.manifest.v1+json", "size": 2392, "digest": "sha256:6346340964309634683409684360934680934608934608934608934068934608", + "annotations": { + "what": "for" + }, "platform": { "architecture": "sun4m", "os": "sunos" @@ -156,9 +176,18 @@ func TestOCIImageIndex(t *testing.T) { }, { Descriptor: distribution.Descriptor{ - Digest: "sha256:6346340964309634683409684360934680934608934608934608934068934608", - Size: 2392, - MediaType: "application/vnd.oci.image.manifest.v1+json", + Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b", + Size: 985, + MediaType: "application/vnd.oci.image.manifest.v1+json", + Annotations: map[string]string{"platform": "none"}, + }, + }, + { + Descriptor: distribution.Descriptor{ + Digest: "sha256:6346340964309634683409684360934680934608934608934608934068934608", + Size: 2392, + MediaType: "application/vnd.oci.image.manifest.v1+json", + Annotations: map[string]string{"what": "for"}, }, Platform: PlatformSpec{ Architecture: "sun4m", @@ -190,7 +219,7 @@ func TestOCIImageIndex(t *testing.T) { // Check that the canonical field has the expected value. if !bytes.Equal(expectedOCIImageIndexSerialization, canonical) { - t.Fatalf("manifest bytes not equal: %q != %q", string(canonical), string(expectedOCIImageIndexSerialization)) + t.Fatalf("manifest bytes not equal to expected: %q != %q", string(canonical), string(expectedOCIImageIndexSerialization)) } var unmarshalled DeserializedManifestList @@ -203,7 +232,7 @@ func TestOCIImageIndex(t *testing.T) { } references := deserialized.References() - if len(references) != 2 { + if len(references) != 3 { t.Fatalf("unexpected number of references: %d", len(references)) } for i := range references { diff --git a/registry/storage/ocimanifesthandler.go b/registry/storage/ocimanifesthandler.go index c61beb4b..f1fbcead 100644 --- a/registry/storage/ocimanifesthandler.go +++ b/registry/storage/ocimanifesthandler.go @@ -3,6 +3,7 @@ package storage import ( "encoding/json" "fmt" + "net/url" "github.com/docker/distribution" "github.com/docker/distribution/context" @@ -79,6 +80,22 @@ func (ms *ocischemaManifestHandler) verifyManifest(ctx context.Context, mnfst oc var err error switch descriptor.MediaType { + case v1.MediaTypeImageLayer, v1.MediaTypeImageLayerGzip, v1.MediaTypeImageLayerNonDistributable, v1.MediaTypeImageLayerNonDistributableGzip: + allow := ms.manifestURLs.allow + deny := ms.manifestURLs.deny + for _, u := range descriptor.URLs { + var pu *url.URL + pu, err = url.Parse(u) + if err != nil || (pu.Scheme != "http" && pu.Scheme != "https") || pu.Fragment != "" || (allow != nil && !allow.MatchString(u)) || (deny != nil && deny.MatchString(u)) { + err = errInvalidURL + break + } + } + if err == nil && len(descriptor.URLs) == 0 { + // If no URLs, require that the blob exists + _, err = blobsService.Stat(ctx, descriptor.Digest) + } + case v1.MediaTypeImageManifest: var exists bool exists, err = manifestService.Exists(ctx, descriptor.Digest) diff --git a/registry/storage/ocimanifesthandler_test.go b/registry/storage/ocimanifesthandler_test.go index 047f1c3a..302294af 100644 --- a/registry/storage/ocimanifesthandler_test.go +++ b/registry/storage/ocimanifesthandler_test.go @@ -52,6 +52,11 @@ func TestVerifyOCIManifestNonDistributableLayer(t *testing.T) { } cases := []testcase{ + { + nonDistributableLayer, + nil, + distribution.ErrManifestBlobUnknown{Digest: nonDistributableLayer.Digest}, + }, { layer, []string{"http://foo/bar"}, @@ -60,37 +65,37 @@ func TestVerifyOCIManifestNonDistributableLayer(t *testing.T) { { nonDistributableLayer, []string{"file:///local/file"}, - nil, + errInvalidURL, }, { nonDistributableLayer, []string{"http://foo/bar#baz"}, - nil, + errInvalidURL, }, { nonDistributableLayer, []string{""}, - nil, + errInvalidURL, }, { nonDistributableLayer, []string{"https://foo/bar", ""}, - nil, + errInvalidURL, }, { nonDistributableLayer, []string{"", "https://foo/bar"}, - nil, + errInvalidURL, }, { nonDistributableLayer, []string{"http://nope/bar"}, - nil, + errInvalidURL, }, { nonDistributableLayer, []string{"http://foo/nope"}, - nil, + errInvalidURL, }, { nonDistributableLayer, @@ -122,6 +127,8 @@ func TestVerifyOCIManifestNonDistributableLayer(t *testing.T) { if _, ok = verr[1].(distribution.ErrManifestBlobUnknown); ok { err = verr[0] } + } else if len(verr) == 1 { + err = verr[0] } } if err != c.Err {