2017-07-10 23:09:10 +00:00
package storage
import (
2017-09-01 02:14:40 +00:00
"context"
2017-07-10 23:09:10 +00:00
"regexp"
2021-04-15 06:44:04 +00:00
"strings"
2017-07-10 23:09:10 +00:00
"testing"
2020-08-24 11:18:39 +00:00
"github.com/distribution/distribution/v3"
"github.com/distribution/distribution/v3/manifest"
"github.com/distribution/distribution/v3/manifest/ocischema"
"github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
2021-04-15 06:44:04 +00:00
"github.com/opencontainers/go-digest"
2019-10-09 12:02:21 +00:00
v1 "github.com/opencontainers/image-spec/specs-go/v1"
2017-07-10 23:09:10 +00:00
)
2017-07-11 19:19:47 +00:00
func TestVerifyOCIManifestNonDistributableLayer ( t * testing . T ) {
2017-07-10 23:09:10 +00:00
ctx := context . Background ( )
inmemoryDriver := inmemory . New ( )
registry := createRegistry ( t , inmemoryDriver ,
ManifestURLsAllowRegexp ( regexp . MustCompile ( "^https?://foo" ) ) ,
ManifestURLsDenyRegexp ( regexp . MustCompile ( "^https?://foo/nope" ) ) )
repo := makeRepository ( t , registry , "test" )
manifestService := makeManifestService ( t , repo )
2017-07-11 19:19:47 +00:00
config , err := repo . Blobs ( ctx ) . Put ( ctx , v1 . MediaTypeImageConfig , nil )
2017-07-10 23:09:10 +00:00
if err != nil {
t . Fatal ( err )
}
2017-07-11 19:19:47 +00:00
layer , err := repo . Blobs ( ctx ) . Put ( ctx , v1 . MediaTypeImageLayerGzip , nil )
2017-07-10 23:09:10 +00:00
if err != nil {
t . Fatal ( err )
}
2017-07-11 19:19:47 +00:00
nonDistributableLayer := distribution . Descriptor {
2017-07-10 23:09:10 +00:00
Digest : "sha256:463435349086340864309863409683460843608348608934092322395278926a" ,
Size : 6323 ,
2023-04-30 17:01:20 +00:00
MediaType : v1 . MediaTypeImageLayerNonDistributableGzip , //nolint:staticcheck // ignore A1019: v1.MediaTypeImageLayerNonDistributableGzip is deprecated: Non-distributable layers are deprecated, and not recommended for future use
2017-07-10 23:09:10 +00:00
}
2021-04-15 06:44:04 +00:00
emptyLayer := distribution . Descriptor {
Digest : "" ,
}
emptyGzipLayer := distribution . Descriptor {
Digest : "" ,
MediaType : v1 . MediaTypeImageLayerGzip ,
}
2017-07-10 23:09:10 +00:00
template := ocischema . Manifest {
Versioned : manifest . Versioned {
SchemaVersion : 2 ,
2017-07-11 19:19:47 +00:00
MediaType : v1 . MediaTypeImageManifest ,
2017-07-10 23:09:10 +00:00
} ,
Config : config ,
}
type testcase struct {
BaseLayer distribution . Descriptor
URLs [ ] string
Err error
}
cases := [ ] testcase {
2017-08-01 22:27:52 +00:00
{
nonDistributableLayer ,
nil ,
distribution . ErrManifestBlobUnknown { Digest : nonDistributableLayer . Digest } ,
} ,
2017-07-10 23:09:10 +00:00
{
layer ,
[ ] string { "http://foo/bar" } ,
nil ,
} ,
{
2017-07-11 19:19:47 +00:00
nonDistributableLayer ,
2017-07-10 23:09:10 +00:00
[ ] string { "file:///local/file" } ,
2017-08-01 22:27:52 +00:00
errInvalidURL ,
2017-07-10 23:09:10 +00:00
} ,
{
2017-07-11 19:19:47 +00:00
nonDistributableLayer ,
2017-07-10 23:09:10 +00:00
[ ] string { "http://foo/bar#baz" } ,
2017-08-01 22:27:52 +00:00
errInvalidURL ,
2017-07-10 23:09:10 +00:00
} ,
{
2017-07-11 19:19:47 +00:00
nonDistributableLayer ,
2017-07-10 23:09:10 +00:00
[ ] string { "" } ,
2017-08-01 22:27:52 +00:00
errInvalidURL ,
2017-07-10 23:09:10 +00:00
} ,
{
2017-07-11 19:19:47 +00:00
nonDistributableLayer ,
2017-07-10 23:09:10 +00:00
[ ] string { "https://foo/bar" , "" } ,
2017-08-01 22:27:52 +00:00
errInvalidURL ,
2017-07-10 23:09:10 +00:00
} ,
{
2017-07-11 19:19:47 +00:00
nonDistributableLayer ,
2017-07-10 23:09:10 +00:00
[ ] string { "" , "https://foo/bar" } ,
2017-08-01 22:27:52 +00:00
errInvalidURL ,
2017-07-10 23:09:10 +00:00
} ,
{
2017-07-11 19:19:47 +00:00
nonDistributableLayer ,
2017-07-10 23:09:10 +00:00
[ ] string { "http://nope/bar" } ,
2017-08-01 22:27:52 +00:00
errInvalidURL ,
2017-07-10 23:09:10 +00:00
} ,
{
2017-07-11 19:19:47 +00:00
nonDistributableLayer ,
2017-07-10 23:09:10 +00:00
[ ] string { "http://foo/nope" } ,
2017-08-01 22:27:52 +00:00
errInvalidURL ,
2017-07-10 23:09:10 +00:00
} ,
{
2017-07-11 19:19:47 +00:00
nonDistributableLayer ,
2017-07-10 23:09:10 +00:00
[ ] string { "http://foo/bar" } ,
nil ,
} ,
{
2017-07-11 19:19:47 +00:00
nonDistributableLayer ,
2017-07-10 23:09:10 +00:00
[ ] string { "https://foo/bar" } ,
nil ,
} ,
2021-04-15 06:44:04 +00:00
{
emptyLayer ,
[ ] string { "https://foo/empty" } ,
digest . ErrDigestInvalidFormat ,
} ,
{
emptyLayer ,
[ ] string { } ,
digest . ErrDigestInvalidFormat ,
} ,
{
emptyGzipLayer ,
[ ] string { "https://foo/empty" } ,
digest . ErrDigestInvalidFormat ,
} ,
{
emptyGzipLayer ,
[ ] string { } ,
digest . ErrDigestInvalidFormat ,
} ,
2017-07-10 23:09:10 +00:00
}
for _ , c := range cases {
m := template
l := c . BaseLayer
l . URLs = c . URLs
m . Layers = [ ] distribution . Descriptor { l }
dm , err := ocischema . FromStruct ( m )
if err != nil {
t . Error ( err )
continue
}
_ , err = manifestService . Put ( ctx , dm )
if verr , ok := err . ( distribution . ErrManifestVerification ) ; ok {
// Extract the first error
if len ( verr ) == 2 {
if _ , ok = verr [ 1 ] . ( distribution . ErrManifestBlobUnknown ) ; ok {
err = verr [ 0 ]
}
2017-08-01 22:27:52 +00:00
} else if len ( verr ) == 1 {
err = verr [ 0 ]
2017-07-10 23:09:10 +00:00
}
}
if err != c . Err {
t . Errorf ( "%#v: expected %v, got %v" , l , c . Err , err )
}
}
}
2021-04-15 06:44:04 +00:00
func TestVerifyOCIManifestBlobLayerAndConfig ( t * testing . T ) {
ctx := context . Background ( )
inmemoryDriver := inmemory . New ( )
registry := createRegistry ( t , inmemoryDriver ,
ManifestURLsAllowRegexp ( regexp . MustCompile ( "^https?://foo" ) ) ,
ManifestURLsDenyRegexp ( regexp . MustCompile ( "^https?://foo/nope" ) ) )
repo := makeRepository ( t , registry , strings . ToLower ( t . Name ( ) ) )
manifestService := makeManifestService ( t , repo )
config , err := repo . Blobs ( ctx ) . Put ( ctx , v1 . MediaTypeImageConfig , nil )
if err != nil {
t . Fatal ( err )
}
layer , err := repo . Blobs ( ctx ) . Put ( ctx , v1 . MediaTypeImageLayerGzip , nil )
if err != nil {
t . Fatal ( err )
}
template := ocischema . Manifest {
Versioned : manifest . Versioned {
SchemaVersion : 2 ,
MediaType : v1 . MediaTypeImageManifest ,
} ,
}
checkFn := func ( m ocischema . Manifest , rerr error ) {
dm , err := ocischema . FromStruct ( m )
if err != nil {
t . Error ( err )
return
}
_ , err = manifestService . Put ( ctx , dm )
if verr , ok := err . ( distribution . ErrManifestVerification ) ; ok {
// Extract the first error
if len ( verr ) == 2 {
if _ , ok = verr [ 1 ] . ( distribution . ErrManifestBlobUnknown ) ; ok {
err = verr [ 0 ]
}
} else if len ( verr ) == 1 {
err = verr [ 0 ]
}
}
if err != rerr {
t . Errorf ( "%#v: expected %v, got %v" , m , rerr , err )
}
}
type testcase struct {
Desc distribution . Descriptor
URLs [ ] string
Err error
}
layercases := [ ] testcase {
// empty media type
{
distribution . Descriptor { } ,
[ ] string { "http://foo/bar" } ,
digest . ErrDigestInvalidFormat ,
} ,
{
distribution . Descriptor { } ,
nil ,
digest . ErrDigestInvalidFormat ,
} ,
// unknown media type, but blob is present
{
distribution . Descriptor {
Digest : layer . Digest ,
} ,
nil ,
nil ,
} ,
{
distribution . Descriptor {
Digest : layer . Digest ,
} ,
[ ] string { "http://foo/bar" } ,
nil ,
} ,
// gzip layer, but invalid digest
{
distribution . Descriptor {
MediaType : v1 . MediaTypeImageLayerGzip ,
} ,
nil ,
digest . ErrDigestInvalidFormat ,
} ,
{
distribution . Descriptor {
MediaType : v1 . MediaTypeImageLayerGzip ,
} ,
[ ] string { "https://foo/bar" } ,
digest . ErrDigestInvalidFormat ,
} ,
{
distribution . Descriptor {
MediaType : v1 . MediaTypeImageLayerGzip ,
Digest : digest . Digest ( "invalid" ) ,
} ,
nil ,
digest . ErrDigestInvalidFormat ,
} ,
// normal uploaded gzip layer
{
layer ,
nil ,
nil ,
} ,
{
layer ,
[ ] string { "https://foo/bar" } ,
nil ,
} ,
}
for _ , c := range layercases {
m := template
m . Config = config
l := c . Desc
l . URLs = c . URLs
m . Layers = [ ] distribution . Descriptor { l }
checkFn ( m , c . Err )
}
configcases := [ ] testcase {
// valid config
{
config ,
nil ,
nil ,
} ,
// invalid digest
{
distribution . Descriptor {
MediaType : v1 . MediaTypeImageConfig ,
} ,
[ ] string { "https://foo/bar" } ,
digest . ErrDigestInvalidFormat ,
} ,
{
distribution . Descriptor {
MediaType : v1 . MediaTypeImageConfig ,
Digest : digest . Digest ( "invalid" ) ,
} ,
nil ,
digest . ErrDigestInvalidFormat ,
} ,
}
for _ , c := range configcases {
m := template
m . Config = c . Desc
m . Config . URLs = c . URLs
checkFn ( m , c . Err )
}
}