Check media types when unmarshalling manifests

When unmarshalling manifests from JSON, check that the MediaType field
corresponds to the type that we are unmarshalling as. This makes sure
that when we retrieve a manifest from the manifest store, it will have
the same type as it was handled as before storing it in the manifest
store.

Signed-off-by: Owen W. Taylor <otaylor@fishsoup.net>
This commit is contained in:
Owen W. Taylor 2018-03-14 16:55:45 -04:00
parent f6224f78ba
commit 1d47ef7b80
6 changed files with 214 additions and 17 deletions

View file

@ -38,6 +38,13 @@ func init() {
return nil, distribution.Descriptor{}, err return nil, distribution.Descriptor{}, err
} }
if m.MediaType != MediaTypeManifestList {
err = fmt.Errorf("mediaType in manifest list should be '%s' not '%s'",
MediaTypeManifestList, m.MediaType)
return nil, distribution.Descriptor{}, err
}
dgst := digest.FromBytes(b) dgst := digest.FromBytes(b)
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifestList}, err return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifestList}, err
} }
@ -53,6 +60,13 @@ func init() {
return nil, distribution.Descriptor{}, err return nil, distribution.Descriptor{}, err
} }
if m.MediaType != v1.MediaTypeImageIndex {
err = fmt.Errorf("mediaType in image index should be '%s' not '%s'",
v1.MediaTypeImageIndex, m.MediaType)
return nil, distribution.Descriptor{}, err
}
dgst := digest.FromBytes(b) dgst := digest.FromBytes(b)
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageIndex}, err return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageIndex}, err
} }
@ -130,15 +144,23 @@ type DeserializedManifestList struct {
// DeserializedManifestList which contains the resulting manifest list // DeserializedManifestList which contains the resulting manifest list
// and its JSON representation. // and its JSON representation.
func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) { func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) {
var m ManifestList var mediaType string
if len(descriptors) > 0 && descriptors[0].Descriptor.MediaType == v1.MediaTypeImageManifest { if len(descriptors) > 0 && descriptors[0].Descriptor.MediaType == v1.MediaTypeImageManifest {
m = ManifestList{ mediaType = v1.MediaTypeImageIndex
Versioned: OCISchemaVersion,
}
} else { } else {
m = ManifestList{ mediaType = MediaTypeManifestList
Versioned: SchemaVersion, }
}
return FromDescriptorsWithMediaType(descriptors, mediaType)
}
// For testing purposes, it's useful to be able to specify the media type explicitly
func FromDescriptorsWithMediaType(descriptors []ManifestDescriptor, mediaType string) (*DeserializedManifestList, error) {
m := ManifestList{
Versioned: manifest.Versioned{
SchemaVersion: 2,
MediaType: mediaType,
},
} }
m.Manifests = make([]ManifestDescriptor, len(descriptors), len(descriptors)) m.Manifests = make([]ManifestDescriptor, len(descriptors), len(descriptors))

View file

@ -38,7 +38,7 @@ var expectedManifestListSerialization = []byte(`{
] ]
}`) }`)
func TestManifestList(t *testing.T) { func makeTestManifestList(t *testing.T, mediaType string) ([]ManifestDescriptor, *DeserializedManifestList) {
manifestDescriptors := []ManifestDescriptor{ manifestDescriptors := []ManifestDescriptor{
{ {
Descriptor: distribution.Descriptor{ Descriptor: distribution.Descriptor{
@ -65,11 +65,16 @@ func TestManifestList(t *testing.T) {
}, },
} }
deserialized, err := FromDescriptors(manifestDescriptors) deserialized, err := FromDescriptorsWithMediaType(manifestDescriptors, mediaType)
if err != nil { if err != nil {
t.Fatalf("error creating DeserializedManifestList: %v", err) t.Fatalf("error creating DeserializedManifestList: %v", err)
} }
return manifestDescriptors, deserialized
}
func TestManifestList(t *testing.T) {
manifestDescriptors, deserialized := makeTestManifestList(t, MediaTypeManifestList)
mediaType, canonical, _ := deserialized.Payload() mediaType, canonical, _ := deserialized.Payload()
if mediaType != MediaTypeManifestList { if mediaType != MediaTypeManifestList {
@ -160,7 +165,7 @@ var expectedOCIImageIndexSerialization = []byte(`{
] ]
}`) }`)
func TestOCIImageIndex(t *testing.T) { func makeTestOCIImageIndex(t *testing.T, mediaType string) ([]ManifestDescriptor, *DeserializedManifestList) {
manifestDescriptors := []ManifestDescriptor{ manifestDescriptors := []ManifestDescriptor{
{ {
Descriptor: distribution.Descriptor{ Descriptor: distribution.Descriptor{
@ -196,11 +201,17 @@ func TestOCIImageIndex(t *testing.T) {
}, },
} }
deserialized, err := FromDescriptors(manifestDescriptors) deserialized, err := FromDescriptorsWithMediaType(manifestDescriptors, mediaType)
if err != nil { if err != nil {
t.Fatalf("error creating DeserializedManifestList: %v", err) t.Fatalf("error creating DeserializedManifestList: %v", err)
} }
return manifestDescriptors, deserialized
}
func TestOCIImageIndex(t *testing.T) {
manifestDescriptors, deserialized := makeTestOCIImageIndex(t, v1.MediaTypeImageIndex)
mediaType, canonical, _ := deserialized.Payload() mediaType, canonical, _ := deserialized.Payload()
if mediaType != v1.MediaTypeImageIndex { if mediaType != v1.MediaTypeImageIndex {
@ -241,3 +252,54 @@ func TestOCIImageIndex(t *testing.T) {
} }
} }
} }
func mediaTypeTest(t *testing.T, contentType string, mediaType string, shouldError bool) {
var m *DeserializedManifestList
if contentType == MediaTypeManifestList {
_, m = makeTestManifestList(t, mediaType)
} else {
_, m = makeTestOCIImageIndex(t, mediaType)
}
_, canonical, err := m.Payload()
if err != nil {
t.Fatalf("error getting payload, %v", err)
}
unmarshalled, descriptor, err := distribution.UnmarshalManifest(
contentType,
canonical)
if shouldError {
if err == nil {
t.Fatalf("bad content type should have produced error")
}
} else {
if err != nil {
t.Fatalf("error unmarshaling manifest, %v", err)
}
asManifest := unmarshalled.(*DeserializedManifestList)
if asManifest.MediaType != mediaType {
t.Fatalf("Bad media type '%v' as unmarshalled", asManifest.MediaType)
}
if descriptor.MediaType != contentType {
t.Fatalf("Bad media type '%v' for descriptor", descriptor.MediaType)
}
unmarshalledMediaType, _, _ := unmarshalled.Payload()
if unmarshalledMediaType != contentType {
t.Fatalf("Bad media type '%v' for payload", unmarshalledMediaType)
}
}
}
func TestMediaTypes(t *testing.T) {
mediaTypeTest(t, MediaTypeManifestList, "", true)
mediaTypeTest(t, MediaTypeManifestList, MediaTypeManifestList, false)
mediaTypeTest(t, MediaTypeManifestList, MediaTypeManifestList+"XXX", true)
mediaTypeTest(t, v1.MediaTypeImageIndex, "", true)
mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex, false)
mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex+"XXX", true)
}

