Merge pull request #1231 from tonistiigi/digest-length-check

Validate digest length on parsing
This commit is contained in:
Stephen Day 2015-12-03 12:17:16 -08:00
commit 4cf93714f3
9 changed files with 95 additions and 65 deletions

View file

@ -58,6 +58,9 @@ var (
// ErrDigestInvalidFormat returned when digest format invalid. // ErrDigestInvalidFormat returned when digest format invalid.
ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format") ErrDigestInvalidFormat = fmt.Errorf("invalid checksum digest format")
// ErrDigestInvalidLength returned when digest has invalid length.
ErrDigestInvalidLength = fmt.Errorf("invalid checksum digest length")
// ErrDigestUnsupported returned when the digest algorithm is unsupported. // ErrDigestUnsupported returned when the digest algorithm is unsupported.
ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm") ErrDigestUnsupported = fmt.Errorf("unsupported digest algorithm")
) )
@ -126,8 +129,11 @@ func (d Digest) Validate() error {
return ErrDigestInvalidFormat return ErrDigestInvalidFormat
} }
switch Algorithm(s[:i]) { switch algorithm := Algorithm(s[:i]); algorithm {
case SHA256, SHA384, SHA512: case SHA256, SHA384, SHA512:
if algorithm.Size()*2 != len(s[i+1:]) {
return ErrDigestInvalidLength
}
break break
default: default:
return ErrDigestUnsupported return ErrDigestUnsupported

View file

@ -53,6 +53,16 @@ func TestParseDigest(t *testing.T) {
input: "sha256:d41d8cd98f00b204e9800m98ecf8427e", input: "sha256:d41d8cd98f00b204e9800m98ecf8427e",
err: ErrDigestInvalidFormat, err: ErrDigestInvalidFormat,
}, },
{
// too short
input: "sha256:abcdef0123456789",
err: ErrDigestInvalidLength,
},
{
// too short (from different algorithm)
input: "sha512:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
err: ErrDigestInvalidLength,
},
{ {
input: "foo:d41d8cd98f00b204e9800998ecf8427e", input: "foo:d41d8cd98f00b204e9800998ecf8427e",
err: ErrDigestUnsupported, err: ErrDigestUnsupported,

View file

@ -54,6 +54,15 @@ func (a Algorithm) String() string {
return string(a) return string(a)
} }
// Size returns number of bytes returned by the hash.
func (a Algorithm) Size() int {
h, ok := algorithms[a]
if !ok {
return 0
}
return h.Size()
}
// Set implemented to allow use of Algorithm as a command line flag. // Set implemented to allow use of Algorithm as a command line flag.
func (a *Algorithm) Set(value string) error { func (a *Algorithm) Set(value string) error {
if value == "" { if value == "" {

View file

@ -15,14 +15,14 @@ func assertEqualDigests(t *testing.T, d1, d2 Digest) {
func TestLookup(t *testing.T) { func TestLookup(t *testing.T) {
digests := []Digest{ digests := []Digest{
"sha256:12345", "sha256:1234511111111111111111111111111111111111111111111111111111111111",
"sha256:1234", "sha256:1234111111111111111111111111111111111111111111111111111111111111",
"sha256:12346", "sha256:1234611111111111111111111111111111111111111111111111111111111111",
"sha256:54321", "sha256:5432111111111111111111111111111111111111111111111111111111111111",
"sha256:65431", "sha256:6543111111111111111111111111111111111111111111111111111111111111",
"sha256:64321", "sha256:6432111111111111111111111111111111111111111111111111111111111111",
"sha256:65421", "sha256:6542111111111111111111111111111111111111111111111111111111111111",
"sha256:65321", "sha256:6532111111111111111111111111111111111111111111111111111111111111",
} }
dset := NewSet() dset := NewSet()
@ -55,10 +55,12 @@ func TestLookup(t *testing.T) {
} }
dgst, err = dset.Lookup("sha256:1234") dgst, err = dset.Lookup("sha256:1234")
if err != nil { if err == nil {
t.Fatal("Expected ambiguous error looking up: sha256:1234")
}
if err != ErrDigestAmbiguous {
t.Fatal(err) t.Fatal(err)
} }
assertEqualDigests(t, dgst, digests[1])
dgst, err = dset.Lookup("sha256:12345") dgst, err = dset.Lookup("sha256:12345")
if err != nil { if err != nil {
@ -87,14 +89,14 @@ func TestLookup(t *testing.T) {
func TestAddDuplication(t *testing.T) { func TestAddDuplication(t *testing.T) {
digests := []Digest{ digests := []Digest{
"sha256:1234", "sha256:1234111111111111111111111111111111111111111111111111111111111111",
"sha256:12345", "sha256:1234511111111111111111111111111111111111111111111111111111111111",
"sha256:12346", "sha256:1234611111111111111111111111111111111111111111111111111111111111",
"sha256:54321", "sha256:5432111111111111111111111111111111111111111111111111111111111111",
"sha256:65431", "sha256:6543111111111111111111111111111111111111111111111111111111111111",
"sha512:65431", "sha512:65431111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
"sha512:65421", "sha512:65421111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
"sha512:65321", "sha512:65321111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111",
} }
dset := NewSet() dset := NewSet()
@ -108,7 +110,7 @@ func TestAddDuplication(t *testing.T) {
t.Fatal("Invalid dset size") t.Fatal("Invalid dset size")
} }
if err := dset.Add(Digest("sha256:12345")); err != nil { if err := dset.Add(Digest("sha256:1234511111111111111111111111111111111111111111111111111111111111")); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -116,7 +118,7 @@ func TestAddDuplication(t *testing.T) {
t.Fatal("Duplicate digest insert allowed") t.Fatal("Duplicate digest insert allowed")
} }
if err := dset.Add(Digest("sha384:12345")); err != nil { if err := dset.Add(Digest("sha384:123451111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")); err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -193,14 +195,14 @@ func assertEqualShort(t *testing.T, actual, expected string) {
func TestShortCodeTable(t *testing.T) { func TestShortCodeTable(t *testing.T) {
digests := []Digest{ digests := []Digest{
"sha256:1234", "sha256:1234111111111111111111111111111111111111111111111111111111111111",
"sha256:12345", "sha256:1234511111111111111111111111111111111111111111111111111111111111",
"sha256:12346", "sha256:1234611111111111111111111111111111111111111111111111111111111111",
"sha256:54321", "sha256:5432111111111111111111111111111111111111111111111111111111111111",
"sha256:65431", "sha256:6543111111111111111111111111111111111111111111111111111111111111",
"sha256:64321", "sha256:6432111111111111111111111111111111111111111111111111111111111111",
"sha256:65421", "sha256:6542111111111111111111111111111111111111111111111111111111111111",
"sha256:65321", "sha256:6532111111111111111111111111111111111111111111111111111111111111",
} }
dset := NewSet() dset := NewSet()
@ -215,10 +217,9 @@ func TestShortCodeTable(t *testing.T) {
if len(dump) < len(digests) { if len(dump) < len(digests) {
t.Fatalf("Error unexpected size: %d, expecting %d", len(dump), len(digests)) t.Fatalf("Error unexpected size: %d, expecting %d", len(dump), len(digests))
} }
assertEqualShort(t, dump[digests[0]], "12341")
assertEqualShort(t, dump[digests[0]], "sha256:1234") assertEqualShort(t, dump[digests[1]], "12345")
assertEqualShort(t, dump[digests[1]], "sha256:12345") assertEqualShort(t, dump[digests[2]], "12346")
assertEqualShort(t, dump[digests[2]], "sha256:12346")
assertEqualShort(t, dump[digests[3]], "54") assertEqualShort(t, dump[digests[3]], "54")
assertEqualShort(t, dump[digests[4]], "6543") assertEqualShort(t, dump[digests[4]], "6543")
assertEqualShort(t, dump[digests[5]], "64") assertEqualShort(t, dump[digests[5]], "64")

View file

@ -7,7 +7,7 @@
// //
// // repository.go // // repository.go
// repository := hostname ['/' component]+ // repository := hostname ['/' component]+
// hostname := hostcomponent [':' port-number] // hostname := hostcomponent [':' port-number]
// component := subcomponent [separator subcomponent]* // component := subcomponent [separator subcomponent]*
// subcomponent := alpha-numeric ['-'* alpha-numeric]* // subcomponent := alpha-numeric ['-'* alpha-numeric]*
// hostcomponent := [hostpart '.']* hostpart // hostcomponent := [hostpart '.']* hostpart
@ -24,7 +24,7 @@
// digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ] // digest-algorithm := digest-algorithm-component [ digest-algorithm-separator digest-algorithm-component ]
// digest-algorithm-separator := /[+.-_]/ // digest-algorithm-separator := /[+.-_]/
// digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/ // digest-algorithm-component := /[A-Za-z][A-Za-z0-9]*/
// digest-hex := /[0-9a-fA-F]{32,}/ ; Atleast 128 bit digest value // digest-hex := /[0-9a-fA-F]{32,}/ ; At least 128 bit digest value
package reference package reference
import ( import (

View file

@ -87,6 +87,10 @@ func TestReferenceParse(t *testing.T) {
input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", input: "@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
err: ErrReferenceInvalidFormat, err: ErrReferenceInvalidFormat,
}, },
{
input: "repo@sha256:ffffffffffffffffffffffffffffffffff",
err: digest.ErrDigestInvalidLength,
},
{ {
input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", input: "validname@invaliddigest:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
err: digest.ErrDigestUnsupported, err: digest.ErrDigestUnsupported,
@ -129,11 +133,11 @@ func TestReferenceParse(t *testing.T) {
tag: "xn--n3h.com", tag: "xn--n3h.com",
}, },
{ {
input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode input: "xn--7o8h.com/myimage:xn--7o8h.com@sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", // 🐳.com in punycode
hostname: "xn--7o8h.com", hostname: "xn--7o8h.com",
repository: "xn--7o8h.com/myimage", repository: "xn--7o8h.com/myimage",
tag: "xn--7o8h.com", tag: "xn--7o8h.com",
digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", digest: "sha512:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
}, },
{ {
input: "foo_bar.com:8080", input: "foo_bar.com:8080",
@ -343,9 +347,9 @@ func TestSerialization(t *testing.T) {
}, },
{ {
description: "name with digest", description: "name with digest",
input: "other.com/named@sha256:1234567890098765432112345667890098765", input: "other.com/named@sha256:1234567890098765432112345667890098765432112345667890098765432112",
name: "other.com/named", name: "other.com/named",
digest: "sha256:1234567890098765432112345667890098765", digest: "sha256:1234567890098765432112345667890098765432112345667890098765432112",
}, },
} }
for _, testcase := range testcases { for _, testcase := range testcases {

View file

@ -20,7 +20,7 @@ func CheckBlobDescriptorCache(t *testing.T, provider cache.BlobDescriptorCachePr
} }
func checkBlobDescriptorCacheEmptyRepository(t *testing.T, ctx context.Context, provider cache.BlobDescriptorCacheProvider) { func checkBlobDescriptorCacheEmptyRepository(t *testing.T, ctx context.Context, provider cache.BlobDescriptorCacheProvider) {
if _, err := provider.Stat(ctx, "sha384:abc"); err != distribution.ErrBlobUnknown { if _, err := provider.Stat(ctx, "sha384:abc111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); err != distribution.ErrBlobUnknown {
t.Fatalf("expected unknown blob error with empty store: %v", err) t.Fatalf("expected unknown blob error with empty store: %v", err)
} }
@ -41,7 +41,7 @@ func checkBlobDescriptorCacheEmptyRepository(t *testing.T, ctx context.Context,
t.Fatalf("expected error with invalid digest: %v", err) t.Fatalf("expected error with invalid digest: %v", err)
} }
if err := cache.SetDescriptor(ctx, "sha384:abc", distribution.Descriptor{ if err := cache.SetDescriptor(ctx, "sha384:abc111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", distribution.Descriptor{
Digest: "", Digest: "",
Size: 10, Size: 10,
MediaType: "application/octet-stream"}); err == nil { MediaType: "application/octet-stream"}); err == nil {
@ -52,15 +52,15 @@ func checkBlobDescriptorCacheEmptyRepository(t *testing.T, ctx context.Context,
t.Fatalf("expected error checking for cache item with empty digest: %v", err) t.Fatalf("expected error checking for cache item with empty digest: %v", err)
} }
if _, err := cache.Stat(ctx, "sha384:abc"); err != distribution.ErrBlobUnknown { if _, err := cache.Stat(ctx, "sha384:abc111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111"); err != distribution.ErrBlobUnknown {
t.Fatalf("expected unknown blob error with empty repo: %v", err) t.Fatalf("expected unknown blob error with empty repo: %v", err)
} }
} }
func checkBlobDescriptorCacheSetAndRead(t *testing.T, ctx context.Context, provider cache.BlobDescriptorCacheProvider) { func checkBlobDescriptorCacheSetAndRead(t *testing.T, ctx context.Context, provider cache.BlobDescriptorCacheProvider) {
localDigest := digest.Digest("sha384:abc") localDigest := digest.Digest("sha384:abc111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111")
expected := distribution.Descriptor{ expected := distribution.Descriptor{
Digest: "sha256:abc", Digest: "sha256:abc1111111111111111111111111111111111111111111111111111111111111",
Size: 10, Size: 10,
MediaType: "application/octet-stream"} MediaType: "application/octet-stream"}

View file

@ -385,15 +385,15 @@ func TestLinkPathFuncs(t *testing.T) {
}{ }{
{ {
repo: "foo/bar", repo: "foo/bar",
digest: "sha256:deadbeaf", digest: "sha256:deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
linkPathFn: blobLinkPath, linkPathFn: blobLinkPath,
expected: "/docker/registry/v2/repositories/foo/bar/_layers/sha256/deadbeaf/link", expected: "/docker/registry/v2/repositories/foo/bar/_layers/sha256/deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/link",
}, },
{ {
repo: "foo/bar", repo: "foo/bar",
digest: "sha256:deadbeaf", digest: "sha256:deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
linkPathFn: manifestRevisionLinkPath, linkPathFn: manifestRevisionLinkPath,
expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/deadbeaf/link", expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/deadbeaf98fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855/link",
}, },
} { } {
p, err := testcase.linkPathFn(testcase.repo, testcase.digest) p, err := testcase.linkPathFn(testcase.repo, testcase.digest)

View file

@ -15,31 +15,31 @@ func TestPathMapper(t *testing.T) {
{ {
spec: manifestRevisionPathSpec{ spec: manifestRevisionPathSpec{
name: "foo/bar", name: "foo/bar",
revision: "sha256:abcdef0123456789", revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
}, },
expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789", expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
}, },
{ {
spec: manifestRevisionLinkPathSpec{ spec: manifestRevisionLinkPathSpec{
name: "foo/bar", name: "foo/bar",
revision: "sha256:abcdef0123456789", revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
}, },
expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789/link", expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/link",
}, },
{ {
spec: manifestSignatureLinkPathSpec{ spec: manifestSignatureLinkPathSpec{
name: "foo/bar", name: "foo/bar",
revision: "sha256:abcdef0123456789", revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
signature: "sha256:abcdef0123456789", signature: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
}, },
expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789/signatures/sha256/abcdef0123456789/link", expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/signatures/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/link",
}, },
{ {
spec: manifestSignaturesPathSpec{ spec: manifestSignaturesPathSpec{
name: "foo/bar", name: "foo/bar",
revision: "sha256:abcdef0123456789", revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
}, },
expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789/signatures", expected: "/docker/registry/v2/repositories/foo/bar/_manifests/revisions/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/signatures",
}, },
{ {
spec: manifestTagsPathSpec{ spec: manifestTagsPathSpec{
@ -72,17 +72,17 @@ func TestPathMapper(t *testing.T) {
spec: manifestTagIndexEntryPathSpec{ spec: manifestTagIndexEntryPathSpec{
name: "foo/bar", name: "foo/bar",
tag: "thetag", tag: "thetag",
revision: "sha256:abcdef0123456789", revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
}, },
expected: "/docker/registry/v2/repositories/foo/bar/_manifests/tags/thetag/index/sha256/abcdef0123456789", expected: "/docker/registry/v2/repositories/foo/bar/_manifests/tags/thetag/index/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
}, },
{ {
spec: manifestTagIndexEntryLinkPathSpec{ spec: manifestTagIndexEntryLinkPathSpec{
name: "foo/bar", name: "foo/bar",
tag: "thetag", tag: "thetag",
revision: "sha256:abcdef0123456789", revision: "sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789",
}, },
expected: "/docker/registry/v2/repositories/foo/bar/_manifests/tags/thetag/index/sha256/abcdef0123456789/link", expected: "/docker/registry/v2/repositories/foo/bar/_manifests/tags/thetag/index/sha256/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/link",
}, },
{ {
spec: layerLinkPathSpec{ spec: layerLinkPathSpec{
@ -93,15 +93,15 @@ func TestPathMapper(t *testing.T) {
}, },
{ {
spec: blobDataPathSpec{ spec: blobDataPathSpec{
digest: digest.Digest("tarsum.dev+sha512:abcdefabcdefabcdef908909909"), digest: digest.Digest("tarsum.dev+sha512:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"),
}, },
expected: "/docker/registry/v2/blobs/tarsum/dev/sha512/ab/abcdefabcdefabcdef908909909/data", expected: "/docker/registry/v2/blobs/tarsum/dev/sha512/ab/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/data",
}, },
{ {
spec: blobDataPathSpec{ spec: blobDataPathSpec{
digest: digest.Digest("tarsum.v1+sha256:abcdefabcdefabcdef908909909"), digest: digest.Digest("tarsum.v1+sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789"),
}, },
expected: "/docker/registry/v2/blobs/tarsum/v1/sha256/ab/abcdefabcdefabcdef908909909/data", expected: "/docker/registry/v2/blobs/tarsum/v1/sha256/ab/abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789/data",
}, },
{ {