Skip to content

Commit

Permalink
Check media types when unmarshalling manifests
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
owtaylor committed Jun 19, 2018
1 parent f6224f7 commit 1d47ef7
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 17 deletions.
36 changes: 29 additions & 7 deletions manifest/manifestlist/manifestlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ func init() {
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)
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: MediaTypeManifestList}, err
}
Expand All @@ -53,6 +60,13 @@ func init() {
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)
return m, distribution.Descriptor{Digest: dgst, Size: int64(len(b)), MediaType: v1.MediaTypeImageIndex}, err
}
Expand Down Expand Up @@ -130,15 +144,23 @@ type DeserializedManifestList struct {
// DeserializedManifestList which contains the resulting manifest list
// and its JSON representation.
func FromDescriptors(descriptors []ManifestDescriptor) (*DeserializedManifestList, error) {
var m ManifestList
var mediaType string
if len(descriptors) > 0 && descriptors[0].Descriptor.MediaType == v1.MediaTypeImageManifest {
m = ManifestList{
Versioned: OCISchemaVersion,
}
mediaType = v1.MediaTypeImageIndex
} else {
m = ManifestList{
Versioned: SchemaVersion,
}
mediaType = MediaTypeManifestList
}

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))
Expand Down
70 changes: 66 additions & 4 deletions manifest/manifestlist/manifestlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var expectedManifestListSerialization = []byte(`{
]
}`)

func TestManifestList(t *testing.T) {
func makeTestManifestList(t *testing.T, mediaType string) ([]ManifestDescriptor, *DeserializedManifestList) {
manifestDescriptors := []ManifestDescriptor{
{
Descriptor: distribution.Descriptor{
Expand All @@ -65,11 +65,16 @@ func TestManifestList(t *testing.T) {
},
}

deserialized, err := FromDescriptors(manifestDescriptors)
deserialized, err := FromDescriptorsWithMediaType(manifestDescriptors, mediaType)
if err != nil {
t.Fatalf("error creating DeserializedManifestList: %v", err)
}

return manifestDescriptors, deserialized
}

func TestManifestList(t *testing.T) {
manifestDescriptors, deserialized := makeTestManifestList(t, MediaTypeManifestList)
mediaType, canonical, _ := deserialized.Payload()

if mediaType != MediaTypeManifestList {
Expand Down Expand Up @@ -160,7 +165,7 @@ var expectedOCIImageIndexSerialization = []byte(`{
]
}`)

func TestOCIImageIndex(t *testing.T) {
func makeTestOCIImageIndex(t *testing.T, mediaType string) ([]ManifestDescriptor, *DeserializedManifestList) {
manifestDescriptors := []ManifestDescriptor{
{
Descriptor: distribution.Descriptor{
Expand Down Expand Up @@ -196,11 +201,17 @@ func TestOCIImageIndex(t *testing.T) {
},
}

deserialized, err := FromDescriptors(manifestDescriptors)
deserialized, err := FromDescriptorsWithMediaType(manifestDescriptors, mediaType)
if err != nil {
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()

if mediaType != v1.MediaTypeImageIndex {
Expand Down Expand Up @@ -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)
}
5 changes: 5 additions & 0 deletions manifest/ocischema/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,11 @@ func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
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

return nil
Expand Down
57 changes: 54 additions & 3 deletions manifest/ocischema/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/docker/distribution"
"github.com/docker/distribution/manifest"
"github.com/opencontainers/image-spec/specs-go/v1"
)

Expand Down Expand Up @@ -36,9 +37,12 @@ var expectedManifestSerialization = []byte(`{
}
}`)

func TestManifest(t *testing.T) {
manifest := Manifest{
Versioned: SchemaVersion,
func makeTestManifest(mediaType string) Manifest {
return Manifest{
Versioned: manifest.Versioned{
SchemaVersion: 2,
MediaType: mediaType,
},
Config: distribution.Descriptor{
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
Size: 985,
Expand All @@ -55,6 +59,10 @@ func TestManifest(t *testing.T) {
},
Annotations: map[string]string{"hot": "potato"},
}
}

func TestManifest(t *testing.T) {
manifest := makeTestManifest(v1.MediaTypeImageManifest)

deserialized, err := FromStruct(manifest)
if err != nil {
Expand Down Expand Up @@ -131,3 +139,46 @@ func TestManifest(t *testing.T) {
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)
}
6 changes: 6 additions & 0 deletions manifest/schema2/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,12 @@ func (m *DeserializedManifest) UnmarshalJSON(b []byte) error {
return err
}

if manifest.MediaType != MediaTypeManifest {
return fmt.Errorf("mediaType in manifest should be '%s' not '%s'",
MediaTypeManifest, manifest.MediaType)

}

m.Manifest = manifest

return nil
Expand Down
57 changes: 54 additions & 3 deletions manifest/schema2/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"testing"

"github.com/docker/distribution"
"github.com/docker/distribution/manifest"
)

var expectedManifestSerialization = []byte(`{
Expand All @@ -26,9 +27,12 @@ var expectedManifestSerialization = []byte(`{
]
}`)

func TestManifest(t *testing.T) {
manifest := Manifest{
Versioned: SchemaVersion,
func makeTestManifest(mediaType string) Manifest {
return Manifest{
Versioned: manifest.Versioned{
SchemaVersion: 2,
MediaType: mediaType,
},
Config: distribution.Descriptor{
Digest: "sha256:1a9ec845ee94c202b2d5da74a24f0ed2058318bfa9879fa541efaecba272e86b",
Size: 985,
Expand All @@ -42,6 +46,10 @@ func TestManifest(t *testing.T) {
},
},
}
}

func TestManifest(t *testing.T) {
manifest := makeTestManifest(MediaTypeManifest)

deserialized, err := FromStruct(manifest)
if err != nil {
Expand Down Expand Up @@ -109,3 +117,46 @@ func TestManifest(t *testing.T) {
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)
}

1 comment on commit 1d47ef7

@cpuguy83
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears to have broken some things in moby.
Why are we validating data when unmarahalling instead of when it's important in the store?

Please sign in to comment.