View file

@ -97,6 +97,11 @@ func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
return err return err
} }
if manifest.MediaType != v1.MediaTypeImageManifest {
return fmt.Errorf("mediaType in manifest should be '%s' not '%s'",
v1.MediaTypeImageManifest, manifest.MediaType)
}
m.Manifest = manifest m.Manifest = manifest
return nil return nil

View file

@ -7,6 +7,7 @@ import (
"testing" "testing"
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/manifest"
"github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/image-spec/specs-go/v1"
) )
@ -36,9 +37,12 @@ var expectedManifestSerialization = []byte(`{
} }
}`) }`)
func TestManifest(t *testing.T) { func makeTestManifest(mediaType string) Manifest {
manifest := Manifest{ return Manifest{
Versioned: SchemaVersion, Versioned: manifest.Versioned{
SchemaVersion: 2,
MediaType: mediaType,
},
Config: distribution.Descriptor{ Config: distribution.Descriptor{
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b", Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
Size: 985, Size: 985,
@ -55,6 +59,10 @@ func TestManifest(t *testing.T) {
}, },
Annotations: map[string]string{"hot": "potato"}, Annotations: map[string]string{"hot": "potato"},
} }
}
func TestManifest(t *testing.T) {
manifest := makeTestManifest(v1.MediaTypeImageManifest)
deserialized, err := FromStruct(manifest) deserialized, err := FromStruct(manifest)
if err != nil { if err != nil {
@ -131,3 +139,46 @@ func TestManifest(t *testing.T) {
t.Fatalf("unexpected annotation in reference: %s", references[1].Annotations["lettuce"]) t.Fatalf("unexpected annotation in reference: %s", references[1].Annotations["lettuce"])
} }
} }
func mediaTypeTest(t *testing.T, mediaType string, shouldError bool) {
manifest := makeTestManifest(mediaType)
deserialized, err := FromStruct(manifest)
if err != nil {
t.Fatalf("error creating DeserializedManifest: %v", err)
}
unmarshalled, descriptor, err := distribution.UnmarshalManifest(
v1.MediaTypeImageManifest,
deserialized.canonical)
if shouldError {
if err == nil {
t.Fatalf("bad content type should have produced error")
}
} else {
if err != nil {
t.Fatalf("error unmarshaling manifest, %v", err)
}
asManifest := unmarshalled.(*DeserializedManifest)
if asManifest.MediaType != mediaType {
t.Fatalf("Bad media type '%v' as unmarshalled", asManifest.MediaType)
}
if descriptor.MediaType != v1.MediaTypeImageManifest {
t.Fatalf("Bad media type '%v' for descriptor", descriptor.MediaType)
}
unmarshalledMediaType, _, _ := unmarshalled.Payload()
if unmarshalledMediaType != v1.MediaTypeImageManifest {
t.Fatalf("Bad media type '%v' for payload", unmarshalledMediaType)
}
}
}
func TestMediaTypes(t *testing.T) {
mediaTypeTest(t, "", true)
mediaTypeTest(t, v1.MediaTypeImageManifest, false)
mediaTypeTest(t, v1.MediaTypeImageManifest+"XXX", true)
}

View file

@ -116,6 +116,12 @@ func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
return err return err
} }
if manifest.MediaType != MediaTypeManifest {
return fmt.Errorf("mediaType in manifest should be '%s' not '%s'",
MediaTypeManifest, manifest.MediaType)
}
m.Manifest = manifest m.Manifest = manifest
return nil return nil

View file

@ -7,6 +7,7 @@ import (
"testing" "testing"
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/manifest"
) )
var expectedManifestSerialization = []byte(`{ var expectedManifestSerialization = []byte(`{
@ -26,9 +27,12 @@ var expectedManifestSerialization = []byte(`{
] ]
}`) }`)
func TestManifest(t *testing.T) { func makeTestManifest(mediaType string) Manifest {
manifest := Manifest{ return Manifest{
Versioned: SchemaVersion, Versioned: manifest.Versioned{
SchemaVersion: 2,
MediaType: mediaType,
},
Config: distribution.Descriptor{ Config: distribution.Descriptor{
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b", Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
Size: 985, Size: 985,
@ -42,6 +46,10 @@ func TestManifest(t *testing.T) {
}, },
}, },
} }
}
func TestManifest(t *testing.T) {
manifest := makeTestManifest(MediaTypeManifest)
deserialized, err := FromStruct(manifest) deserialized, err := FromStruct(manifest)
if err != nil { if err != nil {
@ -109,3 +117,46 @@ func TestManifest(t *testing.T) {
t.Fatalf("unexpected size in reference: %d", references[0].Size) t.Fatalf("unexpected size in reference: %d", references[0].Size)
} }
} }
func mediaTypeTest(t *testing.T, mediaType string, shouldError bool) {
manifest := makeTestManifest(mediaType)
deserialized, err := FromStruct(manifest)
if err != nil {
t.Fatalf("error creating DeserializedManifest: %v", err)
}
unmarshalled, descriptor, err := distribution.UnmarshalManifest(
MediaTypeManifest,
deserialized.canonical)
if shouldError {
if err == nil {
t.Fatalf("bad content type should have produced error")
}
} else {
if err != nil {
t.Fatalf("error unmarshaling manifest, %v", err)
}
asManifest := unmarshalled.(*DeserializedManifest)
if asManifest.MediaType != mediaType {
t.Fatalf("Bad media type '%v' as unmarshalled", asManifest.MediaType)
}
if descriptor.MediaType != MediaTypeManifest {
t.Fatalf("Bad media type '%v' for descriptor", descriptor.MediaType)
}
unmarshalledMediaType, _, _ := unmarshalled.Payload()
if unmarshalledMediaType != MediaTypeManifest {
t.Fatalf("Bad media type '%v' for payload", unmarshalledMediaType)
}
}
}
func TestMediaTypes(t *testing.T) {
mediaTypeTest(t, "", true)
mediaTypeTest(t, MediaTypeManifest, false)
mediaTypeTest(t, MediaTypeManifest+"XXX", true)
}