mirror of
https://github.com/moby/buildkit.git
synced 2026-06-24 08:47:57 +00:00
attestation: emit in-toto v1 statements directly
Use the existing in-toto-golang JSON statement types for attestation export and set the statement type URI to in-toto v1. Remove the local protojson compatibility wrapper so this patch only changes the emitted statement type. Update in-toto-golang to v0.11.0 and refresh vendored module metadata. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
@@ -1,147 +0,0 @@
|
||||
package intoto
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"maps"
|
||||
|
||||
attestationv1 "github.com/in-toto/attestation/go/v1"
|
||||
legacyintoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/pkg/errors"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
)
|
||||
|
||||
var (
|
||||
marshalOptions = protojson.MarshalOptions{}
|
||||
unmarshalOptions = protojson.UnmarshalOptions{}
|
||||
)
|
||||
|
||||
// Subject is the subset of the in-toto v1 resource descriptor.
|
||||
type Subject struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Digest map[string]string `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
// Statement is a local compatibility wrapper around the in-toto v1 protobuf
|
||||
// statement. It keeps protojson isolated at the boundary while still accepting
|
||||
// legacy v0.1 JSON on decode.
|
||||
type Statement struct {
|
||||
Type string
|
||||
Subject []Subject
|
||||
PredicateType string
|
||||
Predicate json.RawMessage
|
||||
}
|
||||
|
||||
func ToResourceDescriptors(subjects []Subject) []*attestationv1.ResourceDescriptor {
|
||||
out := make([]*attestationv1.ResourceDescriptor, 0, len(subjects))
|
||||
for _, subject := range subjects {
|
||||
out = append(out, &attestationv1.ResourceDescriptor{
|
||||
Name: subject.Name,
|
||||
Digest: maps.Clone(subject.Digest),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func FromResourceDescriptors(subjects []*attestationv1.ResourceDescriptor) []Subject {
|
||||
out := make([]Subject, 0, len(subjects))
|
||||
for _, subject := range subjects {
|
||||
if subject == nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, Subject{
|
||||
Name: subject.GetName(),
|
||||
Digest: maps.Clone(subject.GetDigest()),
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (s Statement) MarshalJSON() ([]byte, error) {
|
||||
stmt, err := s.toProtobuf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return marshalOptions.Marshal(stmt)
|
||||
}
|
||||
|
||||
func (s *Statement) UnmarshalJSON(data []byte) error {
|
||||
var stmt attestationv1.Statement
|
||||
if err := unmarshalOptions.Unmarshal(data, &stmt); err != nil {
|
||||
return errors.Wrap(err, "cannot decode in-toto statement")
|
||||
}
|
||||
if !validStatementType(stmt.GetType()) {
|
||||
return errors.Errorf("unsupported in-toto statement type %q", stmt.GetType())
|
||||
}
|
||||
return s.fromProtobuf(&stmt)
|
||||
}
|
||||
|
||||
func (s Statement) toProtobuf() (*attestationv1.Statement, error) {
|
||||
predicate, err := toPredicateStruct(s.Predicate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &attestationv1.Statement{
|
||||
Type: attestationv1.StatementTypeUri,
|
||||
Subject: ToResourceDescriptors(s.Subject),
|
||||
PredicateType: s.PredicateType,
|
||||
Predicate: predicate,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Statement) fromProtobuf(stmt *attestationv1.Statement) error {
|
||||
predicate, err := fromPredicateStruct(stmt.GetPredicate())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.Type = stmt.GetType()
|
||||
s.Subject = FromResourceDescriptors(stmt.GetSubject())
|
||||
s.PredicateType = stmt.GetPredicateType()
|
||||
s.Predicate = predicate
|
||||
return nil
|
||||
}
|
||||
|
||||
func toPredicateStruct(raw json.RawMessage) (*structpb.Struct, error) {
|
||||
raw = normalizePredicate(raw)
|
||||
if len(raw) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var predicate map[string]any
|
||||
if err := json.Unmarshal(raw, &predicate); err != nil {
|
||||
return nil, errors.Wrap(err, "cannot decode in-toto predicate as object")
|
||||
}
|
||||
st, err := structpb.NewStruct(predicate)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot convert in-toto predicate to protobuf")
|
||||
}
|
||||
return st, nil
|
||||
}
|
||||
|
||||
func fromPredicateStruct(predicate *structpb.Struct) (json.RawMessage, error) {
|
||||
if predicate == nil {
|
||||
return nil, nil
|
||||
}
|
||||
dt, err := marshalOptions.Marshal(predicate)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "cannot encode in-toto predicate")
|
||||
}
|
||||
return normalizePredicate(dt), nil
|
||||
}
|
||||
|
||||
func normalizePredicate(raw json.RawMessage) json.RawMessage {
|
||||
raw = bytes.TrimSpace(raw)
|
||||
if len(raw) == 0 || bytes.Equal(raw, []byte("null")) {
|
||||
return nil
|
||||
}
|
||||
return bytes.Clone(raw)
|
||||
}
|
||||
|
||||
func validStatementType(t string) bool {
|
||||
switch t {
|
||||
case legacyintoto.StatementInTotoV01, attestationv1.StatementTypeUri:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package intoto
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
attestationv1 "github.com/in-toto/attestation/go/v1"
|
||||
legacyintoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestStatementMarshalJSON(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
stmt := Statement{
|
||||
Type: legacyintoto.StatementInTotoV01,
|
||||
Subject: []Subject{{
|
||||
Name: "pkg:docker/example@sha256:abc",
|
||||
Digest: map[string]string{
|
||||
"sha256": strings.Repeat("a", 64),
|
||||
},
|
||||
}},
|
||||
PredicateType: "https://example.com/attestations/v1.0",
|
||||
Predicate: json.RawMessage(`{"success":true}`),
|
||||
}
|
||||
|
||||
expected := `{
|
||||
"_type":"https://in-toto.io/Statement/v1",
|
||||
"subject":[{"name":"pkg:docker/example@sha256:abc","digest":{"sha256":"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}}],
|
||||
"predicateType":"https://example.com/attestations/v1.0",
|
||||
"predicate":{"success":true}
|
||||
}`
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
v any
|
||||
}{
|
||||
{"value", stmt},
|
||||
{"pointer", &stmt},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
dt, err := json.Marshal(tc.v)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, expected, string(dt))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatementMarshalJSONOmitsEmptyPredicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
stmt := Statement{
|
||||
Subject: []Subject{{
|
||||
Name: "artifact",
|
||||
Digest: map[string]string{
|
||||
"sha256": strings.Repeat("b", 64),
|
||||
},
|
||||
}},
|
||||
PredicateType: "https://example.com/attestations/v1.0",
|
||||
}
|
||||
|
||||
dt, err := json.Marshal(&stmt)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, `{
|
||||
"_type":"https://in-toto.io/Statement/v1",
|
||||
"subject":[{"name":"artifact","digest":{"sha256":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"}}],
|
||||
"predicateType":"https://example.com/attestations/v1.0"
|
||||
}`, string(dt))
|
||||
}
|
||||
|
||||
func TestStatementUnmarshalJSONLegacyV01(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var stmt Statement
|
||||
err := json.Unmarshal([]byte(`{
|
||||
"_type":"https://in-toto.io/Statement/v0.1",
|
||||
"subject":[{"name":"artifact","digest":{"sha256":"cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"}}],
|
||||
"predicateType":"https://example.com/attestations/v1.0",
|
||||
"predicate":{"success":true}
|
||||
}`), &stmt)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, legacyintoto.StatementInTotoV01, stmt.Type)
|
||||
require.Equal(t, []Subject{{
|
||||
Name: "artifact",
|
||||
Digest: map[string]string{
|
||||
"sha256": strings.Repeat("c", 64),
|
||||
},
|
||||
}}, stmt.Subject)
|
||||
require.Equal(t, "https://example.com/attestations/v1.0", stmt.PredicateType)
|
||||
require.JSONEq(t, `{"success":true}`, string(stmt.Predicate))
|
||||
}
|
||||
|
||||
func TestStatementUnmarshalJSONV1(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var stmt Statement
|
||||
err := json.Unmarshal([]byte(`{
|
||||
"_type":"https://in-toto.io/Statement/v1",
|
||||
"subject":[{"name":"artifact","digest":{"sha256":"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"}}],
|
||||
"predicateType":"https://example.com/attestations/v1.0",
|
||||
"predicate":{"success":true}
|
||||
}`), &stmt)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Equal(t, attestationv1.StatementTypeUri, stmt.Type)
|
||||
require.Equal(t, []Subject{{
|
||||
Name: "artifact",
|
||||
Digest: map[string]string{
|
||||
"sha256": strings.Repeat("d", 64),
|
||||
},
|
||||
}}, stmt.Subject)
|
||||
require.Equal(t, "https://example.com/attestations/v1.0", stmt.PredicateType)
|
||||
require.JSONEq(t, `{"success":true}`, string(stmt.Predicate))
|
||||
}
|
||||
|
||||
func TestStatementUnmarshalJSONNullPredicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var stmt Statement
|
||||
err := json.Unmarshal([]byte(`{
|
||||
"_type":"https://in-toto.io/Statement/v0.1",
|
||||
"subject":[{"name":"artifact","digest":{"sha256":"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"}}],
|
||||
"predicateType":"https://example.com/attestations/v1.0",
|
||||
"predicate":null
|
||||
}`), &stmt)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, stmt.Predicate)
|
||||
}
|
||||
|
||||
func TestStatementUnmarshalJSONOmittedPredicate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var stmt Statement
|
||||
err := json.Unmarshal([]byte(`{
|
||||
"_type":"https://in-toto.io/Statement/v1",
|
||||
"subject":[{"name":"artifact","digest":{"sha256":"abababababababababababababababababababababababababababababababab"}}],
|
||||
"predicateType":"https://example.com/attestations/v1.0"
|
||||
}`), &stmt)
|
||||
require.NoError(t, err)
|
||||
require.Nil(t, stmt.Predicate)
|
||||
}
|
||||
|
||||
func TestStatementUnmarshalJSONRejectsUnknownType(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var stmt Statement
|
||||
err := json.Unmarshal([]byte(`{
|
||||
"_type":"https://in-toto.io/Statement/v9",
|
||||
"subject":[{"name":"artifact","digest":{"sha256":"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"}}],
|
||||
"predicateType":"https://example.com/attestations/v1.0",
|
||||
"predicate":{"success":true}
|
||||
}`), &stmt)
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -6,8 +6,8 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/containerd/continuity/fs"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/moby/buildkit/exporter"
|
||||
intotojson "github.com/moby/buildkit/exporter/attestation/intoto"
|
||||
gatewaypb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
@@ -56,9 +56,9 @@ func ReadAll(ctx context.Context, s session.Group, att exporter.Attestation) ([]
|
||||
|
||||
// MakeInTotoStatements iterates over all provided result attestations and
|
||||
// generates intoto attestation statements.
|
||||
func MakeInTotoStatements(ctx context.Context, s session.Group, attestations []exporter.Attestation, defaultSubjects []intotojson.Subject) ([]*intotojson.Statement, error) {
|
||||
func MakeInTotoStatements(ctx context.Context, s session.Group, attestations []exporter.Attestation, defaultSubjects []intoto.Subject) ([]intoto.Statement, error) {
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
statements := make([]*intotojson.Statement, len(attestations))
|
||||
statements := make([]intoto.Statement, len(attestations))
|
||||
|
||||
for i, att := range attestations {
|
||||
eg.Go(func() error {
|
||||
@@ -73,7 +73,7 @@ func MakeInTotoStatements(ctx context.Context, s session.Group, attestations []e
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
statements[i] = stmt
|
||||
statements[i] = *stmt
|
||||
case gatewaypb.AttestationKind_Bundle:
|
||||
return errors.New("bundle attestation kind must be un-bundled first")
|
||||
}
|
||||
@@ -86,13 +86,13 @@ func MakeInTotoStatements(ctx context.Context, s session.Group, attestations []e
|
||||
return statements, nil
|
||||
}
|
||||
|
||||
func makeInTotoStatement(content []byte, attestation exporter.Attestation, defaultSubjects []intotojson.Subject) (*intotojson.Statement, error) {
|
||||
func makeInTotoStatement(content []byte, attestation exporter.Attestation, defaultSubjects []intoto.Subject) (*intoto.Statement, error) {
|
||||
if len(attestation.InToto.Subjects) == 0 {
|
||||
attestation.InToto.Subjects = []result.InTotoSubject{{
|
||||
Kind: gatewaypb.InTotoSubjectKind_Self,
|
||||
}}
|
||||
}
|
||||
var subjects []intotojson.Subject
|
||||
subjects := []intoto.Subject{}
|
||||
for _, subject := range attestation.InToto.Subjects {
|
||||
subjectName := "_"
|
||||
if subject.Name != "" {
|
||||
@@ -109,14 +109,14 @@ func makeInTotoStatement(content []byte, attestation exporter.Attestation, defau
|
||||
}
|
||||
|
||||
for _, name := range subjectNames {
|
||||
subjects = append(subjects, intotojson.Subject{
|
||||
subjects = append(subjects, intoto.Subject{
|
||||
Name: name,
|
||||
Digest: defaultSubject.Digest,
|
||||
})
|
||||
}
|
||||
}
|
||||
case gatewaypb.InTotoSubjectKind_Raw:
|
||||
subjects = append(subjects, intotojson.Subject{
|
||||
subjects = append(subjects, intoto.Subject{
|
||||
Name: subjectName,
|
||||
Digest: result.ToDigestMap(subject.Digest...),
|
||||
})
|
||||
@@ -124,10 +124,13 @@ func makeInTotoStatement(content []byte, attestation exporter.Attestation, defau
|
||||
return nil, errors.Errorf("unknown attestation subject type %T", subject)
|
||||
}
|
||||
}
|
||||
stmt := intotojson.Statement{
|
||||
Subject: subjects,
|
||||
PredicateType: attestation.InToto.PredicateType,
|
||||
Predicate: json.RawMessage(content),
|
||||
stmt := intoto.Statement{
|
||||
StatementHeader: intoto.StatementHeader{
|
||||
Type: intoto.StatementInTotoV1,
|
||||
PredicateType: attestation.InToto.PredicateType,
|
||||
Subject: subjects,
|
||||
},
|
||||
Predicate: json.RawMessage(content),
|
||||
}
|
||||
return &stmt, nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package attestation
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
@@ -10,8 +9,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/containerd/continuity/fs"
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/moby/buildkit/exporter"
|
||||
intotojson "github.com/moby/buildkit/exporter/attestation/intoto"
|
||||
gatewaypb "github.com/moby/buildkit/frontend/gateway/pb"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
@@ -138,7 +137,7 @@ func unbundle(root string, bundle exporter.Attestation) ([]exporter.Attestation,
|
||||
return nil, err
|
||||
}
|
||||
dec := json.NewDecoder(f)
|
||||
var stmt intotojson.Statement
|
||||
var stmt intoto.Statement
|
||||
if err := dec.Decode(&stmt); err != nil {
|
||||
return nil, errors.Wrap(err, "cannot decode in-toto statement")
|
||||
}
|
||||
@@ -149,6 +148,11 @@ func unbundle(root string, bundle exporter.Attestation) ([]exporter.Attestation,
|
||||
return nil, errors.Errorf("bundle entry %s does not match required predicate type %s", stmt.PredicateType, bundle.InToto.PredicateType)
|
||||
}
|
||||
|
||||
predicate, err := json.Marshal(stmt.Predicate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
subjects := make([]result.InTotoSubject, len(stmt.Subject))
|
||||
for i, subject := range stmt.Subject {
|
||||
subjects[i] = result.InTotoSubject{
|
||||
@@ -161,7 +165,7 @@ func unbundle(root string, bundle exporter.Attestation) ([]exporter.Attestation,
|
||||
Kind: gatewaypb.AttestationKind_InToto,
|
||||
Metadata: bundle.Metadata,
|
||||
Path: path.Join(bundle.Path, entry.Name()),
|
||||
ContentFunc: func(context.Context) ([]byte, error) { return bytes.Clone(stmt.Predicate), nil },
|
||||
ContentFunc: func(context.Context) ([]byte, error) { return predicate, nil },
|
||||
InToto: result.InTotoAttestation{
|
||||
PredicateType: stmt.PredicateType,
|
||||
Subjects: subjects,
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
cacheconfig "github.com/moby/buildkit/cache/config"
|
||||
"github.com/moby/buildkit/exporter"
|
||||
"github.com/moby/buildkit/exporter/attestation"
|
||||
intotojson "github.com/moby/buildkit/exporter/attestation/intoto"
|
||||
"github.com/moby/buildkit/exporter/containerimage/exptypes"
|
||||
"github.com/moby/buildkit/exporter/util/epoch"
|
||||
"github.com/moby/buildkit/session"
|
||||
@@ -321,7 +320,7 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var defaultSubjects []intotojson.Subject
|
||||
var defaultSubjects []intoto.Subject
|
||||
for name := range strings.SplitSeq(opts.ImageName, ",") {
|
||||
if name == "" {
|
||||
continue
|
||||
@@ -330,7 +329,7 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defaultSubjects = append(defaultSubjects, intotojson.Subject{
|
||||
defaultSubjects = append(defaultSubjects, intoto.Subject{
|
||||
Name: pl,
|
||||
Digest: result.ToDigestMap(desc.Digest),
|
||||
})
|
||||
@@ -580,7 +579,7 @@ func (ic *ImageWriter) commitDistributionManifest(ctx context.Context, opts *Ima
|
||||
}, &configDesc, nil
|
||||
}
|
||||
|
||||
func (ic *ImageWriter) commitAttestationsManifest(ctx context.Context, opts *ImageCommitOpts, target ocispecs.Descriptor, statements []*intotojson.Statement, ociArtifact bool) (*ocispecs.Descriptor, error) {
|
||||
func (ic *ImageWriter) commitAttestationsManifest(ctx context.Context, opts *ImageCommitOpts, target ocispecs.Descriptor, statements []intoto.Statement, ociArtifact bool) (*ocispecs.Descriptor, error) {
|
||||
var (
|
||||
manifestType = ocispecs.MediaTypeImageManifest
|
||||
configType = ocispecs.MediaTypeImageConfig
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
intoto "github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/moby/buildkit/cache"
|
||||
"github.com/moby/buildkit/exporter"
|
||||
"github.com/moby/buildkit/exporter/attestation"
|
||||
intotojson "github.com/moby/buildkit/exporter/attestation/intoto"
|
||||
"github.com/moby/buildkit/exporter/util/epoch"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/moby/buildkit/snapshot"
|
||||
@@ -148,7 +148,7 @@ func CreateFS(ctx context.Context, sessionID string, k string, ref cache.Immutab
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(attestations) > 0 {
|
||||
subjects := []intotojson.Subject{}
|
||||
subjects := []intoto.Subject{}
|
||||
err = outputFS.Walk(ctx, "", func(path string, entry fs.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -165,7 +165,7 @@ func CreateFS(ctx context.Context, sessionID string, k string, ref cache.Immutab
|
||||
if _, err := io.Copy(d.Hash(), f); err != nil {
|
||||
return err
|
||||
}
|
||||
subjects = append(subjects, intotojson.Subject{
|
||||
subjects = append(subjects, intoto.Subject{
|
||||
Name: path,
|
||||
Digest: result.ToDigestMap(d.Digest()),
|
||||
})
|
||||
|
||||
2
go.mod
2
go.mod
@@ -46,7 +46,6 @@ require (
|
||||
github.com/hashicorp/go-immutable-radix/v2 v2.1.0
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7
|
||||
github.com/hiddeco/sshsig v0.2.0
|
||||
github.com/in-toto/attestation v1.1.2
|
||||
github.com/in-toto/in-toto-golang v0.11.0
|
||||
github.com/klauspost/compress v1.18.6
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
@@ -194,6 +193,7 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/hanwen/go-fuse/v2 v2.9.0 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/in-toto/attestation v1.1.2 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/moby/sys/capability v0.4.0 // indirect
|
||||
github.com/moby/sys/mount v0.3.4 // indirect
|
||||
|
||||
Reference in New Issue
Block a user