Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions commands/imagetools/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,9 @@ func parseSource(in string) (*imagetools.Source, error) {
if err := json.Unmarshal([]byte(in), &s.Desc); err != nil {
return nil, errors.WithStack(err)
}
if s.Desc.Digest == "" {
return nil, errors.Errorf("descriptor is missing required 'digest' field (source must be a descriptor JSON, not a manifest or manifest list)")
}
return &s, nil
}

Expand Down
61 changes: 61 additions & 0 deletions commands/imagetools/create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package commands

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

// TestParseSource_manifestRejected is a regression test for
// https://github.com/docker/buildx/issues/2091: piping `imagetools inspect
// --raw` (a manifest/manifest-list JSON) into `imagetools create -f` caused a
// nil-pointer panic because the JSON was accepted as a descriptor but had no
// 'digest' field, producing a zero-value descriptor that drove the pusher to
// write 0 bytes and call Commit() on an uninitialised pipe.
func TestParseSource_manifestRejected(t *testing.T) {
t.Parallel()

cases := []struct{ name, in string }{
{
"docker manifest list",
`{"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.list.v2+json","manifests":[]}`,
},
{
"oci image index",
`{"schemaVersion":2,"mediaType":"application/vnd.oci.image.index.v1+json","manifests":[]}`,
},
{
"descriptor missing digest",
`{"mediaType":"application/vnd.oci.image.index.v1+json","size":256}`,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
_, err := parseSource(tc.in)
require.Error(t, err)
assert.Contains(t, err.Error(), "descriptor is missing required 'digest' field")
})
}
}

func TestParseSource_validInputs(t *testing.T) {
t.Parallel()

// plain digest
src, err := parseSource("sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855")
require.NoError(t, err)
assert.Equal(t, "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", src.Desc.Digest.String())

// registry reference
src, err = parseSource("registry.example.com/myimage:latest")
require.NoError(t, err)
require.NotNil(t, src.Ref)
assert.Equal(t, "registry.example.com/myimage:latest", src.Ref.String())

// descriptor JSON
src, err = parseSource(`{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","size":256}`)
require.NoError(t, err)
assert.Equal(t, "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", src.Desc.Digest.String())
}
Loading