diff --git a/cmd/oci-image-tool/validate.go b/cmd/oci-image-tool/validate.go index 7c265f47f..6c2665ca0 100644 --- a/cmd/oci-image-tool/validate.go +++ b/cmd/oci-image-tool/validate.go @@ -16,6 +16,7 @@ package main import ( "fmt" + "io/ioutil" "log" "os" "strings" @@ -138,6 +139,9 @@ func (v *validateCmd) validatePath(name string) error { switch typ { case typeManifest: + if err := validManifestfile(name); err != nil { + return errors.Wrap(err, "invalid manifest format") + } return schema.MediaTypeManifest.Validate(f) case typeManifestList: return schema.MediaTypeManifestList.Validate(f) @@ -147,3 +151,19 @@ func (v *validateCmd) validatePath(name string) error { return fmt.Errorf("type %q unimplemented", typ) } + +func validManifestfile(name string) error { + f, err := os.Open(name) + if err != nil { + return errors.Wrap(err, "unable to open manifest") + } + defer f.Close() + buf, err := ioutil.ReadAll(f) + if err != nil { + return errors.Wrap(err, "unable to read manifest") + } + if err := image.ValidInternalMediaType(buf); err != nil { + return errors.Wrapf(err, "%s: manifest validation failed", name) + } + return nil +} diff --git a/image/manifest.go b/image/manifest.go index 641e849c7..450958263 100644 --- a/image/manifest.go +++ b/image/manifest.go @@ -51,6 +51,10 @@ func findManifest(w walker, d *descriptor) (*manifest, error) { return errors.Wrapf(err, "%s: error reading manifest", path) } + if err := ValidInternalMediaType(buf); err != nil { + return errors.Wrapf(err, "%s: manifest validation failed", path) + } + if err := schema.MediaTypeManifest.Validate(bytes.NewReader(buf)); err != nil { return errors.Wrapf(err, "%s: manifest validation failed", path) } @@ -240,3 +244,33 @@ loop: } return nil } + +// ValidInternalMediaType validate the manifest's internal +// fields mediatype such as config and layers, to make sure +// they match to spec definition, or returns an error if +// the validation failed. +func ValidInternalMediaType(buf []byte) error { + header := struct { + Config struct { + MediaType string `json:"mediaType"` + } `json:"config"` + Layers []struct { + MediaType string `json:"mediaType"` + } `json:"layers"` + }{} + + if err := json.NewDecoder(bytes.NewReader(buf)).Decode(&header); err != nil { + return errors.Wrap(err, "manifest format mismatch") + } + if header.Config.MediaType != string(schema.MediaTypeImageConfig) && + header.Config.MediaType != string(schema.MediaTypeDockerImageConfig) { + return fmt.Errorf("illegal config mediaType: %s", header.Config.MediaType) + } + for _, layer := range header.Layers { + if layer.MediaType != string(schema.MediaTypeImageLayer) && + layer.MediaType != string(schema.MediaTypeDockerImageLayer) { + return fmt.Errorf("illegal layer mediaType: %s", layer.MediaType) + } + } + return nil +} diff --git a/schema/schema.go b/schema/schema.go index 1ca6312c4..de4160179 100644 --- a/schema/schema.go +++ b/schema/schema.go @@ -22,11 +22,13 @@ import ( // Media types for the OCI image formats const ( - MediaTypeDescriptor Validator = v1.MediaTypeDescriptor - MediaTypeManifest Validator = v1.MediaTypeImageManifest - MediaTypeManifestList Validator = v1.MediaTypeImageManifestList - MediaTypeImageConfig Validator = v1.MediaTypeImageConfig - MediaTypeImageLayer unimplemented = v1.MediaTypeImageLayer + MediaTypeDescriptor Validator = v1.MediaTypeDescriptor + MediaTypeManifest Validator = v1.MediaTypeImageManifest + MediaTypeManifestList Validator = v1.MediaTypeImageManifestList + MediaTypeImageConfig Validator = v1.MediaTypeImageConfig + MediaTypeImageLayer unimplemented = v1.MediaTypeImageLayer + MediaTypeDockerImageConfig Validator = "application/vnd.docker.container.image.v1+json" + MediaTypeDockerImageLayer unimplemented = "application/vnd.docker.image.rootfs.diff.tar.gzip" ) var (