Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mutate.AttachFileTo* for attaching SBOMs. #1084

Merged
merged 1 commit into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 55 additions & 9 deletions pkg/oci/mutate/mutate.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,29 +151,55 @@ func AttachAttestationToEntity(se oci.SignedEntity, att oci.Signature, opts ...S
}
}

// AttachFileToEntity attaches the provided file to the provided entity.
func AttachFileToEntity(se oci.SignedEntity, name string, f oci.File, opts ...SignOption) (oci.SignedEntity, error) {
switch obj := se.(type) {
case oci.SignedImage:
return AttachFileToImage(obj, name, f, opts...)
case oci.SignedImageIndex:
return AttachFileToImageIndex(obj, name, f, opts...)
default:
return nil, fmt.Errorf("unsupported type: %T", se)
}
}

// AttachSignatureToImage attaches the provided signature to the provided image.
func AttachSignatureToImage(si oci.SignedImage, sig oci.Signature, opts ...SignOption) (oci.SignedImage, error) {
return &signedImage{
SignedImage: si,
sig: sig,
attachments: make(map[string]oci.File),
so: makeSignOpts(opts...),
}, nil
}

// AttachAttestationToImage attaches the provided signature to the provided image.
// AttachAttestationToImage attaches the provided attestation to the provided image.
func AttachAttestationToImage(si oci.SignedImage, att oci.Signature, opts ...SignOption) (oci.SignedImage, error) {
return &signedImage{
SignedImage: si,
att: att,
attachments: make(map[string]oci.File),
so: makeSignOpts(opts...),
}, nil
}

// AttachFileToImage attaches the provided file to the provided image.
func AttachFileToImage(si oci.SignedImage, name string, f oci.File, opts ...SignOption) (oci.SignedImage, error) {
return &signedImage{
SignedImage: si,
attachments: map[string]oci.File{
name: f,
},
so: makeSignOpts(opts...),
}, nil
}

type signedImage struct {
oci.SignedImage
sig oci.Signature
att oci.Signature
so *signOpts
sig oci.Signature
att oci.Signature
so *signOpts
attachments map[string]oci.File
}

// Signatures implements oci.SignedImage
Expand Down Expand Up @@ -223,14 +249,18 @@ func (si *signedImage) Attestations() (oci.Signatures, error) {

// Attachment implements oci.SignedImage
func (si *signedImage) Attachment(attName string) (oci.File, error) {
return nil, errors.New("unimplemented")
if f, ok := si.attachments[attName]; ok {
return f, nil
}
return nil, fmt.Errorf("attachment %q not found", attName)
}

// AttachSignatureToImageIndex attaches the provided signature to the provided image index.
func AttachSignatureToImageIndex(sii oci.SignedImageIndex, sig oci.Signature, opts ...SignOption) (oci.SignedImageIndex, error) {
return &signedImageIndex{
ociSignedImageIndex: sii,
sig: sig,
attachments: make(map[string]oci.File),
so: makeSignOpts(opts...),
}, nil
}
Expand All @@ -240,17 +270,30 @@ func AttachAttestationToImageIndex(sii oci.SignedImageIndex, att oci.Signature,
return &signedImageIndex{
ociSignedImageIndex: sii,
att: att,
attachments: make(map[string]oci.File),
so: makeSignOpts(opts...),
}, nil
}

// AttachFileToImageIndex attaches the provided file to the provided image index.
func AttachFileToImageIndex(sii oci.SignedImageIndex, name string, f oci.File, opts ...SignOption) (oci.SignedImageIndex, error) {
return &signedImageIndex{
ociSignedImageIndex: sii,
attachments: map[string]oci.File{
name: f,
},
so: makeSignOpts(opts...),
}, nil
}

type ociSignedImageIndex oci.SignedImageIndex

type signedImageIndex struct {
ociSignedImageIndex
sig oci.Signature
att oci.Signature
so *signOpts
sig oci.Signature
att oci.Signature
so *signOpts
attachments map[string]oci.File
}

// Signatures implements oci.SignedImageIndex
Expand Down Expand Up @@ -300,5 +343,8 @@ func (sii *signedImageIndex) Attestations() (oci.Signatures, error) {

// Attachment implements oci.SignedImageIndex
func (sii *signedImageIndex) Attachment(attName string) (oci.File, error) {
return nil, errors.New("unimplemented")
if f, ok := sii.attachments[attName]; ok {
return f, nil
}
return nil, fmt.Errorf("attachment %q not found", attName)
}
35 changes: 35 additions & 0 deletions pkg/oci/mutate/mutate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
package mutate

import (
"crypto/rand"
"errors"
"fmt"
"reflect"
"testing"

v1 "github.com/google/go-containerregistry/pkg/v1"
Expand Down Expand Up @@ -161,6 +163,39 @@ func TestSignEntity(t *testing.T) {
}
sii := signed.ImageIndex(ii)

t.Run("attach SBOMs", func(t *testing.T) {
for _, se := range []oci.SignedEntity{si, sii} {
want := make([]byte, 300)
rand.Read(want)

orig, err := static.NewFile(want)
if err != nil {
t.Fatalf("static.NewFile() = %v", err)
}
se, err = AttachFileToEntity(se, "sbom", orig)
if err != nil {
t.Fatalf("AttachFileToEntity() = %v", err)
}

f, err := se.Attachment("sbom")
if err != nil {
t.Fatalf("Attachment(sbom) = %v", err)
}
got, err := f.Payload()
if err != nil {
t.Fatalf("Payload() = %v", err)
}
if !reflect.DeepEqual(want, got) {
t.Errorf("Payload() = %v, wanted %v", got, want)
}

f, err = se.Attachment("gitbom")
if err == nil {
t.Errorf("Attachment(gitbom) = %T, wanted error", f)
}
}
})

t.Run("without duplicate detector (signature)", func(t *testing.T) {
for _, se := range []oci.SignedEntity{si, sii} {
orig, err := static.NewSignature(nil, "")
Expand Down