Merge pull request #13 from docker/feat--add-attest-sign/verify
feat: add attest sign/verify
This commit is contained in:
130
README.md
130
README.md
@@ -1,2 +1,130 @@
|
||||
# attest
|
||||
Library to create, verify, and evaluate policy for attestations on container images
|
||||
library to create, verify, and evaluate policy for attestations on container images
|
||||
|
||||
# usage
|
||||
## verifying attestations
|
||||
1. create a TUF client
|
||||
* using OCI registry for TUF
|
||||
```go
|
||||
tufOutputPath = "/.docker/tuf"
|
||||
metadataURI = "docker/tuf-metadata:latest"
|
||||
targetsURI = "docker/tuf-targets"
|
||||
tufClient, err := tuf.NewTufClient(embed.DefaultRoot, tufOutputPath, metadataURI, targetsURI)
|
||||
```
|
||||
* using HTTPS for TUF
|
||||
```go
|
||||
tufOutputPath = "/.docker/tuf"
|
||||
metadataURI = "https://docker.github.io/tuf/metadata"
|
||||
targetsURI = "https://docker.github.io/tuf/targets"
|
||||
tufClient, err := tuf.NewTufClient(embed.DefaultRoot, tufOutputPath, metadataURI, targetsURI)
|
||||
```
|
||||
|
||||
1. configure an attestation resolver
|
||||
* using OCI registry
|
||||
```go
|
||||
var resolver oci.AttestationResolver
|
||||
resolver = &oci.RegistryResolver{
|
||||
Image: image, // path to image index in OCI registry containing image attestations (e.g. docker/nginx:latest)
|
||||
Platform: platform, // platform of subject image (image that attestations are being verified against)
|
||||
}
|
||||
```
|
||||
* using local OCI layout
|
||||
```go
|
||||
var resolver oci.AttestationResolver
|
||||
resolver = &oci.OCILayoutResolver{
|
||||
Path: path, // file path to OCI layout containing image attestations (e.g. /myimage)
|
||||
Platform: platform, // platform of subject image (image that attestations are being verified against)
|
||||
}
|
||||
```
|
||||
|
||||
1. configure policy options
|
||||
```go
|
||||
opts := &policy.PolicyOptions{
|
||||
TufClient: tufClient,
|
||||
LocalTargetsDir: "/.docker/policy", // location to store policy files downloaded from TUF
|
||||
LocalPolicyDir: "", // overrides TUF policy for local policy files
|
||||
}
|
||||
```
|
||||
|
||||
1. verify attestations
|
||||
```go
|
||||
policy, err := attest.Verify(ctx, opts, resolver)
|
||||
if err != nil {
|
||||
return false // failed policy or attestation signature verification
|
||||
}
|
||||
if policy {
|
||||
return true // passed policy
|
||||
}
|
||||
return true // no policy for image
|
||||
```
|
||||
|
||||
## signing attestations
|
||||
1. generate an image with intoto Statements (optional)
|
||||
```sh
|
||||
docker buildx build <PATH TO DOCKERFILE> --sbom true --provenance true --output type=oci,tar=false,name=<REPO>:<TAG>,dest=<OUTPUT DIR>
|
||||
```
|
||||
|
||||
1. confgiure a `dsse.SignerVerifier`
|
||||
```go
|
||||
var signer dsse.SignerVerifier
|
||||
signer, err = signerverifier.GetAWSSigner(cmd.Context(), aws_arn, aws_region)
|
||||
```
|
||||
|
||||
1. configure signing options
|
||||
```go
|
||||
opts := &attest.SigningOptions{
|
||||
Replace: true, // replace unsigned intoto statements with signed intoto attestations, otherwise leave in place
|
||||
}
|
||||
```
|
||||
* add [Verification Summary Attestation (VSA)](https://slsa.dev/spec/v1.0/verification_summary) for all intoto attestations (optional)
|
||||
```go
|
||||
opts.VSAOptions = &attestation.VSAOptions{
|
||||
BuildLevel: "SLSA_BUILD_LEVEL_" + slsaBuildLevel,
|
||||
PolicyURI: slsaPolicyUri,
|
||||
VerifierID: slsaVerifierId,
|
||||
}
|
||||
```
|
||||
1. load attestations
|
||||
* oci registry
|
||||
```go
|
||||
ref := "docker/attest:latest"
|
||||
att, err := oci.AttestationIndexFromRemote(ref)
|
||||
```
|
||||
* local filepath
|
||||
```go
|
||||
path := "/test-image"
|
||||
att, err := oci.AttestationIndexFromPath(path)
|
||||
```
|
||||
|
||||
1. sign attestations
|
||||
```go
|
||||
signedImageIndex, err := attest.Sign(ctx, att, signer, opts)
|
||||
```
|
||||
`attest.Sign()` iterates over attestation manifests in the image index and signs all intoto statements (optionally generates a VSA), returning a mutated ImageIndex with all intoto statements signed as attestations.
|
||||
|
||||
1. save output (optional)
|
||||
* push to oci registry
|
||||
```go
|
||||
err = mirror.PushToRegistry(signedImageIndex, ref)
|
||||
```
|
||||
* save to local filesystem
|
||||
```go
|
||||
idx := v1.ImageIndex(empty.Index)
|
||||
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{
|
||||
Add: signedImageIndex,
|
||||
Descriptor: v1.Descriptor{
|
||||
Annotations: map[string]string{
|
||||
oci.OciReferenceTarget: att.Name,
|
||||
},
|
||||
},
|
||||
})
|
||||
err = mirror.SaveAsOCILayout(idx, path)
|
||||
```
|
||||
|
||||
## mirroring TUF repositories
|
||||
TODO: write content for this outline
|
||||
### mirroring TUF metadata to OCI
|
||||
#### delegated metadata
|
||||
### mirroring TUF targets to OCI
|
||||
#### delegated targets
|
||||
### using `go-tuf` OCI registry client
|
||||
|
||||
@@ -2,13 +2,22 @@ package test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/attest/pkg/attestation"
|
||||
"github.com/docker/attest/pkg/oci"
|
||||
"github.com/docker/attest/pkg/policy"
|
||||
"github.com/docker/attest/pkg/signerverifier"
|
||||
"github.com/docker/attest/pkg/tlog"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/layout"
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/secure-systems-lab/go-securesystemslib/dsse"
|
||||
)
|
||||
|
||||
@@ -77,10 +86,121 @@ func GetMockSigner(ctx context.Context) (dsse.SignerVerifier, error) {
|
||||
return signerverifier.GenKeyPair()
|
||||
}
|
||||
|
||||
type MockPolicyEvaluator struct {
|
||||
EvaluateFunc func(ctx context.Context, resolver oci.AttestationResolver, policy []*policy.PolicyFile, input *policy.PolicyInput) error
|
||||
}
|
||||
|
||||
func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*policy.PolicyFile, input *policy.PolicyInput) error {
|
||||
if pe.EvaluateFunc != nil {
|
||||
return pe.EvaluateFunc(ctx, resolver, policy, input)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetMockPolicy() policy.PolicyEvaluator {
|
||||
return &policy.MockPolicyEvaluator{
|
||||
return &MockPolicyEvaluator{
|
||||
EvaluateFunc: func(ctx context.Context, resolver oci.AttestationResolver, policy []*policy.PolicyFile, input *policy.PolicyInput) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type AnnotatedStatement struct {
|
||||
OCIDescriptor *v1.Descriptor
|
||||
InTotoStatement *intoto.Statement
|
||||
Annotations map[string]string
|
||||
}
|
||||
|
||||
func ExtractAnnotatedStatements(path string, mediaType string) ([]*AnnotatedStatement, error) {
|
||||
idx, err := layout.ImageIndexFromPath(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load image index: %w", err)
|
||||
}
|
||||
|
||||
idxm, err := idx.IndexManifest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get digest: %w", err)
|
||||
}
|
||||
idxDigest := idxm.Manifests[0].Digest
|
||||
|
||||
mfs, err := idx.ImageIndex(idxDigest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", idxDigest.String(), err)
|
||||
}
|
||||
mfs2, err := mfs.IndexManifest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
|
||||
}
|
||||
|
||||
var statements []*AnnotatedStatement
|
||||
|
||||
for _, mf := range mfs2.Manifests {
|
||||
if mf.Annotations["vnd.docker.reference.type"] != "attestation-manifest" {
|
||||
continue
|
||||
}
|
||||
|
||||
attestationImage, err := mfs.Image(mf.Digest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract attestation image with digest %s: %w", mf.Digest.String(), err)
|
||||
}
|
||||
layers, err := attestationImage.Layers()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract layers from attestation image: %w", err)
|
||||
}
|
||||
|
||||
for _, layer := range layers {
|
||||
// parse layer blob as json
|
||||
mt, err := layer.MediaType()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get layer media type: %w", err)
|
||||
}
|
||||
|
||||
if string(mt) != mediaType {
|
||||
continue
|
||||
}
|
||||
r, err := layer.Uncompressed()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get layer contents: %w", err)
|
||||
}
|
||||
defer r.Close()
|
||||
var intotoStatement = new(intoto.Statement)
|
||||
var desc *v1.Descriptor
|
||||
if strings.HasSuffix(string(mt), "+dsse") {
|
||||
var env = new(attestation.Envelope)
|
||||
err = json.NewDecoder(r).Decode(env)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode env: %w", err)
|
||||
}
|
||||
payload, err := base64.StdEncoding.Strict().DecodeString(env.Payload)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode payload: %w", err)
|
||||
}
|
||||
err = json.Unmarshal([]byte(payload), intotoStatement)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode %s statement: %w", mediaType, err)
|
||||
}
|
||||
} else {
|
||||
desc := new(v1.Descriptor)
|
||||
err = json.NewDecoder(r).Decode(desc)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode statement: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
layerDesc, err := partial.Descriptor(layer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get descriptor for layer: %w", err)
|
||||
}
|
||||
annotations := make(map[string]string)
|
||||
for k, v := range layerDesc.Annotations {
|
||||
annotations[k] = v
|
||||
}
|
||||
statements = append(statements, &AnnotatedStatement{
|
||||
OCIDescriptor: desc,
|
||||
InTotoStatement: intotoStatement,
|
||||
Annotations: annotations,
|
||||
})
|
||||
}
|
||||
}
|
||||
return statements, nil
|
||||
}
|
||||
|
||||
23
internal/test/test_test.go
Normal file
23
internal/test/test_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
UnsignedTestImage = filepath.Join("..", "..", "test", "testdata", "unsigned-test-image")
|
||||
)
|
||||
|
||||
const (
|
||||
ExpectedStatements = 4
|
||||
)
|
||||
|
||||
func TestExtractAnnotatedStatements(t *testing.T) {
|
||||
statements, err := ExtractAnnotatedStatements(UnsignedTestImage, intoto.PayloadType)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, len(statements), ExpectedStatements, "expected %d statement, got %d", ExpectedStatements, len(statements))
|
||||
}
|
||||
139
pkg/attest/sign.go
Normal file
139
pkg/attest/sign.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package attest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/attest/pkg/attestation"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/empty"
|
||||
"github.com/google/go-containerregistry/pkg/v1/match"
|
||||
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
"github.com/google/go-containerregistry/pkg/v1/static"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/secure-systems-lab/go-securesystemslib/dsse"
|
||||
)
|
||||
|
||||
func Sign(ctx context.Context, idx v1.ImageIndex, signer dsse.SignerVerifier, opts *SigningOptions) (v1.ImageIndex, error) {
|
||||
// extract attestation manifests from index
|
||||
attestationManifests, err := attestation.GetAttestationManifestsFromIndex(idx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get attestation manifests: %w", err)
|
||||
}
|
||||
|
||||
// sign every attestation layer in each manifest
|
||||
for _, manifest := range attestationManifests {
|
||||
attestationLayers, err := attestation.GetAttestationsFromImage(manifest.Attestation.Image)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
|
||||
}
|
||||
signedLayers, err := signLayers(ctx, attestationLayers, signer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sign attestations: %w", err)
|
||||
}
|
||||
if opts.VSAOptions != nil {
|
||||
newLayer, err := generateVSA(ctx, manifest, signer, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate VSA: %w", err)
|
||||
}
|
||||
signedLayers = append(signedLayers, *newLayer)
|
||||
}
|
||||
newImg, err := addSignedLayers(signedLayers, manifest, opts)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to add signed layers: %w", err)
|
||||
}
|
||||
newDesc, err := partial.Descriptor(newImg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get descriptor: %w", err)
|
||||
}
|
||||
cf, err := manifest.Attestation.Image.ConfigFile()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get config file: %w", err)
|
||||
}
|
||||
newDesc.Platform = cf.Platform()
|
||||
newDesc.MediaType = manifest.MediaType
|
||||
newDesc.Annotations = manifest.Annotations
|
||||
idx = mutate.RemoveManifests(idx, match.Digests(manifest.Digest))
|
||||
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{
|
||||
Add: newImg,
|
||||
Descriptor: *newDesc,
|
||||
})
|
||||
}
|
||||
return idx, nil
|
||||
}
|
||||
|
||||
// signLayers signs each intoto attestation layer with the given signer
|
||||
func signLayers(ctx context.Context, layers []attestation.AttestationLayer, signer dsse.SignerVerifier) ([]mutate.Addendum, error) {
|
||||
var signedLayers []mutate.Addendum
|
||||
for _, layer := range layers {
|
||||
// only sign intoto layers
|
||||
if layer.MediaType != types.MediaType(intoto.PayloadType) {
|
||||
continue
|
||||
}
|
||||
// mark attestation as experimental
|
||||
layer.Annotations[InTotoReferenceLifecycleStage] = LifecycleStageExperimental
|
||||
|
||||
// sign the statement
|
||||
payload, err := json.Marshal(layer.Statement)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal statement: %w", err)
|
||||
}
|
||||
env, err := attestation.SignDSSE(ctx, payload, intoto.PayloadType, signer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sign statement: %w", err)
|
||||
}
|
||||
mediaType, err := attestation.DSSEMediaType(layer.Statement.PredicateType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get DSSE media type: %w", err)
|
||||
}
|
||||
data, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal envelope: %w", err)
|
||||
}
|
||||
newLayer := static.NewLayer(data, types.MediaType(mediaType))
|
||||
withAnnotations := mutate.Addendum{
|
||||
Layer: newLayer,
|
||||
Annotations: layer.Annotations,
|
||||
}
|
||||
signedLayers = append(signedLayers, withAnnotations)
|
||||
}
|
||||
return signedLayers, nil
|
||||
}
|
||||
|
||||
// addSignedLayers adds signed layers to a new or existing attestation image
|
||||
func addSignedLayers(signedLayers []mutate.Addendum, manifest attestation.AttestationManifest, opts *SigningOptions) (v1.Image, error) {
|
||||
var err error
|
||||
if opts.Replace {
|
||||
// create a new attestation image with only signed layers
|
||||
newImg := empty.Image
|
||||
newImg = mutate.MediaType(newImg, manifest.MediaType)
|
||||
newImg = mutate.ConfigMediaType(newImg, "application/vnd.oci.image.config.v1+json")
|
||||
for _, layer := range signedLayers {
|
||||
newImg, err = mutate.Append(newImg, layer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to append signed layer: %w", err)
|
||||
}
|
||||
}
|
||||
// add any existing unsigned (non-intoto) layers to the new image
|
||||
for _, layer := range manifest.Attestation.Layers {
|
||||
if layer.MediaType != types.MediaType(intoto.PayloadType) {
|
||||
newImg, err = mutate.AppendLayers(newImg, layer.Layer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to append unsigned layer: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return newImg, nil
|
||||
}
|
||||
// Add signed layers to the existing image
|
||||
for _, layer := range signedLayers {
|
||||
manifest.Attestation.Image, err = mutate.Append(manifest.Attestation.Image, layer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to append layer: %w", err)
|
||||
}
|
||||
}
|
||||
return manifest.Attestation.Image, nil
|
||||
}
|
||||
165
pkg/attest/sign_test.go
Normal file
165
pkg/attest/sign_test.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package attest
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/attest/internal/test"
|
||||
"github.com/docker/attest/pkg/attestation"
|
||||
"github.com/docker/attest/pkg/oci"
|
||||
"github.com/docker/attest/pkg/policy"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/empty"
|
||||
"github.com/google/go-containerregistry/pkg/v1/layout"
|
||||
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||
"github.com/google/go-containerregistry/pkg/v1/static"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
v02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
UnsignedTestImage = filepath.Join("..", "..", "test", "testdata", "unsigned-test-image")
|
||||
NoProvenanceImage = filepath.Join("..", "..", "test", "testdata", "no-provenance-image")
|
||||
LocalPolicyDir = filepath.Join("..", "..", "test", "testdata", "local-policy")
|
||||
TestTempDir = "attest-sign-test"
|
||||
)
|
||||
|
||||
func TestSignVerifyOCILayout(t *testing.T) {
|
||||
ctx, signer := test.Setup(t)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
TestImage string
|
||||
expectedStatements int
|
||||
expectedAttestations int
|
||||
replace bool
|
||||
}{
|
||||
|
||||
{"signed replaced (does nothing)", UnsignedTestImage, 0, 6, true},
|
||||
{"without replace", UnsignedTestImage, 4, 6, false},
|
||||
// image without provenance doesn't fail
|
||||
{"no provenance (replace)", NoProvenanceImage, 0, 4, true},
|
||||
{"no provenance (no replace)", NoProvenanceImage, 2, 4, false},
|
||||
}
|
||||
policyResolver := &policy.PolicyOptions{
|
||||
LocalPolicyDir: LocalPolicyDir,
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tempDir := test.CreateTempDir(t, "", TestTempDir)
|
||||
outputLayout := tempDir
|
||||
opts := &SigningOptions{
|
||||
Replace: tc.replace,
|
||||
VSAOptions: &attestation.VSAOptions{
|
||||
BuildLevel: "SLSA_BUILD_LEVEL_3",
|
||||
PolicyURI: "https://docker.com/attest/policy",
|
||||
VerifierID: "https://docker.com",
|
||||
},
|
||||
}
|
||||
attIdx, err := oci.AttestationIndexFromPath(tc.TestImage)
|
||||
assert.NoError(t, err)
|
||||
signedIndex, err := Sign(ctx, attIdx.Index, signer, opts)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// output signed attestations
|
||||
idx := v1.ImageIndex(empty.Index)
|
||||
idx = mutate.AppendManifests(idx, mutate.IndexAddendum{
|
||||
Add: signedIndex,
|
||||
Descriptor: v1.Descriptor{
|
||||
Annotations: map[string]string{
|
||||
oci.OciReferenceTarget: attIdx.Name,
|
||||
},
|
||||
},
|
||||
})
|
||||
_, err = layout.Write(outputLayout, idx)
|
||||
assert.NoError(t, err)
|
||||
|
||||
resolver := &oci.OCILayoutResolver{
|
||||
Path: outputLayout,
|
||||
Platform: "",
|
||||
}
|
||||
policy, err := Verify(ctx, policyResolver, resolver)
|
||||
assert.NoError(t, err)
|
||||
assert.Truef(t, policy, "Policy should have been found")
|
||||
|
||||
mt, _ := attestation.DSSEMediaType(attestation.VSAPredicateType)
|
||||
vsas, err := test.ExtractAnnotatedStatements(tempDir, mt)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, len(vsas), 2, "expected %d vsa statement, got %d", 2, len(vsas))
|
||||
var allEnvelopes []*test.AnnotatedStatement
|
||||
for _, predicate := range []string{intoto.PredicateSPDX, v02.PredicateSLSAProvenance, attestation.VSAPredicateType} {
|
||||
mt, _ := attestation.DSSEMediaType(predicate)
|
||||
statements, err := test.ExtractAnnotatedStatements(tempDir, mt)
|
||||
assert.NoError(t, err)
|
||||
allEnvelopes = append(allEnvelopes, statements...)
|
||||
|
||||
for _, stmt := range statements {
|
||||
assert.Equalf(t, predicate, stmt.Annotations[oci.InTotoPredicateType], "expected predicate-type annotation to be set to %s, got %s", predicate, stmt.Annotations[oci.InTotoPredicateType])
|
||||
assert.Equalf(t, LifecycleStageExperimental, stmt.Annotations[InTotoReferenceLifecycleStage], "expected reference lifecycle stage annotation to be set to %s, got %s", LifecycleStageExperimental, stmt.Annotations[InTotoReferenceLifecycleStage])
|
||||
}
|
||||
}
|
||||
assert.Equalf(t, tc.expectedAttestations, len(allEnvelopes), "expected %d attestations, got %d", tc.expectedAttestations, len(allEnvelopes))
|
||||
statements, err := test.ExtractAnnotatedStatements(tempDir, intoto.PayloadType)
|
||||
assert.NoError(t, err)
|
||||
assert.Equalf(t, tc.expectedStatements, len(statements), "expected %d statement, got %d", tc.expectedStatements, len(statements))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddSignedLayerAnnotations(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
replace bool
|
||||
}{
|
||||
{"replaced", true},
|
||||
{"not replaced", false},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
data := []byte("signed")
|
||||
signedLayer := static.NewLayer(data, types.MediaType(intoto.PayloadType))
|
||||
signedLayers := []mutate.Addendum{
|
||||
{
|
||||
Layer: signedLayer,
|
||||
Annotations: map[string]string{"test": "test"},
|
||||
},
|
||||
}
|
||||
data = []byte("test")
|
||||
testLayer := static.NewLayer(data, types.MediaType(intoto.PayloadType))
|
||||
mediaType := types.OCIManifestSchema1
|
||||
opts := &SigningOptions{
|
||||
Replace: tc.replace,
|
||||
}
|
||||
manifest := attestation.AttestationManifest{
|
||||
MediaType: mediaType,
|
||||
Attestation: attestation.AttestationImage{
|
||||
Image: empty.Image,
|
||||
Layers: []attestation.AttestationLayer{
|
||||
{
|
||||
Layer: testLayer,
|
||||
Statement: &intoto.Statement{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
newImg, err := addSignedLayers(signedLayers, manifest, opts)
|
||||
assert.NoError(t, err)
|
||||
mf, _ := newImg.RawManifest()
|
||||
type Annotations struct {
|
||||
Annotations map[string]string `json:"annotations"`
|
||||
}
|
||||
type Layers struct {
|
||||
Layers []Annotations `json:"layers"`
|
||||
}
|
||||
l := &Layers{}
|
||||
err = json.Unmarshal(mf, l)
|
||||
assert.NoError(t, err)
|
||||
_, ok := l.Layers[0].Annotations["test"]
|
||||
assert.Truef(t, ok, "missing annotations")
|
||||
})
|
||||
}
|
||||
}
|
||||
15
pkg/attest/types.go
Normal file
15
pkg/attest/types.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package attest
|
||||
|
||||
import (
|
||||
"github.com/docker/attest/pkg/attestation"
|
||||
)
|
||||
|
||||
const (
|
||||
InTotoReferenceLifecycleStage = "vnd.docker.lifecycle-stage"
|
||||
LifecycleStageExperimental = "experimental"
|
||||
)
|
||||
|
||||
type SigningOptions struct {
|
||||
Replace bool
|
||||
VSAOptions *attestation.VSAOptions
|
||||
}
|
||||
55
pkg/attest/verify.go
Normal file
55
pkg/attest/verify.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package attest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/attest/pkg/oci"
|
||||
"github.com/docker/attest/pkg/policy"
|
||||
)
|
||||
|
||||
func VerifyAttestations(ctx context.Context, resolver oci.AttestationResolver, files []*policy.PolicyFile) error {
|
||||
digest, err := resolver.ImageDigest(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get image digest: %w", err)
|
||||
}
|
||||
name, err := resolver.ImageName(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get image name: %w", err)
|
||||
}
|
||||
purl, canonical, err := oci.RefToPURL(name, resolver.ImagePlatformStr())
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert ref to purl: %w", err)
|
||||
}
|
||||
input := &policy.PolicyInput{
|
||||
Digest: digest,
|
||||
Purl: purl,
|
||||
IsCanonical: canonical,
|
||||
}
|
||||
|
||||
evaluator, err := policy.GetPolicyEvaluator(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = evaluator.Evaluate(ctx, resolver, files, input)
|
||||
if err != nil {
|
||||
return fmt.Errorf("policy evaluation failed: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Verify(ctx context.Context, opts *policy.PolicyOptions, resolver oci.AttestationResolver) (policyFound bool, err error) {
|
||||
policyFiles, err := policy.ResolvePolicy(ctx, resolver, opts)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to resolve policy: %w", err)
|
||||
}
|
||||
|
||||
// no policy for image -> success
|
||||
if policyFiles == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// policy found -> verify
|
||||
return true, VerifyAttestations(ctx, resolver, policyFiles)
|
||||
}
|
||||
61
pkg/attest/verify_test.go
Normal file
61
pkg/attest/verify_test.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package attest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/attest/internal/test"
|
||||
"github.com/docker/attest/pkg/attestation"
|
||||
"github.com/docker/attest/pkg/oci"
|
||||
"github.com/docker/attest/pkg/policy"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
ExampleAttestation = filepath.Join("..", "..", "test", "testdata", "example_attestation.json")
|
||||
)
|
||||
|
||||
func TestVerifyAttestations(t *testing.T) {
|
||||
ex, err := os.ReadFile(ExampleAttestation)
|
||||
assert.NoError(t, err)
|
||||
|
||||
var env = new(attestation.Envelope)
|
||||
err = json.Unmarshal(ex, env)
|
||||
assert.NoError(t, err)
|
||||
resolver := &oci.MockResolver{
|
||||
Envs: []*attestation.Envelope{env},
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
policyEvaluationError error
|
||||
expectedError error
|
||||
}{
|
||||
{"policy ok", nil, nil},
|
||||
{"policy error", fmt.Errorf("policy error"), fmt.Errorf("policy evaluation failed: policy error")},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
|
||||
mockPE := test.MockPolicyEvaluator{
|
||||
EvaluateFunc: func(ctx context.Context, resolver oci.AttestationResolver, pfs []*policy.PolicyFile, input *policy.PolicyInput) error {
|
||||
return tc.policyEvaluationError
|
||||
},
|
||||
}
|
||||
|
||||
ctx := policy.WithPolicyEvaluator(context.Background(), &mockPE)
|
||||
err = VerifyAttestations(ctx, resolver, nil)
|
||||
if tc.expectedError != nil {
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, tc.expectedError.Error(), err.Error())
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
96
pkg/attest/vsa.go
Normal file
96
pkg/attest/vsa.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package attest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/docker/attest/pkg/attestation"
|
||||
"github.com/docker/attest/pkg/oci"
|
||||
"github.com/google/go-containerregistry/pkg/v1/mutate"
|
||||
"github.com/google/go-containerregistry/pkg/v1/static"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/secure-systems-lab/go-securesystemslib/dsse"
|
||||
)
|
||||
|
||||
// generateVSA generates a VSA from the attestation manifest
|
||||
// TODO: remove signing logic and move generateVSA to attestation/vsa.go
|
||||
func generateVSA(ctx context.Context, manifest attestation.AttestationManifest, signer dsse.SignerVerifier, opts *SigningOptions) (*mutate.Addendum, error) {
|
||||
if len(manifest.Attestation.Layers) == 0 {
|
||||
return nil, fmt.Errorf("no attestations found to generate VSA from")
|
||||
}
|
||||
sub := manifest.Attestation.Layers[0].Statement.Subject[0]
|
||||
stype := manifest.Attestation.Layers[0].Statement.Type
|
||||
|
||||
uri, err := attestation.ToVSAResourceURI(sub)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to generate VSA resource URI: %w", err)
|
||||
}
|
||||
|
||||
inputs := make([]attestation.VSAInputAttestation, 0, len(manifest.Attestation.Layers))
|
||||
for _, att := range manifest.Attestation.Layers {
|
||||
mt, err := att.Layer.MediaType()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get layer media type: %w", err)
|
||||
}
|
||||
if !strings.HasSuffix(string(mt), "+dsse") {
|
||||
continue
|
||||
}
|
||||
dgst, err := att.Layer.Digest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get layer digest: %w", err)
|
||||
}
|
||||
inputs = append(inputs, attestation.VSAInputAttestation{
|
||||
Digest: map[string]string{"sha256": dgst.Hex},
|
||||
MediaType: string(mt),
|
||||
})
|
||||
}
|
||||
vsaStatement := &intoto.Statement{
|
||||
StatementHeader: intoto.StatementHeader{
|
||||
PredicateType: attestation.VSAPredicateType,
|
||||
Type: stype,
|
||||
Subject: manifest.Attestation.Layers[0].Statement.Subject,
|
||||
},
|
||||
Predicate: attestation.VSAPredicate{
|
||||
Verifier: attestation.VSAVerifier{
|
||||
ID: opts.VSAOptions.VerifierID,
|
||||
},
|
||||
TimeVerified: time.Now().UTC().Format(time.RFC3339),
|
||||
ResourceUri: uri,
|
||||
Policy: attestation.VSAPolicy{URI: opts.VSAOptions.PolicyURI},
|
||||
VerificationResult: "PASSED",
|
||||
VerifiedLevels: []string{opts.VSAOptions.BuildLevel},
|
||||
InputAttestations: inputs,
|
||||
},
|
||||
}
|
||||
payload, err := json.Marshal(vsaStatement)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal statement: %w", err)
|
||||
}
|
||||
env, err := attestation.SignDSSE(ctx, payload, intoto.PayloadType, signer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to sign statement: %w", err)
|
||||
}
|
||||
mediaType, err := attestation.DSSEMediaType(vsaStatement.PredicateType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get DSSE media type: %w", err)
|
||||
}
|
||||
|
||||
data, err := json.Marshal(env)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal envelope: %w", err)
|
||||
}
|
||||
mt := types.MediaType(mediaType)
|
||||
newLayer := static.NewLayer(data, mt)
|
||||
ann := make(map[string]string)
|
||||
ann[InTotoReferenceLifecycleStage] = LifecycleStageExperimental
|
||||
ann[oci.InTotoPredicateType] = attestation.VSAPredicateType
|
||||
withAnnotations := mutate.Addendum{
|
||||
Layer: newLayer,
|
||||
Annotations: ann,
|
||||
}
|
||||
return &withAnnotations, nil
|
||||
}
|
||||
82
pkg/attestation/attestation.go
Normal file
82
pkg/attestation/attestation.go
Normal file
@@ -0,0 +1,82 @@
|
||||
package attestation
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/partial"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
)
|
||||
|
||||
// GetAttestationManifestsFromIndex extracts all attestation manifests from an index
|
||||
func GetAttestationManifestsFromIndex(index v1.ImageIndex) ([]AttestationManifest, error) {
|
||||
idx, err := index.IndexManifest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
|
||||
}
|
||||
var attestationManifests []AttestationManifest
|
||||
for _, manifest := range idx.Manifests {
|
||||
if manifest.Annotations[DockerReferenceType] == AttestationManifestType {
|
||||
attestationImage, err := index.Image(manifest.Digest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract attestation image with digest %s: %w", manifest.Digest.String(), err)
|
||||
}
|
||||
attestationLayers, err := GetAttestationsFromImage(attestationImage)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get attestations from image: %w", err)
|
||||
}
|
||||
attestationManifests = append(attestationManifests,
|
||||
AttestationManifest{
|
||||
Manifest: manifest,
|
||||
Attestation: AttestationImage{
|
||||
Layers: attestationLayers,
|
||||
Image: attestationImage},
|
||||
MediaType: manifest.MediaType,
|
||||
Annotations: manifest.Annotations,
|
||||
Digest: manifest.Digest})
|
||||
}
|
||||
}
|
||||
return attestationManifests, nil
|
||||
}
|
||||
|
||||
// GetAttestationsFromImage extracts all attestation layers from an image
|
||||
func GetAttestationsFromImage(image v1.Image) ([]AttestationLayer, error) {
|
||||
layers, err := image.Layers()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract layers from image: %w", err)
|
||||
}
|
||||
var attestationLayers []AttestationLayer
|
||||
for _, layer := range layers {
|
||||
// parse layer blob as json
|
||||
r, err := layer.Uncompressed()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get layer contents: %w", err)
|
||||
}
|
||||
defer r.Close()
|
||||
mt, err := layer.MediaType()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get layer media type: %w", err)
|
||||
}
|
||||
layerDesc, err := partial.Descriptor(layer)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get descriptor for layer: %w", err)
|
||||
}
|
||||
// copy original annotations
|
||||
ann := make(map[string]string)
|
||||
for k, v := range layerDesc.Annotations {
|
||||
ann[k] = v
|
||||
}
|
||||
// only decode intoto statements
|
||||
var stmt = new(intoto.Statement)
|
||||
if mt == types.MediaType(intoto.PayloadType) {
|
||||
err = json.NewDecoder(r).Decode(&stmt)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to decode statement layer contents: %w", err)
|
||||
}
|
||||
}
|
||||
attestationLayers = append(attestationLayers, AttestationLayer{Layer: layer, MediaType: mt, Statement: stmt, Annotations: ann})
|
||||
}
|
||||
return attestationLayers, nil
|
||||
}
|
||||
@@ -1,14 +1,46 @@
|
||||
package attestation
|
||||
|
||||
import "encoding/base64"
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/types"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
v02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
|
||||
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
DockerDsseExtKind = "application/vnd.docker.attestation-verification.v1+json"
|
||||
RekorTlExtKind = "Rekor"
|
||||
DockerReferenceType = "vnd.docker.reference.type"
|
||||
AttestationManifestType = "attestation-manifest"
|
||||
DockerDsseExtKind = "application/vnd.docker.attestation-verification.v1+json"
|
||||
RekorTlExtKind = "Rekor"
|
||||
OCIDescriptorDSSEMediaType = ociv1.MediaTypeDescriptor + "+dsse"
|
||||
)
|
||||
|
||||
var base64Encoding = base64.StdEncoding.Strict()
|
||||
|
||||
type AttestationLayer struct {
|
||||
Statement *intoto.Statement
|
||||
Layer v1.Layer
|
||||
MediaType types.MediaType
|
||||
Annotations map[string]string
|
||||
}
|
||||
|
||||
type AttestationImage struct {
|
||||
Layers []AttestationLayer
|
||||
Image v1.Image
|
||||
}
|
||||
|
||||
type AttestationManifest struct {
|
||||
Manifest v1.Descriptor
|
||||
Attestation AttestationImage
|
||||
MediaType types.MediaType
|
||||
Annotations map[string]string
|
||||
Digest v1.Hash
|
||||
}
|
||||
|
||||
// the following types are needed until https://github.com/secure-systems-lab/dsse/pull/61 is merged
|
||||
type Envelope struct {
|
||||
PayloadType string `json:"payloadType"`
|
||||
@@ -33,3 +65,20 @@ type DockerTlExtension struct {
|
||||
Kind string `json:"kind"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
|
||||
func DSSEMediaType(predicateType string) (string, error) {
|
||||
var predicateName string
|
||||
switch predicateType {
|
||||
case v02.PredicateSLSAProvenance:
|
||||
predicateName = "provenance"
|
||||
case intoto.PredicateSPDX:
|
||||
predicateName = "spdx"
|
||||
case VSAPredicateType:
|
||||
predicateName = "verification_summary"
|
||||
|
||||
default:
|
||||
return "", fmt.Errorf("unknown predicate type %q", predicateType)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("application/vnd.in-toto.%s+dsse", predicateName), nil
|
||||
}
|
||||
|
||||
55
pkg/attestation/vsa.go
Normal file
55
pkg/attestation/vsa.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package attestation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/package-url/packageurl-go"
|
||||
)
|
||||
|
||||
const (
|
||||
VSAPredicateType = "https://slsa.dev/verification_summary/v1"
|
||||
)
|
||||
|
||||
type VSAPredicate struct {
|
||||
Verifier VSAVerifier `json:"verifier"`
|
||||
TimeVerified string `json:"timeVerified"`
|
||||
ResourceUri string `json:"resourceUri"`
|
||||
Policy VSAPolicy `json:"policy"`
|
||||
InputAttestations []VSAInputAttestation `json:"inputAttestations"`
|
||||
VerificationResult string `json:"verificationResult"`
|
||||
VerifiedLevels []string `json:"verifiedLevels"`
|
||||
}
|
||||
|
||||
type VSAVerifier struct {
|
||||
ID string `json:"id"`
|
||||
}
|
||||
|
||||
type VSAPolicy struct {
|
||||
URI string `json:"uri"`
|
||||
}
|
||||
|
||||
type VSAInputAttestation struct {
|
||||
Digest map[string]string `json:"digest"`
|
||||
MediaType string `json:"mediaType"`
|
||||
}
|
||||
|
||||
type VSAOptions struct {
|
||||
BuildLevel string
|
||||
PolicyURI string
|
||||
VerifierID string
|
||||
}
|
||||
|
||||
func ToVSAResourceURI(sub intoto.Subject) (string, error) {
|
||||
//parse purl
|
||||
purl, err := packageurl.FromString(sub.Name)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse package url: %w", err)
|
||||
}
|
||||
quals := purl.Qualifiers.Map()
|
||||
if quals["digest"] == "" {
|
||||
quals["digest"] = "sha256:" + sub.Digest["sha256"]
|
||||
}
|
||||
purl.Qualifiers = packageurl.QualifiersFromMap(quals)
|
||||
return purl.String(), nil
|
||||
}
|
||||
@@ -48,7 +48,9 @@ func PushToRegistry(image any, imageName string) error {
|
||||
return fmt.Errorf("failed to push image index %s: %w", imageName, err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown image type: %T", image)
|
||||
if err := remote.WriteIndex(ref, image.(v1.ImageIndex), remote.WithAuth(auth)); err != nil {
|
||||
return fmt.Errorf("failed to push image index %s: %w", imageName, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -76,7 +78,10 @@ func SaveAsOCILayout(image any, path string) error {
|
||||
return fmt.Errorf("failed to create index: %w", err)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown image type: %T", image)
|
||||
_, err := layout.Write(path, image.(v1.ImageIndex))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create index: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,69 @@
|
||||
package oci
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/google/go-containerregistry/pkg/v1/layout"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
)
|
||||
|
||||
const (
|
||||
DockerReferenceType = "vnd.docker.reference.type"
|
||||
DockerReferenceDigest = "vnd.docker.reference.digest"
|
||||
AttestationManifestType = "attestation-manifest"
|
||||
InTotoPredicateType = "in-toto.io/predicate-type"
|
||||
OciReferenceTarget = "org.opencontainers.image.ref.name"
|
||||
)
|
||||
|
||||
type AttestationIndex struct {
|
||||
Index v1.ImageIndex
|
||||
Name string
|
||||
}
|
||||
|
||||
func AttestationIndexFromPath(path string) (*AttestationIndex, error) {
|
||||
wrapperIdx, err := layout.ImageIndexFromPath(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to load image index: %w", err)
|
||||
}
|
||||
|
||||
idxm, err := wrapperIdx.IndexManifest()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get digest: %w", err)
|
||||
}
|
||||
imageName := idxm.Manifests[0].Annotations[OciReferenceTarget]
|
||||
idxDigest := idxm.Manifests[0].Digest
|
||||
|
||||
idx, err := wrapperIdx.ImageIndex(idxDigest)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", idxDigest.String(), err)
|
||||
}
|
||||
return &AttestationIndex{
|
||||
Index: idx,
|
||||
Name: imageName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func AttestationIndexFromRemote(image string) (*AttestationIndex, error) {
|
||||
ref, err := name.ParseReference(image)
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to parse image name: %v", err)
|
||||
}
|
||||
// Get the authenticator from the default Docker keychain
|
||||
auth, err := authn.DefaultKeychain.Resolve(ref.Context())
|
||||
if err != nil {
|
||||
log.Fatalf("Failed to get authenticator: %v", err)
|
||||
}
|
||||
// Pull the image from the registry
|
||||
idx, err := remote.Index(ref, remote.WithAuth(auth))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to pull image %s: %w", image, err)
|
||||
}
|
||||
return &AttestationIndex{
|
||||
Index: idx,
|
||||
Name: image,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -28,14 +28,3 @@ func GetPolicyEvaluator(ctx context.Context) (PolicyEvaluator, error) {
|
||||
type PolicyEvaluator interface {
|
||||
Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error
|
||||
}
|
||||
|
||||
type MockPolicyEvaluator struct {
|
||||
EvaluateFunc func(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error
|
||||
}
|
||||
|
||||
func (pe *MockPolicyEvaluator) Evaluate(ctx context.Context, resolver oci.AttestationResolver, policy []*PolicyFile, input *PolicyInput) error {
|
||||
if pe.EvaluateFunc != nil {
|
||||
return pe.EvaluateFunc(ctx, resolver, policy, input)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -98,7 +98,6 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
|
||||
policyFiles, err := policy.ResolvePolicy(ctx, tc.resolver, tc.policy)
|
||||
assert.NoErrorf(t, err, "failed to resolve policy")
|
||||
err = re.Evaluate(ctx, tc.resolver, policyFiles, tc.input)
|
||||
|
||||
if tc.expectSuccess {
|
||||
assert.NoErrorf(t, err, "Evaluate failed")
|
||||
} else {
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
type regoEvaluator struct {
|
||||
debug bool
|
||||
query string
|
||||
}
|
||||
|
||||
func NewRegoEvaluator(debug bool) PolicyEvaluator {
|
||||
|
||||
45
scripts/gen-testdata.sh
Executable file
45
scripts/gen-testdata.sh
Executable file
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
set -eo pipefail
|
||||
|
||||
echo "Starting the process to generate testdata..."
|
||||
|
||||
# Define functions
|
||||
function check_command () {
|
||||
command -v "$1" >/dev/null 2>&1 || { echo >&2 "This script requires $1 but it's not installed. Aborting."; exit 1; }
|
||||
}
|
||||
|
||||
function cleanup_testdata () {
|
||||
echo "Cleaning up existing testdata..."
|
||||
rm -rf "${TESTDATA_PATH:?}/${UNSIGNED_IMAGE_DIR:?}"
|
||||
rm -rf "${TESTDATA_PATH:?}/${NO_PROVENANCE_IMAGE_DIR:?}"
|
||||
}
|
||||
|
||||
function build_unsigned_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"
|
||||
}
|
||||
|
||||
# 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"
|
||||
ATTESTATION_PAYLOADTYPE="application/vnd.in-toto+json"
|
||||
|
||||
# Run steps
|
||||
cleanup_testdata
|
||||
build_unsigned_image
|
||||
build_no_provenance_image
|
||||
|
||||
echo "Process completed successfully."
|
||||
5
test/Dockerfile
Normal file
5
test/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM alpine AS build
|
||||
RUN echo "hello world" > /tmp/hello.txt
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /tmp/hello.txt /
|
||||
@@ -0,0 +1 @@
|
||||
{"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"]}}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.index.v1+json",
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "arm64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:2aaebbb079957470e7c0adddbb054b2b4c01f717d408efba753da2bf6e8905da",
|
||||
"size": 558,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:9b009d6b84b1ed941070b3f919823446286a674ad669d0baa8ab2c358aeb3a82",
|
||||
"size": 558,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:b6ef78de3633e45d1c08019fbabb4464fabd6dd32e82c67ea2b2a3c4e8bacdf5",
|
||||
"size": 167
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e"]}}
|
||||
@@ -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:d85d624a324422194b43cccd975b5752cf0acaedd668bb525fcd40c3587cc460",
|
||||
"size": 453
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad",
|
||||
"size": 116
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:2e82727457f04f320b643cb6e13bcbafb8e0dc0adc0443f1a25666f9518c5071",
|
||||
"size": 167
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +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"}]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"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"]}}
|
||||
@@ -0,0 +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"}]}}
|
||||
@@ -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:1c70b3e7c3a57801501ec127aa6c918c390c373294ec4fc48f2c6fe703fcc6fe",
|
||||
"size": 453
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad",
|
||||
"size": 116
|
||||
}
|
||||
]
|
||||
}
|
||||
1
test/testdata/no-provenance-image/index.json
vendored
Normal file
1
test/testdata/no-provenance-image/index.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"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"}}]}
|
||||
1
test/testdata/no-provenance-image/oci-layout
vendored
Normal file
1
test/testdata/no-provenance-image/oci-layout
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"imageLayoutVersion":"1.0.0"}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.index.v1+json",
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "arm64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:aeca14119e3242c51633a899438518217417e01414d18189a3cf71c07f2a02c3",
|
||||
"size": 836,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:6658b8ba1e1221a6288bf50cd7813f814e2baad70141a3e315b7c3476b0f476f",
|
||||
"size": 836,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"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"]}}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:2953164d6cc6c8bb8271f78f9fb2003318350a8026ea082b63a249cfa60918a3",
|
||||
"size": 241
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:c6dd08ccc92ab60a87648a6b61fbf88d9287a936b285a8b4dde8893a1f4ffedf",
|
||||
"size": 3944,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850","sha256:c6dd08ccc92ab60a87648a6b61fbf88d9287a936b285a8b4dde8893a1f4ffedf"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850","sha256:8f94b6e2a8be82e2e5b562d73212578bb3a02e8c0da7fc175c79045e73519375"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.2","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Farm64","digest":{"sha256":"7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e"}}],"predicate":{"builder":{"id":""},"buildType":"https://mobyproject.org/buildkit@v1","materials":[{"uri":"pkg:docker/docker/buildkit-syft-scanner@stable-1","digest":{"sha256":"176e0869c38aeaede37e594fcf182c91d44391a932e1d71e99ec204873445a33"}},{"uri":"pkg:docker/alpine@latest?platform=linux%2Farm64","digest":{"sha256":"c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}}],"invocation":{"configSource":{"entryPoint":"Dockerfile"},"parameters":{"frontend":"dockerfile.v0","locals":[{"name":"context"},{"name":"dockerfile"}]},"environment":{"platform":"linux/amd64"}},"buildConfig":{"llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"docker-image://docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}},"platform":{"Architecture":"arm64","OS":"linux"},"constraints":{}}},{"id":"step1","op":{"Op":{"exec":{"meta":{"args":["/bin/sh","-c","echo \"hello world\" \u003e /tmp/hello.txt"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"cwd":"/","removeMountStubsRecursive":true},"mounts":[{"input":0,"dest":"/","output":0}]}},"platform":{"Architecture":"arm64","OS":"linux"},"constraints":{}},"inputs":["step0:0"]},{"id":"step2","op":{"Op":{"file":{"actions":[{"input":-1,"secondaryInput":0,"output":0,"Action":{"copy":{"src":"/tmp/hello.txt","dest":"/","mode":-1,"followSymlink":true,"dirCopyContents":true,"createDestPath":true,"allowWildcard":true,"allowEmptyWildcard":true,"timestamp":-1}}}]}},"constraints":{}},"inputs":["step1:0"]},{"id":"step3","op":{"Op":null},"inputs":["step2:0"]}],"digestMapping":{"sha256:9ef7beaea047309185047764e3aa0d4f29979dacc88e573daa9d0f82050b4ab1":"step0","sha256:c3529d59c164c4bc6fd97482020a2147fa20b991d48916315d80504673c871bf":"step2","sha256:c99cf7ecacd148d653fcacb51c2eadf68504a4d32c870f6de26706635b08df28":"step1","sha256:f9acdec24273860ab573e08a5e75a6cb8c4baef6602571df599b3d9b745b7b0f":"step3"}},"metadata":{"buildInvocationID":"jckf4dzdrb4i99ygsshezjtsq","buildStartedOn":"2024-04-29T10:23:45.125886079Z","buildFinishedOn":"2024-04-29T10:23:46.287890106Z","completeness":{"parameters":true,"environment":true,"materials":false},"reproducible":false,"https://mobyproject.org/buildkit@v1#metadata":{"vcs":{"revision":"ba33451b352942e366f12662f2c3a0c8ac54d696","source":"git@github.com:docker/image-signer-verifier.git"},"source":{"locations":{"step0":{"locations":[{"ranges":[{"start":{"line":1},"end":{"line":1}}]}]},"step1":{"locations":[{"ranges":[{"start":{"line":2},"end":{"line":2}}]}]},"step2":{"locations":[{"ranges":[{"start":{"line":5},"end":{"line":5}}]}]}},"infos":[{"filename":"Dockerfile","language":"Dockerfile","data":"RlJPTSBhbHBpbmUgQVMgYnVpbGQKUlVOIGVjaG8gImhlbGxvIHdvcmxkIiA+IC90bXAvaGVsbG8udHh0CgpGUk9NIHNjcmF0Y2gKQ09QWSAtLWZyb209YnVpbGQgL3RtcC9oZWxsby50eHQgLwo=","llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"local://dockerfile","attrs":{"local.differ":"none","local.followpaths":"[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]","local.sharedkeyhint":"dockerfile"}}},"constraints":{}}},{"id":"step1","op":{"Op":null},"inputs":["step0:0"]}],"digestMapping":{"sha256:4efcb3fab1b18d9764b436e8f9430f56dd8fee0f811ae064f8ec051e7ac5dec4":"step0","sha256:5c08b537da7192a6909348620c571138948f7874016718afc6d6d4e1569453d3":"step1"}}]},"layers":{"step0:0":[[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:bca4290a96390d7a6fc6f2f9929370d06f8dfcacba591c76e3d5c5044e7f420c","size":3347715}]],"step2:0":[[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad","size":116}]]}}}}}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:2a9b671f3fc9bc5ca967b616d96cbbdb6493e32d4f6abd8f7a191990e8efb289",
|
||||
"size": 241
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:8f94b6e2a8be82e2e5b562d73212578bb3a02e8c0da7fc175c79045e73519375",
|
||||
"size": 3944,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850","sha256:8f2f55fc493890c2482a1220844157f4b0c8a6445d220af741e9fee8099bf532"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850","sha256:c01e5307ec84299048d76f162abec6f8bee4c463103161ab772c774e7ae9dd6d"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.2","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Farm64","digest":{"sha256":"7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e"}}],"predicate":{"builder":{"id":""},"buildType":"https://mobyproject.org/buildkit@v1","materials":[{"uri":"pkg:docker/docker/buildkit-syft-scanner@stable-1","digest":{"sha256":"176e0869c38aeaede37e594fcf182c91d44391a932e1d71e99ec204873445a33"}},{"uri":"pkg:docker/alpine@latest?platform=linux%2Farm64","digest":{"sha256":"c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}}],"invocation":{"configSource":{"entryPoint":"Dockerfile"},"parameters":{"frontend":"dockerfile.v0","locals":[{"name":"context"},{"name":"dockerfile"}]},"environment":{"platform":"linux/amd64"}},"buildConfig":{"llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"docker-image://docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}},"platform":{"Architecture":"arm64","OS":"linux"},"constraints":{}}},{"id":"step1","op":{"Op":{"exec":{"meta":{"args":["/bin/sh","-c","echo \"hello world\" \u003e /tmp/hello.txt"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"cwd":"/","removeMountStubsRecursive":true},"mounts":[{"input":0,"dest":"/","output":0}]}},"platform":{"Architecture":"arm64","OS":"linux"},"constraints":{}},"inputs":["step0:0"]},{"id":"step2","op":{"Op":{"file":{"actions":[{"input":-1,"secondaryInput":0,"output":0,"Action":{"copy":{"src":"/tmp/hello.txt","dest":"/","mode":-1,"followSymlink":true,"dirCopyContents":true,"createDestPath":true,"allowWildcard":true,"allowEmptyWildcard":true,"timestamp":-1}}}]}},"constraints":{}},"inputs":["step1:0"]},{"id":"step3","op":{"Op":null},"inputs":["step2:0"]}],"digestMapping":{"sha256:9ef7beaea047309185047764e3aa0d4f29979dacc88e573daa9d0f82050b4ab1":"step0","sha256:c3529d59c164c4bc6fd97482020a2147fa20b991d48916315d80504673c871bf":"step2","sha256:c99cf7ecacd148d653fcacb51c2eadf68504a4d32c870f6de26706635b08df28":"step1","sha256:f9acdec24273860ab573e08a5e75a6cb8c4baef6602571df599b3d9b745b7b0f":"step3"}},"metadata":{"buildInvocationID":"nkbtpuurp2ogre7j5hlfdrvma","buildStartedOn":"2024-04-26T14:24:49.386895737Z","buildFinishedOn":"2024-04-26T14:24:50.962885799Z","completeness":{"parameters":true,"environment":true,"materials":false},"reproducible":false,"https://mobyproject.org/buildkit@v1#metadata":{"vcs":{"revision":"77b839051c1796c77a5383f1570585c7f3ff70ea","source":"git@github.com:docker/image-signer-verifier.git"},"source":{"locations":{"step0":{"locations":[{"ranges":[{"start":{"line":1},"end":{"line":1}}]}]},"step1":{"locations":[{"ranges":[{"start":{"line":2},"end":{"line":2}}]}]},"step2":{"locations":[{"ranges":[{"start":{"line":5},"end":{"line":5}}]}]}},"infos":[{"filename":"Dockerfile","language":"Dockerfile","data":"RlJPTSBhbHBpbmUgQVMgYnVpbGQKUlVOIGVjaG8gImhlbGxvIHdvcmxkIiA+IC90bXAvaGVsbG8udHh0CgpGUk9NIHNjcmF0Y2gKQ09QWSAtLWZyb209YnVpbGQgL3RtcC9oZWxsby50eHQgLwo=","llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"local://dockerfile","attrs":{"local.differ":"none","local.followpaths":"[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]","local.sharedkeyhint":"dockerfile"}}},"constraints":{}}},{"id":"step1","op":{"Op":null},"inputs":["step0:0"]}],"digestMapping":{"sha256:41e476230918469ca0be3275c43594cc0a909d01fdf0fe8b73f59c50d2c2d1b9":"step0","sha256:f1bd0b5d897e59fc7a5775b7082658676e06cc12674666e1713b56c38f7d1c0a":"step1"}}]},"layers":{"step0:0":[[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:bca4290a96390d7a6fc6f2f9929370d06f8dfcacba591c76e3d5c5044e7f420c","size":3347715}]],"step2:0":[[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad","size":116}]]}}}}}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:fed2c8841731e2cf1ceb53c49c6440fcd6d565a8658141914a8a07c127e00d7e",
|
||||
"size": 241
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:92d3311aa91737ff81e2a4c8e269e78c3c95df611b44580426c384d3f5057776",
|
||||
"size": 3944,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.index.v1+json",
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "arm64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:9638ca53d2795806cf51b7461575c51e4a626a091dc2842b35cac18c787ff80f",
|
||||
"size": 836,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:8049aa9ad3479085066b31d02b74310803129c3eb1e22d2e62279f8c72340b55",
|
||||
"size": 836,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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:d85d624a324422194b43cccd975b5752cf0acaedd668bb525fcd40c3587cc460",
|
||||
"size": 453
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad",
|
||||
"size": 116
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:f634c4c53b03bf8ff917b61165631fda0cfe691a383e7b333269a53bf9a79c34",
|
||||
"size": 241
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:9fe102c03d71d47a24cd7fc7db8e7affc05fd9bf98eb027038b7daf176861e85",
|
||||
"size": 3943,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.2","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"}}],"predicate":{"builder":{"id":""},"buildType":"https://mobyproject.org/buildkit@v1","materials":[{"uri":"pkg:docker/docker/buildkit-syft-scanner@stable-1","digest":{"sha256":"176e0869c38aeaede37e594fcf182c91d44391a932e1d71e99ec204873445a33"}},{"uri":"pkg:docker/alpine@latest?platform=linux%2Famd64","digest":{"sha256":"c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}}],"invocation":{"configSource":{"entryPoint":"Dockerfile"},"parameters":{"frontend":"dockerfile.v0","locals":[{"name":"context"},{"name":"dockerfile"}]},"environment":{"platform":"linux/amd64"}},"buildConfig":{"llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"docker-image://docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}},"platform":{"Architecture":"amd64","OS":"linux"},"constraints":{}}},{"id":"step1","op":{"Op":{"exec":{"meta":{"args":["/bin/sh","-c","echo \"hello world\" \u003e /tmp/hello.txt"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"cwd":"/","removeMountStubsRecursive":true},"mounts":[{"input":0,"dest":"/","output":0}]}},"platform":{"Architecture":"amd64","OS":"linux"},"constraints":{}},"inputs":["step0:0"]},{"id":"step2","op":{"Op":{"file":{"actions":[{"input":-1,"secondaryInput":0,"output":0,"Action":{"copy":{"src":"/tmp/hello.txt","dest":"/","mode":-1,"followSymlink":true,"dirCopyContents":true,"createDestPath":true,"allowWildcard":true,"allowEmptyWildcard":true,"timestamp":-1}}}]}},"constraints":{}},"inputs":["step1:0"]},{"id":"step3","op":{"Op":null},"inputs":["step2:0"]}],"digestMapping":{"sha256:46a51bd96c5f07c63901007f7fc78b5cbc14d05c2f2b1ec8e206d0f72a8d077a":"step2","sha256:a99a5b91a7644662f368e8bc1c6ca7fad2ea98ce130fd2d2ca648a42e8f9cca9":"step1","sha256:db2976cd1ada912761fd8495e52cdb659606c87e1b2d529926b859b4b7df140c":"step3","sha256:faa2314417184f0f81e21fe46ef9c04da17b8fc0d629ac5f017dd02a596697bb":"step0"}},"metadata":{"buildInvocationID":"4sdluz1285kjezq148xqo3fcr","buildStartedOn":"2024-04-29T10:20:54.451240475Z","buildFinishedOn":"2024-04-29T10:20:56.48203241Z","completeness":{"parameters":true,"environment":true,"materials":false},"reproducible":false,"https://mobyproject.org/buildkit@v1#metadata":{"vcs":{"revision":"ba33451b352942e366f12662f2c3a0c8ac54d696","source":"git@github.com:docker/image-signer-verifier.git"},"source":{"locations":{"step0":{"locations":[{"ranges":[{"start":{"line":1},"end":{"line":1}}]}]},"step1":{"locations":[{"ranges":[{"start":{"line":2},"end":{"line":2}}]}]},"step2":{"locations":[{"ranges":[{"start":{"line":5},"end":{"line":5}}]}]}},"infos":[{"filename":"Dockerfile","language":"Dockerfile","data":"RlJPTSBhbHBpbmUgQVMgYnVpbGQKUlVOIGVjaG8gImhlbGxvIHdvcmxkIiA+IC90bXAvaGVsbG8udHh0CgpGUk9NIHNjcmF0Y2gKQ09QWSAtLWZyb209YnVpbGQgL3RtcC9oZWxsby50eHQgLwo=","llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"local://dockerfile","attrs":{"local.differ":"none","local.followpaths":"[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]","local.sharedkeyhint":"dockerfile"}}},"constraints":{}}},{"id":"step1","op":{"Op":null},"inputs":["step0:0"]}],"digestMapping":{"sha256:8c30f94c18660dd82452640583d991e007708a353546172a422418107a360f48":"step1","sha256:e1d9e97f2a2942f94811bc5453568509de77e86328036ef73562ba0b56137db6":"step0"}}]},"layers":{"step0:0":[[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:4abcf20661432fb2d719aaf90656f55c287f8ca915dc1c92ec14ff61e67fbaf8","size":3408729}]],"step2:0":[[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad","size":116}]]}}}}}
|
||||
@@ -0,0 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.2","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"}}],"predicate":{"builder":{"id":""},"buildType":"https://mobyproject.org/buildkit@v1","materials":[{"uri":"pkg:docker/docker/buildkit-syft-scanner@stable-1","digest":{"sha256":"176e0869c38aeaede37e594fcf182c91d44391a932e1d71e99ec204873445a33"}},{"uri":"pkg:docker/alpine@latest?platform=linux%2Famd64","digest":{"sha256":"c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}}],"invocation":{"configSource":{"entryPoint":"Dockerfile"},"parameters":{"frontend":"dockerfile.v0","locals":[{"name":"context"},{"name":"dockerfile"}]},"environment":{"platform":"linux/amd64"}},"buildConfig":{"llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"docker-image://docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}},"platform":{"Architecture":"amd64","OS":"linux"},"constraints":{}}},{"id":"step1","op":{"Op":{"exec":{"meta":{"args":["/bin/sh","-c","echo \"hello world\" \u003e /tmp/hello.txt"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"cwd":"/","removeMountStubsRecursive":true},"mounts":[{"input":0,"dest":"/","output":0}]}},"platform":{"Architecture":"amd64","OS":"linux"},"constraints":{}},"inputs":["step0:0"]},{"id":"step2","op":{"Op":{"file":{"actions":[{"input":-1,"secondaryInput":0,"output":0,"Action":{"copy":{"src":"/tmp/hello.txt","dest":"/","mode":-1,"followSymlink":true,"dirCopyContents":true,"createDestPath":true,"allowWildcard":true,"allowEmptyWildcard":true,"timestamp":-1}}}]}},"constraints":{}},"inputs":["step1:0"]},{"id":"step3","op":{"Op":null},"inputs":["step2:0"]}],"digestMapping":{"sha256:46a51bd96c5f07c63901007f7fc78b5cbc14d05c2f2b1ec8e206d0f72a8d077a":"step2","sha256:a99a5b91a7644662f368e8bc1c6ca7fad2ea98ce130fd2d2ca648a42e8f9cca9":"step1","sha256:db2976cd1ada912761fd8495e52cdb659606c87e1b2d529926b859b4b7df140c":"step3","sha256:faa2314417184f0f81e21fe46ef9c04da17b8fc0d629ac5f017dd02a596697bb":"step0"}},"metadata":{"buildInvocationID":"nkbtpuurp2ogre7j5hlfdrvma","buildStartedOn":"2024-04-26T14:24:49.386895737Z","buildFinishedOn":"2024-04-26T14:24:50.962885799Z","completeness":{"parameters":true,"environment":true,"materials":false},"reproducible":false,"https://mobyproject.org/buildkit@v1#metadata":{"vcs":{"revision":"77b839051c1796c77a5383f1570585c7f3ff70ea","source":"git@github.com:docker/image-signer-verifier.git"},"source":{"locations":{"step0":{"locations":[{"ranges":[{"start":{"line":1},"end":{"line":1}}]}]},"step1":{"locations":[{"ranges":[{"start":{"line":2},"end":{"line":2}}]}]},"step2":{"locations":[{"ranges":[{"start":{"line":5},"end":{"line":5}}]}]}},"infos":[{"filename":"Dockerfile","language":"Dockerfile","data":"RlJPTSBhbHBpbmUgQVMgYnVpbGQKUlVOIGVjaG8gImhlbGxvIHdvcmxkIiA+IC90bXAvaGVsbG8udHh0CgpGUk9NIHNjcmF0Y2gKQ09QWSAtLWZyb209YnVpbGQgL3RtcC9oZWxsby50eHQgLwo=","llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"local://dockerfile","attrs":{"local.differ":"none","local.followpaths":"[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]","local.sharedkeyhint":"dockerfile"}}},"constraints":{}}},{"id":"step1","op":{"Op":null},"inputs":["step0:0"]}],"digestMapping":{"sha256:41e476230918469ca0be3275c43594cc0a909d01fdf0fe8b73f59c50d2c2d1b9":"step0","sha256:f1bd0b5d897e59fc7a5775b7082658676e06cc12674666e1713b56c38f7d1c0a":"step1"}}]},"layers":{"step0:0":[[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:4abcf20661432fb2d719aaf90656f55c287f8ca915dc1c92ec14ff61e67fbaf8","size":3408729}]],"step2:0":[[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad","size":116}]]}}}}}
|
||||
@@ -0,0 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.2","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Farm64","digest":{"sha256":"7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e"}}],"predicate":{"builder":{"id":""},"buildType":"https://mobyproject.org/buildkit@v1","materials":[{"uri":"pkg:docker/docker/buildkit-syft-scanner@stable-1","digest":{"sha256":"176e0869c38aeaede37e594fcf182c91d44391a932e1d71e99ec204873445a33"}},{"uri":"pkg:docker/alpine@latest?platform=linux%2Farm64","digest":{"sha256":"c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}}],"invocation":{"configSource":{"entryPoint":"Dockerfile"},"parameters":{"frontend":"dockerfile.v0","locals":[{"name":"context"},{"name":"dockerfile"}]},"environment":{"platform":"linux/amd64"}},"buildConfig":{"llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"docker-image://docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}},"platform":{"Architecture":"arm64","OS":"linux"},"constraints":{}}},{"id":"step1","op":{"Op":{"exec":{"meta":{"args":["/bin/sh","-c","echo \"hello world\" \u003e /tmp/hello.txt"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"cwd":"/","removeMountStubsRecursive":true},"mounts":[{"input":0,"dest":"/","output":0}]}},"platform":{"Architecture":"arm64","OS":"linux"},"constraints":{}},"inputs":["step0:0"]},{"id":"step2","op":{"Op":{"file":{"actions":[{"input":-1,"secondaryInput":0,"output":0,"Action":{"copy":{"src":"/tmp/hello.txt","dest":"/","mode":-1,"followSymlink":true,"dirCopyContents":true,"createDestPath":true,"allowWildcard":true,"allowEmptyWildcard":true,"timestamp":-1}}}]}},"constraints":{}},"inputs":["step1:0"]},{"id":"step3","op":{"Op":null},"inputs":["step2:0"]}],"digestMapping":{"sha256:9ef7beaea047309185047764e3aa0d4f29979dacc88e573daa9d0f82050b4ab1":"step0","sha256:c3529d59c164c4bc6fd97482020a2147fa20b991d48916315d80504673c871bf":"step2","sha256:c99cf7ecacd148d653fcacb51c2eadf68504a4d32c870f6de26706635b08df28":"step1","sha256:f9acdec24273860ab573e08a5e75a6cb8c4baef6602571df599b3d9b745b7b0f":"step3"}},"metadata":{"buildInvocationID":"oufyhd0aciw714m1i5y6l09xz","buildStartedOn":"2024-04-19T16:25:04.209111656Z","buildFinishedOn":"2024-04-19T16:25:05.831440763Z","completeness":{"parameters":true,"environment":true,"materials":false},"reproducible":false,"https://mobyproject.org/buildkit@v1#metadata":{"vcs":{"revision":"5914df2930aba701d6ddc3ba9272a5732dfe8ab4","source":"git@github.com:docker/image-signer-verifier.git"},"source":{"locations":{"step0":{"locations":[{"ranges":[{"start":{"line":1},"end":{"line":1}}]}]},"step1":{"locations":[{"ranges":[{"start":{"line":2},"end":{"line":2}}]}]},"step2":{"locations":[{"ranges":[{"start":{"line":5},"end":{"line":5}}]}]}},"infos":[{"filename":"Dockerfile","language":"Dockerfile","data":"RlJPTSBhbHBpbmUgQVMgYnVpbGQKUlVOIGVjaG8gImhlbGxvIHdvcmxkIiA+IC90bXAvaGVsbG8udHh0CgpGUk9NIHNjcmF0Y2gKQ09QWSAtLWZyb209YnVpbGQgL3RtcC9oZWxsby50eHQgLwo=","llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"local://dockerfile","attrs":{"local.differ":"none","local.followpaths":"[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]","local.sharedkeyhint":"dockerfile"}}},"constraints":{}}},{"id":"step1","op":{"Op":null},"inputs":["step0:0"]}],"digestMapping":{"sha256:18b166b594df5270bf6a4aa8dcd0a96b3635d0a23106d819620cbab2f3f5cf22":"step0","sha256:31d6cbba3f437cfb5933e60d5f4d2be69109c2fae9ed5ffe04da16931a459cca":"step1"}}]},"layers":{"step0:0":[[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:bca4290a96390d7a6fc6f2f9929370d06f8dfcacba591c76e3d5c5044e7f420c","size":3347715}]],"step2:0":[[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad","size":116}]]}}}}}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:3e64f9d2888ed9211fbf2c6b5853ea559248fdb4ab711bcea34b65c62f0e026b",
|
||||
"size": 241
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:8f2f55fc493890c2482a1220844157f4b0c8a6445d220af741e9fee8099bf532",
|
||||
"size": 3943,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.index.v1+json",
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "arm64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:3883faf6acc3cae029364ed17ec2ce917fc9a500aab72f813d26fed8404e7162",
|
||||
"size": 836,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:f2b95cecafef9c22a5d059fac8f20e3645a45370e52abf9581dd4eedd152fce0",
|
||||
"size": 836,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.2","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Farm64","digest":{"sha256":"7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e"}}],"predicate":{"builder":{"id":""},"buildType":"https://mobyproject.org/buildkit@v1","materials":[{"uri":"pkg:docker/docker/buildkit-syft-scanner@stable-1","digest":{"sha256":"176e0869c38aeaede37e594fcf182c91d44391a932e1d71e99ec204873445a33"}},{"uri":"pkg:docker/alpine@latest?platform=linux%2Farm64","digest":{"sha256":"c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}}],"invocation":{"configSource":{"entryPoint":"Dockerfile"},"parameters":{"frontend":"dockerfile.v0","locals":[{"name":"context"},{"name":"dockerfile"}]},"environment":{"platform":"linux/amd64"}},"buildConfig":{"llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"docker-image://docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}},"platform":{"Architecture":"arm64","OS":"linux"},"constraints":{}}},{"id":"step1","op":{"Op":{"exec":{"meta":{"args":["/bin/sh","-c","echo \"hello world\" \u003e /tmp/hello.txt"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"cwd":"/","removeMountStubsRecursive":true},"mounts":[{"input":0,"dest":"/","output":0}]}},"platform":{"Architecture":"arm64","OS":"linux"},"constraints":{}},"inputs":["step0:0"]},{"id":"step2","op":{"Op":{"file":{"actions":[{"input":-1,"secondaryInput":0,"output":0,"Action":{"copy":{"src":"/tmp/hello.txt","dest":"/","mode":-1,"followSymlink":true,"dirCopyContents":true,"createDestPath":true,"allowWildcard":true,"allowEmptyWildcard":true,"timestamp":-1}}}]}},"constraints":{}},"inputs":["step1:0"]},{"id":"step3","op":{"Op":null},"inputs":["step2:0"]}],"digestMapping":{"sha256:9ef7beaea047309185047764e3aa0d4f29979dacc88e573daa9d0f82050b4ab1":"step0","sha256:c3529d59c164c4bc6fd97482020a2147fa20b991d48916315d80504673c871bf":"step2","sha256:c99cf7ecacd148d653fcacb51c2eadf68504a4d32c870f6de26706635b08df28":"step1","sha256:f9acdec24273860ab573e08a5e75a6cb8c4baef6602571df599b3d9b745b7b0f":"step3"}},"metadata":{"buildInvocationID":"4sdluz1285kjezq148xqo3fcr","buildStartedOn":"2024-04-29T10:20:54.451240475Z","buildFinishedOn":"2024-04-29T10:20:56.48203241Z","completeness":{"parameters":true,"environment":true,"materials":false},"reproducible":false,"https://mobyproject.org/buildkit@v1#metadata":{"vcs":{"revision":"ba33451b352942e366f12662f2c3a0c8ac54d696","source":"git@github.com:docker/image-signer-verifier.git"},"source":{"locations":{"step0":{"locations":[{"ranges":[{"start":{"line":1},"end":{"line":1}}]}]},"step1":{"locations":[{"ranges":[{"start":{"line":2},"end":{"line":2}}]}]},"step2":{"locations":[{"ranges":[{"start":{"line":5},"end":{"line":5}}]}]}},"infos":[{"filename":"Dockerfile","language":"Dockerfile","data":"RlJPTSBhbHBpbmUgQVMgYnVpbGQKUlVOIGVjaG8gImhlbGxvIHdvcmxkIiA+IC90bXAvaGVsbG8udHh0CgpGUk9NIHNjcmF0Y2gKQ09QWSAtLWZyb209YnVpbGQgL3RtcC9oZWxsby50eHQgLwo=","llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"local://dockerfile","attrs":{"local.differ":"none","local.followpaths":"[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]","local.sharedkeyhint":"dockerfile"}}},"constraints":{}}},{"id":"step1","op":{"Op":null},"inputs":["step0:0"]}],"digestMapping":{"sha256:8c30f94c18660dd82452640583d991e007708a353546172a422418107a360f48":"step1","sha256:e1d9e97f2a2942f94811bc5453568509de77e86328036ef73562ba0b56137db6":"step0"}}]},"layers":{"step0:0":[[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:bca4290a96390d7a6fc6f2f9929370d06f8dfcacba591c76e3d5c5044e7f420c","size":3347715}]],"step2:0":[[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad","size":116}]]}}}}}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e","sha256:371954672cfaa92735d6fbd70a787aac618a41d4c8ec8d6e12bd12d0cc601706"]}}
|
||||
@@ -0,0 +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"}]}}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:4e5988d06eee647cb901d4435830fbf13cf3ab1ae27ec91246b280514e6a7b33",
|
||||
"size": 241
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:c01e5307ec84299048d76f162abec6f8bee4c463103161ab772c774e7ae9dd6d",
|
||||
"size": 3944,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.2","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"}}],"predicate":{"builder":{"id":""},"buildType":"https://mobyproject.org/buildkit@v1","materials":[{"uri":"pkg:docker/docker/buildkit-syft-scanner@stable-1","digest":{"sha256":"176e0869c38aeaede37e594fcf182c91d44391a932e1d71e99ec204873445a33"}},{"uri":"pkg:docker/alpine@latest?platform=linux%2Famd64","digest":{"sha256":"c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}}],"invocation":{"configSource":{"entryPoint":"Dockerfile"},"parameters":{"frontend":"dockerfile.v0","locals":[{"name":"context"},{"name":"dockerfile"}]},"environment":{"platform":"linux/amd64"}},"buildConfig":{"llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"docker-image://docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}},"platform":{"Architecture":"amd64","OS":"linux"},"constraints":{}}},{"id":"step1","op":{"Op":{"exec":{"meta":{"args":["/bin/sh","-c","echo \"hello world\" \u003e /tmp/hello.txt"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"cwd":"/","removeMountStubsRecursive":true},"mounts":[{"input":0,"dest":"/","output":0}]}},"platform":{"Architecture":"amd64","OS":"linux"},"constraints":{}},"inputs":["step0:0"]},{"id":"step2","op":{"Op":{"file":{"actions":[{"input":-1,"secondaryInput":0,"output":0,"Action":{"copy":{"src":"/tmp/hello.txt","dest":"/","mode":-1,"followSymlink":true,"dirCopyContents":true,"createDestPath":true,"allowWildcard":true,"allowEmptyWildcard":true,"timestamp":-1}}}]}},"constraints":{}},"inputs":["step1:0"]},{"id":"step3","op":{"Op":null},"inputs":["step2:0"]}],"digestMapping":{"sha256:46a51bd96c5f07c63901007f7fc78b5cbc14d05c2f2b1ec8e206d0f72a8d077a":"step2","sha256:a99a5b91a7644662f368e8bc1c6ca7fad2ea98ce130fd2d2ca648a42e8f9cca9":"step1","sha256:db2976cd1ada912761fd8495e52cdb659606c87e1b2d529926b859b4b7df140c":"step3","sha256:faa2314417184f0f81e21fe46ef9c04da17b8fc0d629ac5f017dd02a596697bb":"step0"}},"metadata":{"buildInvocationID":"oufyhd0aciw714m1i5y6l09xz","buildStartedOn":"2024-04-19T16:25:04.209111656Z","buildFinishedOn":"2024-04-19T16:25:05.831440763Z","completeness":{"parameters":true,"environment":true,"materials":false},"reproducible":false,"https://mobyproject.org/buildkit@v1#metadata":{"vcs":{"revision":"5914df2930aba701d6ddc3ba9272a5732dfe8ab4","source":"git@github.com:docker/image-signer-verifier.git"},"source":{"locations":{"step0":{"locations":[{"ranges":[{"start":{"line":1},"end":{"line":1}}]}]},"step1":{"locations":[{"ranges":[{"start":{"line":2},"end":{"line":2}}]}]},"step2":{"locations":[{"ranges":[{"start":{"line":5},"end":{"line":5}}]}]}},"infos":[{"filename":"Dockerfile","language":"Dockerfile","data":"RlJPTSBhbHBpbmUgQVMgYnVpbGQKUlVOIGVjaG8gImhlbGxvIHdvcmxkIiA+IC90bXAvaGVsbG8udHh0CgpGUk9NIHNjcmF0Y2gKQ09QWSAtLWZyb209YnVpbGQgL3RtcC9oZWxsby50eHQgLwo=","llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"local://dockerfile","attrs":{"local.differ":"none","local.followpaths":"[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]","local.sharedkeyhint":"dockerfile"}}},"constraints":{}}},{"id":"step1","op":{"Op":null},"inputs":["step0:0"]}],"digestMapping":{"sha256:18b166b594df5270bf6a4aa8dcd0a96b3635d0a23106d819620cbab2f3f5cf22":"step0","sha256:31d6cbba3f437cfb5933e60d5f4d2be69109c2fae9ed5ffe04da16931a459cca":"step1"}}]},"layers":{"step0:0":[[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:4abcf20661432fb2d719aaf90656f55c287f8ca915dc1c92ec14ff61e67fbaf8","size":3408729}]],"step2:0":[[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad","size":116}]]}}}}}
|
||||
@@ -0,0 +1 @@
|
||||
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://slsa.dev/provenance/v0.2","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"}}],"predicate":{"builder":{"id":""},"buildType":"https://mobyproject.org/buildkit@v1","materials":[{"uri":"pkg:docker/docker/buildkit-syft-scanner@stable-1","digest":{"sha256":"176e0869c38aeaede37e594fcf182c91d44391a932e1d71e99ec204873445a33"}},{"uri":"pkg:docker/alpine@latest?platform=linux%2Famd64","digest":{"sha256":"c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}}],"invocation":{"configSource":{"entryPoint":"Dockerfile"},"parameters":{"frontend":"dockerfile.v0","locals":[{"name":"context"},{"name":"dockerfile"}]},"environment":{"platform":"linux/amd64"}},"buildConfig":{"llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"docker-image://docker.io/library/alpine:latest@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b"}},"platform":{"Architecture":"amd64","OS":"linux"},"constraints":{}}},{"id":"step1","op":{"Op":{"exec":{"meta":{"args":["/bin/sh","-c","echo \"hello world\" \u003e /tmp/hello.txt"],"env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"cwd":"/","removeMountStubsRecursive":true},"mounts":[{"input":0,"dest":"/","output":0}]}},"platform":{"Architecture":"amd64","OS":"linux"},"constraints":{}},"inputs":["step0:0"]},{"id":"step2","op":{"Op":{"file":{"actions":[{"input":-1,"secondaryInput":0,"output":0,"Action":{"copy":{"src":"/tmp/hello.txt","dest":"/","mode":-1,"followSymlink":true,"dirCopyContents":true,"createDestPath":true,"allowWildcard":true,"allowEmptyWildcard":true,"timestamp":-1}}}]}},"constraints":{}},"inputs":["step1:0"]},{"id":"step3","op":{"Op":null},"inputs":["step2:0"]}],"digestMapping":{"sha256:46a51bd96c5f07c63901007f7fc78b5cbc14d05c2f2b1ec8e206d0f72a8d077a":"step2","sha256:a99a5b91a7644662f368e8bc1c6ca7fad2ea98ce130fd2d2ca648a42e8f9cca9":"step1","sha256:db2976cd1ada912761fd8495e52cdb659606c87e1b2d529926b859b4b7df140c":"step3","sha256:faa2314417184f0f81e21fe46ef9c04da17b8fc0d629ac5f017dd02a596697bb":"step0"}},"metadata":{"buildInvocationID":"jckf4dzdrb4i99ygsshezjtsq","buildStartedOn":"2024-04-29T10:23:45.125886079Z","buildFinishedOn":"2024-04-29T10:23:46.287890106Z","completeness":{"parameters":true,"environment":true,"materials":false},"reproducible":false,"https://mobyproject.org/buildkit@v1#metadata":{"vcs":{"revision":"ba33451b352942e366f12662f2c3a0c8ac54d696","source":"git@github.com:docker/image-signer-verifier.git"},"source":{"locations":{"step0":{"locations":[{"ranges":[{"start":{"line":1},"end":{"line":1}}]}]},"step1":{"locations":[{"ranges":[{"start":{"line":2},"end":{"line":2}}]}]},"step2":{"locations":[{"ranges":[{"start":{"line":5},"end":{"line":5}}]}]}},"infos":[{"filename":"Dockerfile","language":"Dockerfile","data":"RlJPTSBhbHBpbmUgQVMgYnVpbGQKUlVOIGVjaG8gImhlbGxvIHdvcmxkIiA+IC90bXAvaGVsbG8udHh0CgpGUk9NIHNjcmF0Y2gKQ09QWSAtLWZyb209YnVpbGQgL3RtcC9oZWxsby50eHQgLwo=","llbDefinition":[{"id":"step0","op":{"Op":{"source":{"identifier":"local://dockerfile","attrs":{"local.differ":"none","local.followpaths":"[\"Dockerfile\",\"Dockerfile.dockerignore\",\"dockerfile\"]","local.sharedkeyhint":"dockerfile"}}},"constraints":{}}},{"id":"step1","op":{"Op":null},"inputs":["step0:0"]}],"digestMapping":{"sha256:4efcb3fab1b18d9764b436e8f9430f56dd8fee0f811ae064f8ec051e7ac5dec4":"step0","sha256:5c08b537da7192a6909348620c571138948f7874016718afc6d6d4e1569453d3":"step1"}}]},"layers":{"step0:0":[[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","digest":"sha256:4abcf20661432fb2d719aaf90656f55c287f8ca915dc1c92ec14ff61e67fbaf8","size":3408729}]],"step2:0":[[{"mediaType":"application/vnd.oci.image.layer.v1.tar+gzip","digest":"sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad","size":116}]]}}}}}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e","sha256:5171425b78a2aedb43eb4e95083e64d3764c798507596ceded776c4ab038c224"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"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"]}}
|
||||
@@ -0,0 +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"}]}}
|
||||
@@ -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:1c70b3e7c3a57801501ec127aa6c918c390c373294ec4fc48f2c6fe703fcc6fe",
|
||||
"size": 453
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
|
||||
"digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad",
|
||||
"size": 116
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.index.v1+json",
|
||||
"manifests": [
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"size": 476,
|
||||
"platform": {
|
||||
"architecture": "arm64",
|
||||
"os": "linux"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:26da286bbc886aa14d191808db8fcbbd5d8ec68cf0047f954133e76d8e73d71c",
|
||||
"size": 836,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"digest": "sha256:e0a9b9404ac2691b9b1c9ef217f22bb1e106efd5ee791640411764e1cf39ea2c",
|
||||
"size": 836,
|
||||
"annotations": {
|
||||
"vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
|
||||
"vnd.docker.reference.type": "attestation-manifest"
|
||||
},
|
||||
"platform": {
|
||||
"architecture": "unknown",
|
||||
"os": "unknown"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:a4cf4b24f3fa8cd49a59e8fd4ef5ce285f0aa928d2651f7ec3d5a78276249dec",
|
||||
"size": 241
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:371954672cfaa92735d6fbd70a787aac618a41d4c8ec8d6e12bd12d0cc601706",
|
||||
"size": 3944,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"schemaVersion": 2,
|
||||
"mediaType": "application/vnd.oci.image.manifest.v1+json",
|
||||
"config": {
|
||||
"mediaType": "application/vnd.oci.image.config.v1+json",
|
||||
"digest": "sha256:c9f436179969b60ec0bbd406b1340c501e59376a658b14b53c1828924c0ac668",
|
||||
"size": 241
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e",
|
||||
"size": 946,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://spdx.dev/Document"
|
||||
}
|
||||
},
|
||||
{
|
||||
"mediaType": "application/vnd.in-toto+json",
|
||||
"digest": "sha256:5171425b78a2aedb43eb4e95083e64d3764c798507596ceded776c4ab038c224",
|
||||
"size": 3944,
|
||||
"annotations": {
|
||||
"in-toto.io/predicate-type": "https://slsa.dev/provenance/v0.2"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e","sha256:9fe102c03d71d47a24cd7fc7db8e7affc05fd9bf98eb027038b7daf176861e85"]}}
|
||||
@@ -0,0 +1 @@
|
||||
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e","sha256:92d3311aa91737ff81e2a4c8e269e78c3c95df611b44580426c384d3f5057776"]}}
|
||||
1
test/testdata/unsigned-test-image/index.json
vendored
Normal file
1
test/testdata/unsigned-test-image/index.json
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:db8f2a6e112ea6396f57d073269ecfac61e8dcdad3a4a643dcb577522492f898","size":1607,"annotations":{"org.opencontainers.image.created":"2024-04-29T10:23:46Z","org.opencontainers.image.ref.name":"docker.io/library/test-image:test"}}]}
|
||||
1
test/testdata/unsigned-test-image/oci-layout
vendored
Normal file
1
test/testdata/unsigned-test-image/oci-layout
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"imageLayoutVersion":"1.0.0"}
|
||||
Reference in New Issue
Block a user