Support images as well as indexes in ImageDetailResolvers (#183)
* build: Generate test data for unsigned and no provenance image indexes * feat: Add function to build index without SBOM or provenance for linux/amd64 platform * feat: add build_image function to build image without SBOM or provenance for linux/amd64 * feat: Rename NO_SBOM_NO_PROVENANCE_INDEX_DIR to UNSIGNED_IMAGE_DIR * feat: support images in details resolvers
This commit is contained in:
@@ -12,7 +12,7 @@ import (
|
||||
const ExpectedStatements = 4
|
||||
|
||||
func TestExtractAnnotatedStatements(t *testing.T) {
|
||||
statements, err := attestation.ExtractAnnotatedStatements(test.UnsignedTestImage(".."), intoto.PayloadType)
|
||||
statements, err := attestation.ExtractAnnotatedStatements(test.UnsignedTestIndex(".."), intoto.PayloadType)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, len(statements), ExpectedStatements, "expected %d statement, got %d", ExpectedStatements, len(statements))
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ func ExampleManifest() {
|
||||
|
||||
ref := "docker/image-signer-verifier:latest"
|
||||
|
||||
digest, err := v1.NewHash("sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620")
|
||||
digest, err := v1.NewHash("sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@ import (
|
||||
)
|
||||
|
||||
// implementation of Resolver that closes over attestations from an oci layout.
|
||||
|
||||
var _ Resolver = (*LayoutResolver)(nil)
|
||||
|
||||
type LayoutResolver struct {
|
||||
*Manifest
|
||||
*oci.ImageSpec
|
||||
@@ -86,38 +89,49 @@ func (r *LayoutResolver) ImagePlatform(_ context.Context) (*v1.Platform, error)
|
||||
}
|
||||
|
||||
func manifestFromOCILayout(path string, platform *v1.Platform) (*Manifest, error) {
|
||||
idx, err := layout.ImageIndexFromPath(path)
|
||||
layoutIndex, err := layout.ImageIndexFromPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
idxm, err := idx.IndexManifest()
|
||||
layoutIndexManifest, err := layoutIndex.IndexManifest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get digest: %w", err)
|
||||
}
|
||||
|
||||
idxDescriptor := idxm.Manifests[0]
|
||||
idxDigest := idxDescriptor.Digest
|
||||
subjectName := idxDescriptor.Annotations[ocispec.AnnotationRefName]
|
||||
layoutDescriptor := layoutIndexManifest.Manifests[0]
|
||||
layoutDescriptorDigest := layoutDescriptor.Digest
|
||||
subjectName := layoutDescriptor.Annotations[ocispec.AnnotationRefName]
|
||||
if _, err := reference.ParseNamed(subjectName); err != nil {
|
||||
// try the containerd annotation if the org.opencontainers.image.ref.name is not a full name
|
||||
subjectName = idxDescriptor.Annotations[containerd.AnnotationImageName]
|
||||
subjectName = layoutDescriptor.Annotations[containerd.AnnotationImageName]
|
||||
if _, err := reference.ParseNamed(subjectName); err != nil {
|
||||
return nil, fmt.Errorf("failed to find subject name in annotations")
|
||||
}
|
||||
}
|
||||
|
||||
mfs, err := idx.ImageIndex(idxDigest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", idxDigest.String(), err)
|
||||
// check if digest refers to an image or an index
|
||||
_, err = layoutIndex.Image(layoutDescriptorDigest)
|
||||
if err == nil {
|
||||
return &Manifest{
|
||||
OriginalLayers: nil,
|
||||
OriginalDescriptor: nil,
|
||||
SubjectName: subjectName,
|
||||
SubjectDescriptor: &layoutDescriptor,
|
||||
}, nil
|
||||
}
|
||||
mfs2, err := mfs.IndexManifest()
|
||||
|
||||
subjectIndex, err := layoutIndex.ImageIndex(layoutDescriptorDigest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", layoutDescriptorDigest.String(), err)
|
||||
}
|
||||
subjectIndexManifest, err := subjectIndex.IndexManifest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
|
||||
}
|
||||
var subjectDescriptor *v1.Descriptor
|
||||
for i := range mfs2.Manifests {
|
||||
manifest := &mfs2.Manifests[i]
|
||||
for i := range subjectIndexManifest.Manifests {
|
||||
manifest := &subjectIndexManifest.Manifests[i]
|
||||
if manifest.Platform != nil {
|
||||
if manifest.Platform.Equals(*platform) {
|
||||
subjectDescriptor = manifest
|
||||
@@ -128,8 +142,8 @@ func manifestFromOCILayout(path string, platform *v1.Platform) (*Manifest, error
|
||||
if subjectDescriptor == nil {
|
||||
return nil, fmt.Errorf("platform not found in index")
|
||||
}
|
||||
for i := range mfs2.Manifests {
|
||||
mf := &mfs2.Manifests[i]
|
||||
for i := range subjectIndexManifest.Manifests {
|
||||
mf := &subjectIndexManifest.Manifests[i]
|
||||
if mf.Annotations[DockerReferenceType] != AttestationManifestType {
|
||||
continue
|
||||
}
|
||||
@@ -138,7 +152,7 @@ func manifestFromOCILayout(path string, platform *v1.Platform) (*Manifest, error
|
||||
continue
|
||||
}
|
||||
|
||||
attestationImage, err := mfs.Image(mf.Digest)
|
||||
attestationImage, err := subjectIndex.Image(mf.Digest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract attestation image with digest %s: %w", mf.Digest.String(), err)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package attestation_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -25,7 +26,7 @@ func TestAttestationFromOCILayout(t *testing.T) {
|
||||
}
|
||||
|
||||
opts := &attestation.SigningOptions{}
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
|
||||
require.NoError(t, err)
|
||||
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
|
||||
require.NoError(t, err)
|
||||
@@ -74,7 +75,7 @@ func TestSubjectNameAnnotations(t *testing.T) {
|
||||
ociLayoutPath string
|
||||
errorStr string
|
||||
}{
|
||||
{name: "oci annotation", ociLayoutPath: test.UnsignedTestImage("..")},
|
||||
{name: "oci annotation", ociLayoutPath: test.UnsignedTestIndex("..")},
|
||||
{name: "containerd annotation", ociLayoutPath: filepath.Join("..", "test", "testdata", "containerd-subject-layout")},
|
||||
{name: "missing subject name", ociLayoutPath: filepath.Join("..", "test", "testdata", "missing-subject-layout"), errorStr: "failed to find subject name in annotations"},
|
||||
}
|
||||
@@ -93,3 +94,14 @@ func TestSubjectNameAnnotations(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestImageDetailsFromImageLayout(t *testing.T) {
|
||||
spec, err := oci.ParseImageSpec(oci.LocalPrefix+test.UnsignedTestImage(".."), oci.WithPlatform("linux/arm64"))
|
||||
require.NoError(t, err)
|
||||
resolver, err := policy.CreateImageDetailsResolver(spec)
|
||||
require.NoError(t, err)
|
||||
desc, err := resolver.ImageDescriptor(context.Background())
|
||||
require.NoError(t, err)
|
||||
digest := desc.Digest.String()
|
||||
assert.Equal(t, "sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390", digest)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package attestation
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/docker/attest/internal/test"
|
||||
"github.com/docker/attest/oci"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
)
|
||||
@@ -36,7 +37,7 @@ func (r MockResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error)
|
||||
if r.DescriptorFn != nil {
|
||||
return r.DescriptorFn()
|
||||
}
|
||||
digest, err := v1.NewHash("sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620")
|
||||
digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ func TestAttestationReferenceTypes(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
opts := &attestation.SigningOptions{}
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
|
||||
require.NoError(t, err)
|
||||
|
||||
indexName := fmt.Sprintf("%s/repo:root", u.Host)
|
||||
@@ -209,7 +209,7 @@ func TestReferencesInDifferentRepo(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
opts := &attestation.SigningOptions{}
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
|
||||
require.NoError(t, err)
|
||||
|
||||
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
|
||||
@@ -233,7 +233,7 @@ func TestReferencesInDifferentRepo(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
opts := &attestation.SigningOptions{}
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
|
||||
require.NoError(t, err)
|
||||
|
||||
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
|
||||
@@ -286,7 +286,7 @@ func TestCorrectArtifactTypeInTagFallback(t *testing.T) {
|
||||
repoName := "repo"
|
||||
|
||||
opts := &attestation.SigningOptions{}
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
|
||||
require.NoError(t, err)
|
||||
|
||||
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestRegistry(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
opts := &attestation.SigningOptions{}
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
|
||||
require.NoError(t, err)
|
||||
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
|
||||
require.NoError(t, err)
|
||||
@@ -46,4 +46,14 @@ func TestRegistry(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
digest := desc.Digest.String()
|
||||
assert.True(t, strings.Contains(digest, "sha256:"))
|
||||
|
||||
// resolver also works with platform specific digest
|
||||
spec, err = oci.ParseImageSpec(fmt.Sprintf("%s@%s", indexName, digest))
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver, err = policy.CreateImageDetailsResolver(spec)
|
||||
require.NoError(t, err)
|
||||
desc, err = resolver.ImageDescriptor(ctx)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, desc.Digest.String(), digest)
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ func TestSimpleStatementSigning(t *testing.T) {
|
||||
PredicateType: attestation.VSAPredicateType,
|
||||
},
|
||||
}
|
||||
digest, err := v1.NewHash("sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620")
|
||||
digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest)
|
||||
require.NoError(t, err)
|
||||
subject := &v1.Descriptor{
|
||||
MediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||
|
||||
@@ -23,14 +23,20 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
UseMockKMS = true
|
||||
|
||||
AWSRegion = "us-east-1"
|
||||
AWSKMSKeyARN = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
|
||||
UseMockKMS = true
|
||||
AWSRegion = "us-east-1"
|
||||
AWSKMSKeyARN = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
|
||||
UnsignedLinuxAMD64ImageDigest = "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"
|
||||
UnsignedLinuxArm64ImageDigest = "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e"
|
||||
)
|
||||
|
||||
func UnsignedTestIndex(rel ...string) string {
|
||||
rel = append(rel, "test", "testdata", "unsigned-index")
|
||||
return filepath.Join(rel...)
|
||||
}
|
||||
|
||||
func UnsignedTestImage(rel ...string) string {
|
||||
rel = append(rel, "test", "testdata", "unsigned-test-image")
|
||||
rel = append(rel, "test", "testdata", "unsigned-image")
|
||||
return filepath.Join(rel...)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func TestRegistryAuth(t *testing.T) {
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
|
||||
require.NoError(t, err)
|
||||
// test cases for ecr, gcr and dockerhub
|
||||
testCases := []struct {
|
||||
|
||||
@@ -67,7 +67,7 @@ func TestRefToPurl(t *testing.T) {
|
||||
|
||||
// Test fix for https://github.com/docker/secure-artifacts-team-issues/issues/202
|
||||
func TestImageDigestForPlatform(t *testing.T) {
|
||||
idx, err := layout.ImageIndexFromPath(test.UnsignedTestImage(".."))
|
||||
idx, err := layout.ImageIndexFromPath(test.UnsignedTestIndex(".."))
|
||||
assert.NoError(t, err)
|
||||
|
||||
idxm, err := idx.IndexManifest()
|
||||
@@ -86,14 +86,14 @@ func TestImageDigestForPlatform(t *testing.T) {
|
||||
desc, err := oci.ImageDescriptor(mfs2, p)
|
||||
assert.NoError(t, err)
|
||||
digest := desc.Digest.String()
|
||||
assert.Equal(t, "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", digest)
|
||||
assert.Equal(t, test.UnsignedLinuxAMD64ImageDigest, digest)
|
||||
|
||||
p, err = oci.ParsePlatform("linux/arm64")
|
||||
assert.NoError(t, err)
|
||||
desc, err = oci.ImageDescriptor(mfs2, p)
|
||||
assert.NoError(t, err)
|
||||
digest = desc.Digest.String()
|
||||
assert.Equal(t, "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", digest)
|
||||
assert.Equal(t, test.UnsignedLinuxArm64ImageDigest, digest)
|
||||
}
|
||||
|
||||
func TestWithoutTag(t *testing.T) {
|
||||
|
||||
@@ -18,7 +18,7 @@ import (
|
||||
|
||||
func TestSavingIndex(t *testing.T) {
|
||||
outputLayout := test.CreateTempDir(t, "", "mirror-test")
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := context.Background()
|
||||
@@ -73,7 +73,7 @@ func TestSavingReferrers(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
digest, err := v1.NewHash("sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620")
|
||||
digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest)
|
||||
require.NoError(t, err)
|
||||
subject := &v1.Descriptor{
|
||||
MediaType: "application/vnd.oci.image.manifest.v1+json",
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/attest/attestation"
|
||||
@@ -357,6 +358,7 @@ func TestVerifySubject(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
digestHex := strings.TrimPrefix(test.UnsignedLinuxAMD64ImageDigest, "sha256:")
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
defaultResolver.Image = tc.img
|
||||
@@ -365,7 +367,7 @@ func TestVerifySubject(t *testing.T) {
|
||||
return &v1.Platform{Architecture: "amd64", OS: "linux"}, nil
|
||||
}
|
||||
// digest from mock resolver
|
||||
tc.subject[0].Digest = map[string]string{"sha256": "da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"}
|
||||
tc.subject[0].Digest = map[string]string{"sha256": digestHex}
|
||||
if tc.digest != "" {
|
||||
tc.subject[0].Digest = map[string]string{"sha256": tc.digest}
|
||||
}
|
||||
@@ -381,7 +383,7 @@ func TestVerifySubject(t *testing.T) {
|
||||
subject := []intoto.Subject{
|
||||
{
|
||||
Name: "pkg:docker/alpine@latest?platform=linux%2Famd64",
|
||||
Digest: map[string]string{"sha256": "da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"},
|
||||
Digest: map[string]string{"sha256": digestHex},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -10,36 +10,45 @@ function check_command () {
|
||||
|
||||
function cleanup_testdata () {
|
||||
echo "Cleaning up existing testdata..."
|
||||
rm -rf "${TESTDATA_PATH:?}/${UNSIGNED_INDEX_DIR:?}"
|
||||
rm -rf "${TESTDATA_PATH:?}/${NO_PROVENANCE_INDEX_DIR:?}"
|
||||
rm -rf "${TESTDATA_PATH:?}/${UNSIGNED_IMAGE_DIR:?}"
|
||||
rm -rf "${TESTDATA_PATH:?}/${NO_PROVENANCE_IMAGE_DIR:?}"
|
||||
}
|
||||
|
||||
function build_unsigned_image () {
|
||||
function build_unsigned_index () {
|
||||
echo "Building $UNSIGNED_INDEX_DIR..."
|
||||
docker buildx build "$TEST_INDEX_DOCKERFILE_PATH" --sbom true --provenance true --platform linux/amd64,linux/arm64 \
|
||||
--output type=oci,tar=false,name="$TEST_INDEX_REPO:$TEST_INDEX_TAG",dest="$TESTDATA_PATH/$UNSIGNED_INDEX_DIR"
|
||||
}
|
||||
|
||||
function build_no_provenance_index () {
|
||||
echo "Building unsigned $NO_PROVENANCE_INDEX_DIR..."
|
||||
docker buildx build "$TEST_INDEX_DOCKERFILE_PATH" --sbom true --provenance false --platform linux/amd64,linux/arm64 \
|
||||
--output type=oci,tar=false,name="$TEST_INDEX_REPO:$TEST_INDEX_TAG",dest="$TESTDATA_PATH/$NO_PROVENANCE_INDEX_DIR"
|
||||
}
|
||||
|
||||
function build_image () {
|
||||
echo "Building $UNSIGNED_IMAGE_DIR..."
|
||||
docker buildx build "$TEST_IMAGE_DOCKERFILE_PATH" --sbom true --provenance true --platform linux/amd64,linux/arm64 \
|
||||
--output type=oci,tar=false,name="$TEST_IMAGE_REPO:$TEST_IMAGE_TAG",dest="$TESTDATA_PATH/$UNSIGNED_IMAGE_DIR"
|
||||
}
|
||||
|
||||
function build_no_provenance_image () {
|
||||
echo "Building unsigned $NO_PROVENANCE_IMAGE_DIR..."
|
||||
docker buildx build "$TEST_IMAGE_DOCKERFILE_PATH" --sbom true --provenance false --platform linux/amd64,linux/arm64 \
|
||||
--output type=oci,tar=false,name="$TEST_IMAGE_REPO:$TEST_IMAGE_TAG",dest="$TESTDATA_PATH/$NO_PROVENANCE_IMAGE_DIR"
|
||||
docker buildx build "$TEST_INDEX_DOCKERFILE_PATH" --sbom false --provenance false --platform linux/amd64 \
|
||||
--output type=oci,tar=false,name="$TEST_INDEX_REPO:$TEST_INDEX_TAG",dest="$TESTDATA_PATH/$UNSIGNED_IMAGE_DIR"
|
||||
}
|
||||
|
||||
# Check required commands
|
||||
check_command docker
|
||||
|
||||
TESTDATA_PATH="../test/testdata"
|
||||
TEST_IMAGE_DOCKERFILE_PATH="../test"
|
||||
TEST_IMAGE_REPO="test-image"
|
||||
TEST_IMAGE_TAG="test"
|
||||
UNSIGNED_IMAGE_DIR="unsigned-test-image"
|
||||
NO_PROVENANCE_IMAGE_DIR="no-provenance-image"
|
||||
TEST_INDEX_DOCKERFILE_PATH="../test"
|
||||
TEST_INDEX_REPO="test-image"
|
||||
TEST_INDEX_TAG="test"
|
||||
UNSIGNED_INDEX_DIR="unsigned-index"
|
||||
NO_PROVENANCE_INDEX_DIR="no-provenance-index"
|
||||
UNSIGNED_IMAGE_DIR="unsigned-image"
|
||||
ATTESTATION_PAYLOADTYPE="application/vnd.in-toto+json"
|
||||
|
||||
# Run steps
|
||||
cleanup_testdata
|
||||
build_unsigned_image
|
||||
build_no_provenance_image
|
||||
build_unsigned_index
|
||||
build_no_provenance_index
|
||||
build_image
|
||||
|
||||
echo "Process completed successfully."
|
||||
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
NoProvenanceImage = filepath.Join("test", "testdata", "no-provenance-image")
|
||||
NoProvenanceImage = filepath.Join("test", "testdata", "no-provenance-index")
|
||||
PassPolicyDir = filepath.Join("test", "testdata", "local-policy-pass")
|
||||
PassMirrorPolicyDir = filepath.Join("test", "testdata", "local-policy-mirror")
|
||||
PassNoTLPolicyDir = filepath.Join("test", "testdata", "local-policy-no-tl")
|
||||
@@ -35,8 +35,8 @@ func TestSignVerifyOCILayout(t *testing.T) {
|
||||
expectedAttestations int
|
||||
replace bool
|
||||
}{
|
||||
{"signed replaced", test.UnsignedTestImage(), 0, 4, true},
|
||||
{"without replace", test.UnsignedTestImage(), 4, 4, false},
|
||||
{"signed replaced", test.UnsignedTestIndex(), 0, 4, true},
|
||||
{"without replace", test.UnsignedTestIndex(), 4, 4, false},
|
||||
// image without provenance doesn't fail
|
||||
{"no provenance (replace)", NoProvenanceImage, 0, 2, true},
|
||||
{"no provenance (no replace)", NoProvenanceImage, 2, 2, false},
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e"]}}
|
||||
@@ -1 +0,0 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850"]}}
|
||||
1
test/testdata/no-provenance-image/index.json
vendored
1
test/testdata/no-provenance-image/index.json
vendored
@@ -1 +0,0 @@
|
||||
{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:1effe3a77c594e579388dc4553dbbe762e4457a099ab8b706e67f5f9fc934701","size":1607,"annotations":{"org.opencontainers.image.created":"2024-04-29T10:23:48Z","org.opencontainers.image.ref.name":"docker.io/library/test-image:test"}}]}
|
||||
@@ -3,13 +3,13 @@
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:b6ef78de3633e45d1c08019fbabb4464fabd6dd32e82c67ea2b2a3c4e8bacdf5",
|
||||
"digest": "sha256:bb0ed50656ccdb2eb114407de579554426777d6dc0e4206a6f746afb4ee5237e",
|
||||
"size": 167
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850",
|
||||
"digest": "sha256:618f1e2f903648dde23cc38dc0ed7eed83d5394a6902bb7bfae8fa707c2e5c33",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
Binary file not shown.
@@ -3,13 +3,13 @@
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:2e82727457f04f320b643cb6e13bcbafb8e0dc0adc0443f1a25666f9518c5071",
|
||||
"digest": "sha256:816b20ea86474dcfb2906ffaf4410262dfcb0d49fdfb60698775f7bc10aad7fb",
|
||||
"size": 167
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e",
|
||||
"digest": "sha256:f0dac65dd0ff6a656c419c654ac672c38029a3f1a4b4acce062bd2f5a923ffae",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
@@ -4,7 +4,7 @@
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"digest": "sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
@@ -13,7 +13,7 @@
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"digest": "sha256:52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "arm64",
|
||||
@@ -22,10 +22,10 @@
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:2aaebbb079957470e7c0adddbb054b2b4c01f717d408efba753da2bf6e8905da",
|
||||
"digest": "sha256:059eea09507d0f904b8892ee59fcd3ddec1a637fc40fb7c83c432c6ff27e2f91",
|
||||
"size": 558,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"vnd.docker.reference.digest": "sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
@@ -35,10 +35,10 @@
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:9b009d6b84b1ed941070b3f919823446286a674ad669d0baa8ab2c358aeb3a82",
|
||||
"digest": "sha256:0b1ee0f360b073d2f76ceed15a63e291659fbcc6c3caf3be39e437d8344b520e",
|
||||
"size": 558,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"vnd.docker.reference.digest": "sha256:52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/"},"created":"2024-09-27T16:10:13.292759474Z","history":[{"created":"2024-09-27T16:10:13.292759474Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:81a78ab7aa0b72d665a9c203b4c30f0423e434b789ed95b2d418e60a1b726470"]}}
|
||||
@@ -3,13 +3,13 @@
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:d85d624a324422194b43cccd975b5752cf0acaedd668bb525fcd40c3587cc460",
|
||||
"size": 453
|
||||
"digest": "sha256:c0bd7799c46e00830b4d7cb8c1f622d14aae81643a90be5ec38c9be4bdd70f6c",
|
||||
"size": 438
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad",
|
||||
"digest": "sha256:07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336",
|
||||
"size": 116
|
||||
}
|
||||
]
|
||||
@@ -1 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-6d900ae6-587d-4695-9c01-511801a85b65","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.12.4"],"created":"2024-03-08T16:42:30Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}}
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-b92d7d2e-7ffe-4d0a-8194-9af68e80e169","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.15.2"],"created":"2024-09-27T16:10:21Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}}
|
||||
@@ -3,13 +3,13 @@
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:1c70b3e7c3a57801501ec127aa6c918c390c373294ec4fc48f2c6fe703fcc6fe",
|
||||
"size": 453
|
||||
"digest": "sha256:363133d587b90ff7a21f7b32a96be8422c6799683f0e1e6d71de5c03a82ab35e",
|
||||
"size": 438
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad",
|
||||
"digest": "sha256:07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336",
|
||||
"size": 116
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:f0dac65dd0ff6a656c419c654ac672c38029a3f1a4b4acce062bd2f5a923ffae"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:618f1e2f903648dde23cc38dc0ed7eed83d5394a6902bb7bfae8fa707c2e5c33"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/"},"created":"2024-09-27T16:10:13.292759474Z","history":[{"created":"2024-09-27T16:10:13.292759474Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:81a78ab7aa0b72d665a9c203b4c30f0423e434b789ed95b2d418e60a1b726470"]}}
|
||||
@@ -1 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Farm64","digest":{"sha256":"7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-6d900ae6-587d-4695-9c01-511801a85b65","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.12.4"],"created":"2024-03-08T16:42:30Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}}
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Farm64","digest":{"sha256":"52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-b92d7d2e-7ffe-4d0a-8194-9af68e80e169","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.15.2"],"created":"2024-09-27T16:10:21Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}}
|
||||
1
test/testdata/no-provenance-index/index.json
vendored
Normal file
1
test/testdata/no-provenance-index/index.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:1e3839ac14fba8c5e4db574df2046ce21a9e012e4030305cea97ad3f07f81a4a","size":1607,"annotations":{"org.opencontainers.image.created":"2024-09-27T20:22:06Z","org.opencontainers.image.ref.name":"docker.io/library/test-image:test"}}]}
|
||||
Binary file not shown.
@@ -0,0 +1 @@
|
||||
{"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/"},"created":"2024-09-27T16:10:13.292759474Z","history":[{"created":"2024-09-27T16:10:13.292759474Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:81a78ab7aa0b72d665a9c203b4c30f0423e434b789ed95b2d418e60a1b726470"]}}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:363133d587b90ff7a21f7b32a96be8422c6799683f0e1e6d71de5c03a82ab35e",
|
||||
"size": 438
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336",
|
||||
"size": 116
|
||||
}
|
||||
]
|
||||
}
|
||||
1
test/testdata/unsigned-image/index.json
vendored
Normal file
1
test/testdata/unsigned-image/index.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.manifest.v1+json","digest":"sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390","size":476,"annotations":{"org.opencontainers.image.created":"2024-09-27T20:22:07Z","org.opencontainers.image.ref.name":"docker.io/library/test-image:test"},"platform":{"architecture":"amd64","os":"linux"}}]}
|
||||
1
test/testdata/unsigned-index/oci-layout
vendored
Normal file
1
test/testdata/unsigned-index/oci-layout
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"imageLayoutVersion":"1.0.0"}
|
||||
@@ -1 +0,0 @@
|
||||
{"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","OnBuild":null},"created":"2024-03-08T16:42:30.065465358Z","history":[{"created":"2024-03-08T16:42:30.065465358Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:b842af8c2f1451ffc802ae4139819eaea8441223357642548d8a25ab5c52cff7"]}}
|
||||
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","OnBuild":null},"created":"2024-03-08T16:42:30.065465358Z","history":[{"created":"2024-03-08T16:42:30.065465358Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:b842af8c2f1451ffc802ae4139819eaea8441223357642548d8a25ab5c52cff7"]}}
|
||||
@@ -80,7 +80,7 @@ func TestVSA(t *testing.T) {
|
||||
opts := &attestation.SigningOptions{
|
||||
TransparencyLog: tlog.GetMockTL(),
|
||||
}
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage())
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex())
|
||||
assert.NoError(t, err)
|
||||
signedManifests, err := SignStatements(ctx, attIdx.Index, signer, opts)
|
||||
require.NoError(t, err)
|
||||
@@ -106,7 +106,7 @@ func TestVSA(t *testing.T) {
|
||||
assert.Empty(t, results.Violations)
|
||||
|
||||
if assert.NotNil(t, results.Input) {
|
||||
assert.Equal(t, "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", results.Input.Digest)
|
||||
assert.Equal(t, test.UnsignedLinuxAMD64ImageDigest, results.Input.Digest)
|
||||
assert.NotNil(t, results.Input.Tag)
|
||||
}
|
||||
|
||||
@@ -135,7 +135,7 @@ func TestVerificationFailure(t *testing.T) {
|
||||
opts := &attestation.SigningOptions{
|
||||
TransparencyLog: tlog.GetMockTL(),
|
||||
}
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage())
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex())
|
||||
assert.NoError(t, err)
|
||||
signedManifests, err := SignStatements(ctx, attIdx.Index, signer, opts)
|
||||
require.NoError(t, err)
|
||||
@@ -216,7 +216,7 @@ func TestSignVerify(t *testing.T) {
|
||||
{name: "mirror with verification", signTL: false, policyDir: LocalKeysPolicy, imageName: "mirror.org/library/test-image:test"},
|
||||
}
|
||||
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage())
|
||||
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex())
|
||||
assert.NoError(t, err)
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user