Merge pull request from GHSA-qq97-vm5h-rrhg
manifest: validate document type before unmarshal
This commit is contained in:
		
						commit
						41a0452eea
					
				
					 4 changed files with 109 additions and 0 deletions
				
			
		|  | @ -54,6 +54,9 @@ func init() { | |||
| 	} | ||||
| 
 | ||||
| 	imageIndexFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) { | ||||
| 		if err := validateIndex(b); err != nil { | ||||
| 			return nil, distribution.Descriptor{}, err | ||||
| 		} | ||||
| 		m := new(DeserializedManifestList) | ||||
| 		err := m.UnmarshalJSON(b) | ||||
| 		if err != nil { | ||||
|  | @ -221,3 +224,23 @@ func (m DeserializedManifestList) Payload() (string, []byte, error) { | |||
| 
 | ||||
| 	return mediaType, m.canonical, nil | ||||
| } | ||||
| 
 | ||||
| // unknownDocument represents a manifest, manifest list, or index that has not | ||||
| // yet been validated | ||||
| type unknownDocument struct { | ||||
| 	Config interface{} `json:"config,omitempty"` | ||||
| 	Layers interface{} `json:"layers,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // validateIndex returns an error if the byte slice is invalid JSON or if it | ||||
| // contains fields that belong to a manifest | ||||
| func validateIndex(b []byte) error { | ||||
| 	var doc unknownDocument | ||||
| 	if err := json.Unmarshal(b, &doc); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if doc.Config != nil || doc.Layers != nil { | ||||
| 		return errors.New("index: expected index but found manifest") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
|  | @ -7,6 +7,8 @@ import ( | |||
| 	"testing" | ||||
| 
 | ||||
| 	"github.com/distribution/distribution/v3" | ||||
| 	"github.com/distribution/distribution/v3/manifest/ocischema" | ||||
| 
 | ||||
| 	v1 "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| ) | ||||
| 
 | ||||
|  | @ -327,3 +329,33 @@ func TestMediaTypes(t *testing.T) { | |||
| 	mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex, false) | ||||
| 	mediaTypeTest(t, v1.MediaTypeImageIndex, v1.MediaTypeImageIndex+"XXX", true) | ||||
| } | ||||
| 
 | ||||
| func TestValidateManifest(t *testing.T) { | ||||
| 	manifest := ocischema.Manifest{ | ||||
| 		Config: distribution.Descriptor{Size: 1}, | ||||
| 		Layers: []distribution.Descriptor{{Size: 2}}, | ||||
| 	} | ||||
| 	index := ManifestList{ | ||||
| 		Manifests: []ManifestDescriptor{ | ||||
| 			{Descriptor: distribution.Descriptor{Size: 3}}, | ||||
| 		}, | ||||
| 	} | ||||
| 	t.Run("valid", func(t *testing.T) { | ||||
| 		b, err := json.Marshal(index) | ||||
| 		if err != nil { | ||||
| 			t.Fatal("unexpected error marshaling index", err) | ||||
| 		} | ||||
| 		if err := validateIndex(b); err != nil { | ||||
| 			t.Error("index should be valid", err) | ||||
| 		} | ||||
| 	}) | ||||
| 	t.Run("invalid", func(t *testing.T) { | ||||
| 		b, err := json.Marshal(manifest) | ||||
| 		if err != nil { | ||||
| 			t.Fatal("unexpected error marshaling manifest", err) | ||||
| 		} | ||||
| 		if err := validateIndex(b); err == nil { | ||||
| 			t.Error("manifest should not be valid") | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  |  | |||
|  | @ -22,6 +22,9 @@ var ( | |||
| 
 | ||||
| func init() { | ||||
| 	ocischemaFunc := func(b []byte) (distribution.Manifest, distribution.Descriptor, error) { | ||||
| 		if err := validateManifest(b); err != nil { | ||||
| 			return nil, distribution.Descriptor{}, err | ||||
| 		} | ||||
| 		m := new(DeserializedManifest) | ||||
| 		err := m.UnmarshalJSON(b) | ||||
| 		if err != nil { | ||||
|  | @ -122,3 +125,22 @@ func (m *DeserializedManifest) MarshalJSON() ([]byte, error) { | |||
| func (m DeserializedManifest) Payload() (string, []byte, error) { | ||||
| 	return v1.MediaTypeImageManifest, m.canonical, nil | ||||
| } | ||||
| 
 | ||||
| // unknownDocument represents a manifest, manifest list, or index that has not | ||||
| // yet been validated | ||||
| type unknownDocument struct { | ||||
| 	Manifests interface{} `json:"manifests,omitempty"` | ||||
| } | ||||
| 
 | ||||
| // validateManifest returns an error if the byte slice is invalid JSON or if it | ||||
| // contains fields that belong to a index | ||||
| func validateManifest(b []byte) error { | ||||
| 	var doc unknownDocument | ||||
| 	if err := json.Unmarshal(b, &doc); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if doc.Manifests != nil { | ||||
| 		return errors.New("ocimanifest: expected manifest but found index") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  |  | |||
|  | @ -8,6 +8,8 @@ import ( | |||
| 
 | ||||
| 	"github.com/distribution/distribution/v3" | ||||
| 	"github.com/distribution/distribution/v3/manifest" | ||||
| 	"github.com/distribution/distribution/v3/manifest/manifestlist" | ||||
| 
 | ||||
| 	v1 "github.com/opencontainers/image-spec/specs-go/v1" | ||||
| ) | ||||
| 
 | ||||
|  | @ -182,3 +184,33 @@ func TestMediaTypes(t *testing.T) { | |||
| 	mediaTypeTest(t, v1.MediaTypeImageManifest, false) | ||||
| 	mediaTypeTest(t, v1.MediaTypeImageManifest+"XXX", true) | ||||
| } | ||||
| 
 | ||||
| func TestValidateManifest(t *testing.T) { | ||||
| 	manifest := Manifest{ | ||||
| 		Config: distribution.Descriptor{Size: 1}, | ||||
| 		Layers: []distribution.Descriptor{{Size: 2}}, | ||||
| 	} | ||||
| 	index := manifestlist.ManifestList{ | ||||
| 		Manifests: []manifestlist.ManifestDescriptor{ | ||||
| 			{Descriptor: distribution.Descriptor{Size: 3}}, | ||||
| 		}, | ||||
| 	} | ||||
| 	t.Run("valid", func(t *testing.T) { | ||||
| 		b, err := json.Marshal(manifest) | ||||
| 		if err != nil { | ||||
| 			t.Fatal("unexpected error marshaling manifest", err) | ||||
| 		} | ||||
| 		if err := validateManifest(b); err != nil { | ||||
| 			t.Error("manifest should be valid", err) | ||||
| 		} | ||||
| 	}) | ||||
| 	t.Run("invalid", func(t *testing.T) { | ||||
| 		b, err := json.Marshal(index) | ||||
| 		if err != nil { | ||||
| 			t.Fatal("unexpected error marshaling index", err) | ||||
| 		} | ||||
| 		if err := validateManifest(b); err == nil { | ||||
| 			t.Error("index should not be valid") | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue