mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
835 - Keyless Support for SBOM Attestations (#910)
Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
1cea0ecd5c
commit
d2d532f4a8
@ -8,6 +8,7 @@ import (
|
||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/config"
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@ -53,7 +54,19 @@ func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions) *c
|
||||
checkForApplicationUpdate()
|
||||
}
|
||||
|
||||
return attest.Run(cmd.Context(), app, args)
|
||||
// build cosign key options for attestation
|
||||
ko := sign.KeyOpts{
|
||||
KeyRef: app.Attest.KeyRef,
|
||||
FulcioURL: app.Attest.FulcioURL,
|
||||
IDToken: app.Attest.FulcioIdentityToken,
|
||||
InsecureSkipFulcioVerify: app.Attest.InsecureSkipFulcioVerify,
|
||||
RekorURL: app.Attest.RekorURL,
|
||||
OIDCIssuer: app.Attest.OIDCIssuer,
|
||||
OIDCClientID: app.Attest.OIDCClientID,
|
||||
OIDCRedirectURL: app.Attest.OIDCRedirectURL,
|
||||
}
|
||||
|
||||
return attest.Run(cmd.Context(), app, ko, args)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,8 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/wagoodman/go-progress"
|
||||
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
@ -18,15 +19,28 @@ import (
|
||||
"github.com/anchore/syft/internal/formats/cyclonedxjson"
|
||||
"github.com/anchore/syft/internal/formats/spdx22json"
|
||||
"github.com/anchore/syft/internal/formats/syftjson"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/ui"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/event"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/rekor"
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/sign"
|
||||
"github.com/sigstore/cosign/pkg/cosign"
|
||||
"github.com/sigstore/cosign/pkg/cosign/attestation"
|
||||
cbundle "github.com/sigstore/cosign/pkg/cosign/bundle"
|
||||
"github.com/sigstore/cosign/pkg/oci/mutate"
|
||||
ociremote "github.com/sigstore/cosign/pkg/oci/remote"
|
||||
"github.com/sigstore/cosign/pkg/oci/static"
|
||||
sigs "github.com/sigstore/cosign/pkg/signature"
|
||||
"github.com/sigstore/cosign/pkg/types"
|
||||
"github.com/sigstore/rekor/pkg/generated/client"
|
||||
"github.com/sigstore/rekor/pkg/generated/models"
|
||||
"github.com/sigstore/sigstore/pkg/signature/dsse"
|
||||
"github.com/wagoodman/go-partybus"
|
||||
|
||||
@ -43,7 +57,7 @@ var (
|
||||
intotoJSONDsseType = `application/vnd.in-toto+json`
|
||||
)
|
||||
|
||||
func Run(ctx context.Context, app *config.Application, args []string) error {
|
||||
func Run(ctx context.Context, app *config.Application, ko sign.KeyOpts, args []string) error {
|
||||
// We cannot generate an attestation for more than one output
|
||||
if len(app.Outputs) > 1 {
|
||||
return fmt.Errorf("unable to generate attestation for more than one output")
|
||||
@ -59,17 +73,20 @@ func Run(ctx context.Context, app *config.Application, args []string) error {
|
||||
format := syft.FormatByName(app.Outputs[0])
|
||||
predicateType := formatPredicateType(format)
|
||||
if predicateType == "" {
|
||||
return fmt.Errorf("could not produce attestation predicate for given format: %q. Available formats: %+v", options.FormatAliases(format.ID()), options.FormatAliases(allowedAttestFormats...))
|
||||
return fmt.Errorf(
|
||||
"could not produce attestation predicate for given format: %q. Available formats: %+v",
|
||||
options.FormatAliases(format.ID()),
|
||||
options.FormatAliases(allowedAttestFormats...),
|
||||
)
|
||||
}
|
||||
|
||||
passFunc, err := selectPassFunc(app.Attest.Key, app.Attest.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if app.Attest.KeyRef != "" {
|
||||
passFunc, err := selectPassFunc(app.Attest.KeyRef, app.Attest.Password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ko := sign.KeyOpts{
|
||||
KeyRef: app.Attest.Key,
|
||||
PassFunc: passFunc,
|
||||
ko.PassFunc = passFunc
|
||||
}
|
||||
|
||||
sv, err := sign.SignerFromKeyOpts(ctx, "", "", ko)
|
||||
@ -144,7 +161,7 @@ func execWorker(app *config.Application, sourceInput source.Input, format sbom.F
|
||||
return
|
||||
}
|
||||
|
||||
err = generateAttestation(sbomBytes, src, sv, predicateType)
|
||||
err = generateAttestation(app, sbomBytes, src, sv, predicateType)
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
@ -153,7 +170,7 @@ func execWorker(app *config.Application, sourceInput source.Input, format sbom.F
|
||||
return errs
|
||||
}
|
||||
|
||||
func generateAttestation(predicate []byte, src *source.Source, sv *sign.SignerVerifier, predicateType string) error {
|
||||
func generateAttestation(app *config.Application, predicate []byte, src *source.Source, sv *sign.SignerVerifier, predicateType string) error {
|
||||
switch len(src.Image.Metadata.RepoDigests) {
|
||||
case 0:
|
||||
return fmt.Errorf("cannot generate attestation since no repo digests were found; make sure you're passing an OCI registry source for the attest command")
|
||||
@ -163,11 +180,22 @@ func generateAttestation(predicate []byte, src *source.Source, sv *sign.SignerVe
|
||||
}
|
||||
|
||||
wrapped := dsse.WrapSigner(sv, intotoJSONDsseType)
|
||||
ref, err := name.ParseReference(src.Metadata.ImageMetadata.UserInput)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
digest, err := ociremote.ResolveDigest(ref)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h, _ := v1.NewHash(digest.Identifier())
|
||||
|
||||
sh, err := attestation.GenerateStatement(attestation.GenerateOpts{
|
||||
Predicate: bytes.NewBuffer(predicate),
|
||||
Type: predicateType,
|
||||
Digest: findValidDigest(src.Image.Metadata.RepoDigests),
|
||||
Digest: h.Hex,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -183,24 +211,107 @@ func generateAttestation(predicate []byte, src *source.Source, sv *sign.SignerVe
|
||||
return errors.Wrap(err, "unable to sign SBOM")
|
||||
}
|
||||
|
||||
// We want to give the option to not upload the generated attestation
|
||||
// if passed or if the user is using local PKI
|
||||
if app.Attest.NoUpload || app.Attest.KeyRef != "" {
|
||||
bus.Publish(partybus.Event{
|
||||
Type: event.Exit,
|
||||
Value: func() error {
|
||||
_, err := os.Stdout.Write(signedPayload)
|
||||
return err
|
||||
},
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
return uploadAttestation(app, signedPayload, digest, sv)
|
||||
}
|
||||
|
||||
func trackUploadAttestation() (*progress.Stage, *progress.Manual) {
|
||||
stage := &progress.Stage{}
|
||||
prog := &progress.Manual{}
|
||||
|
||||
bus.Publish(partybus.Event{
|
||||
Type: event.UploadAttestation,
|
||||
Value: progress.StagedProgressable(&struct {
|
||||
progress.Stager
|
||||
progress.Progressable
|
||||
}{
|
||||
Stager: stage,
|
||||
Progressable: prog,
|
||||
}),
|
||||
})
|
||||
|
||||
return stage, prog
|
||||
}
|
||||
|
||||
// uploads signed SBOM payload to Rekor transparency log along with key information;
|
||||
// returns a bundle for attestation annotations
|
||||
// rekor bundle includes a signed payload and rekor timestamp;
|
||||
// the bundle is then wrapped onto an OCI signed entity and uploaded to
|
||||
// the user's image's OCI registry repository as *.att
|
||||
func uploadAttestation(app *config.Application, signedPayload []byte, digest name.Digest, sv *sign.SignerVerifier) error {
|
||||
// add application/vnd.dsse.envelope.v1+json as media type for other applications to decode attestation
|
||||
opts := []static.Option{static.WithLayerMediaType(types.DssePayloadType)}
|
||||
if sv.Cert != nil {
|
||||
opts = append(opts, static.WithCertChain(sv.Cert, sv.Chain))
|
||||
}
|
||||
|
||||
stage, prog := trackUploadAttestation()
|
||||
defer prog.SetCompleted() // just in case we return early
|
||||
|
||||
prog.Total = 2
|
||||
stage.Current = "uploading signing information to transparency log"
|
||||
|
||||
// uploads payload to Rekor transparency log along with key information;
|
||||
// returns bundle for attesation annotations
|
||||
// rekor bundle includes a signed payload and rekor timestamp;
|
||||
// the bundle is then wrapped onto an OCI signed entity and uploaded to
|
||||
// the user's image's OCI registry repository as *.att
|
||||
bundle, err := uploadToTlog(context.TODO(), sv, app.Attest.RekorURL, func(r *client.Rekor, b []byte) (*models.LogEntryAnon, error) {
|
||||
return cosign.TLogUploadInTotoAttestation(context.TODO(), r, signedPayload, b)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prog.N = 1
|
||||
stage.Current = "uploading attestation to OCI registry"
|
||||
|
||||
// add bundle OCI attestation that is uploaded to
|
||||
opts = append(opts, static.WithBundle(bundle))
|
||||
sig, err := static.NewAttestation(signedPayload, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
se, err := ociremote.SignedEntity(digest)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newSE, err := mutate.AttachAttestationToEntity(se, sig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Publish the attestations associated with this entity
|
||||
err = ociremote.WriteAttestations(digest.Repository, newSE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
prog.SetCompleted()
|
||||
|
||||
bus.Publish(partybus.Event{
|
||||
Type: event.Exit,
|
||||
Value: func() error {
|
||||
_, err := os.Stdout.Write(signedPayload)
|
||||
return err
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func findValidDigest(digests []string) string {
|
||||
// since we are only using the OCI repo provider for this source we are safe that this is only 1 value
|
||||
// see https://github.com/anchore/stereoscope/blob/25ebd49a842b5ac0a20c2e2b4b81335b64ad248c/pkg/image/oci/registry_provider.go#L57-L63
|
||||
split := strings.Split(digests[0], "sha256:")
|
||||
return split[1]
|
||||
}
|
||||
|
||||
func formatPredicateType(format sbom.Format) string {
|
||||
switch format.ID() {
|
||||
case spdx22json.ID:
|
||||
@ -214,3 +325,33 @@ func formatPredicateType(format sbom.Format) string {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
type tlogUploadFn func(*client.Rekor, []byte) (*models.LogEntryAnon, error)
|
||||
|
||||
func uploadToTlog(ctx context.Context, sv *sign.SignerVerifier, rekorURL string, upload tlogUploadFn) (*cbundle.RekorBundle, error) {
|
||||
var rekorBytes []byte
|
||||
// Upload the cert or the public key, depending on what we have
|
||||
if sv.Cert != nil {
|
||||
rekorBytes = sv.Cert
|
||||
} else {
|
||||
pemBytes, err := sigs.PublicKeyPem(sv, signatureoptions.WithContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rekorBytes = pemBytes
|
||||
}
|
||||
|
||||
rekorClient, err := rekor.NewClient(rekorURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
entry, err := upload(rekorClient, rekorBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.LogIndex != nil {
|
||||
log.Debugf("transparency log entry created with index: %v", *entry.LogIndex)
|
||||
}
|
||||
return cbundle.EntryToBundle(entry), nil
|
||||
}
|
||||
|
||||
@ -6,17 +6,50 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const defaultKeyFileName = "cosign.key"
|
||||
|
||||
type AttestOptions struct {
|
||||
Key string
|
||||
Key string
|
||||
Cert string
|
||||
CertChain string
|
||||
NoUpload bool
|
||||
Force bool
|
||||
Recursive bool
|
||||
|
||||
Rekor RekorOptions
|
||||
Fulcio FulcioOptions
|
||||
OIDC OIDCOptions
|
||||
}
|
||||
|
||||
var _ Interface = (*AttestOptions)(nil)
|
||||
|
||||
func (o *AttestOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
||||
cmd.PersistentFlags().StringVarP(&o.Key, "key", "", "cosign.key",
|
||||
if err := o.Rekor.AddFlags(cmd, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.Fulcio.AddFlags(cmd, v); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.OIDC.AddFlags(cmd, v); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmd.Flags().StringVarP(&o.Key, "key", "", defaultKeyFileName,
|
||||
"path to the private key file to use for attestation")
|
||||
|
||||
return bindAttestationConfigOptions(cmd.PersistentFlags(), v)
|
||||
cmd.Flags().StringVarP(&o.Cert, "cert", "", "",
|
||||
"path to the x.509 certificate in PEM format to include in the OCI Signature")
|
||||
|
||||
cmd.Flags().BoolVarP(&o.NoUpload, "no-upload", "", false,
|
||||
"do not upload the generated attestation")
|
||||
|
||||
cmd.Flags().BoolVarP(&o.Force, "force", "", false,
|
||||
"skip warnings and confirmations")
|
||||
|
||||
cmd.Flags().BoolVarP(&o.Recursive, "recursive", "", false,
|
||||
"if a multi-arch image is specified, additionally sign each discrete image")
|
||||
|
||||
return bindAttestationConfigOptions(cmd.Flags(), v)
|
||||
}
|
||||
|
||||
func bindAttestationConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
||||
@ -24,5 +57,21 @@ func bindAttestationConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.BindPFlag("attest.cert", flags.Lookup("cert")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.BindPFlag("attest.no-upload", flags.Lookup("no-upload")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.BindPFlag("attest.force", flags.Lookup("force")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.BindPFlag("attest.recursive", flags.Lookup("recursive")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
48
cmd/syft/cli/options/fulcio.go
Normal file
48
cmd/syft/cli/options/fulcio.go
Normal file
@ -0,0 +1,48 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const defaultFulcioURL = "https://fulcio.sigstore.dev"
|
||||
|
||||
// FulcioOptions is the wrapper for Fulcio related options.
|
||||
type FulcioOptions struct {
|
||||
URL string
|
||||
IdentityToken string
|
||||
InsecureSkipFulcioVerify bool
|
||||
}
|
||||
|
||||
var _ Interface = (*FulcioOptions)(nil)
|
||||
|
||||
// AddFlags implements Interface
|
||||
func (o *FulcioOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
||||
// TODO: change this back to api.SigstorePublicServerURL after the v1 migration is complete.
|
||||
cmd.Flags().StringVar(&o.URL, "fulcio-url", defaultFulcioURL,
|
||||
"address of sigstore PKI server")
|
||||
|
||||
cmd.Flags().StringVar(&o.IdentityToken, "identity-token", "",
|
||||
"identity token to use for certificate from fulcio")
|
||||
|
||||
cmd.Flags().BoolVar(&o.InsecureSkipFulcioVerify, "insecure-skip-verify", false,
|
||||
"skip verifying fulcio certificat and the SCT (Signed Certificate Timestamp) (this should only be used for testing).")
|
||||
return bindFulcioConfigOptions(cmd.Flags(), v)
|
||||
}
|
||||
|
||||
func bindFulcioConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
||||
if err := v.BindPFlag("attest.fulcio-url", flags.Lookup("fulcio-url")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.BindPFlag("attest.fulcio-identity-token", flags.Lookup("identity-token")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.BindPFlag("attest.insecure-skip-verify", flags.Lookup("insecure-skip-verify")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
48
cmd/syft/cli/options/oidc.go
Normal file
48
cmd/syft/cli/options/oidc.go
Normal file
@ -0,0 +1,48 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const DefaultOIDCIssuerURL = "https://oauth2.sigstore.dev/auth"
|
||||
|
||||
// OIDCOptions is the wrapper for OIDC related options.
|
||||
type OIDCOptions struct {
|
||||
Issuer string
|
||||
ClientID string
|
||||
RedirectURL string
|
||||
}
|
||||
|
||||
var _ Interface = (*OIDCOptions)(nil)
|
||||
|
||||
// AddFlags implements Interface
|
||||
func (o *OIDCOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
||||
cmd.Flags().StringVar(&o.Issuer, "oidc-issuer", DefaultOIDCIssuerURL,
|
||||
"OIDC provider to be used to issue ID token")
|
||||
|
||||
cmd.Flags().StringVar(&o.ClientID, "oidc-client-id", "sigstore",
|
||||
"OIDC client ID for application")
|
||||
|
||||
cmd.Flags().StringVar(&o.RedirectURL, "oidc-redirect-url", "",
|
||||
"OIDC redirect URL (Optional)")
|
||||
|
||||
return bindOIDCConfigOptions(cmd.Flags(), v)
|
||||
}
|
||||
|
||||
func bindOIDCConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
||||
if err := v.BindPFlag("attest.oidc-issuer", flags.Lookup("oidc-issuer")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.BindPFlag("attest.oidc-client-id", flags.Lookup("oidc-client-id")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := v.BindPFlag("attest.oidc-redirect-url", flags.Lookup("oidc-redirect-url")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
32
cmd/syft/cli/options/rekor.go
Normal file
32
cmd/syft/cli/options/rekor.go
Normal file
@ -0,0 +1,32 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
const DefaultRekorURL = "https://rekor.sigstore.dev"
|
||||
|
||||
// RekorOptions is the wrapper for Rekor related options.
|
||||
type RekorOptions struct {
|
||||
URL string
|
||||
}
|
||||
|
||||
var _ Interface = (*RekorOptions)(nil)
|
||||
|
||||
// AddFlags implements Interface
|
||||
func (o *RekorOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
||||
cmd.Flags().StringVar(&o.URL, "rekor-url", DefaultRekorURL,
|
||||
"address of rekor STL server")
|
||||
return bindRekorConfigOptions(cmd.Flags(), v)
|
||||
}
|
||||
|
||||
func bindRekorConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
||||
// TODO: config re-design
|
||||
if err := v.BindPFlag("attest.rekor-url", flags.Lookup("rekor-url")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
4
go.mod
4
go.mod
@ -58,6 +58,7 @@ require (
|
||||
github.com/docker/docker v20.10.12+incompatible
|
||||
github.com/in-toto/in-toto-golang v0.3.4-0.20211211042327-af1f9fb822bf
|
||||
github.com/sigstore/cosign v1.7.2
|
||||
github.com/sigstore/rekor v0.4.1-0.20220114213500-23f583409af3
|
||||
github.com/sigstore/sigstore v1.2.1-0.20220401110139-0e610e39782f
|
||||
)
|
||||
|
||||
@ -209,7 +210,6 @@ require (
|
||||
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/sigstore/fulcio v0.1.2-0.20220114150912-86a2036f9bc7 // indirect
|
||||
github.com/sigstore/rekor v0.4.1-0.20220114213500-23f583409af3 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.0.0 // indirect
|
||||
@ -292,7 +292,7 @@ require (
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-containerregistry v0.8.1-0.20220209165246-a44adc326839 // indirect
|
||||
github.com/google/go-containerregistry v0.8.1-0.20220209165246-a44adc326839
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
|
||||
|
||||
@ -5,22 +5,35 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/sigstore/cosign/cmd/cosign/cli/options"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// IMPORTANT: do not show the password in any YAML/JSON output (sensitive information)
|
||||
type attest struct {
|
||||
Key string `yaml:"key" json:"key" mapstructure:"key"` // same as --key, file path to the private key
|
||||
// IMPORTANT: do not show the password in any YAML/JSON output (sensitive information)
|
||||
Password string `yaml:"-" json:"-" mapstructure:"password"` // password for the private key
|
||||
KeyRef string `yaml:"key" json:"key" mapstructure:"key"` // same as --key, file path to the private key
|
||||
Cert string `yaml:"cert" json:"cert" mapstructure:"cert"`
|
||||
NoUpload bool `yaml:"no_upload" json:"noUpload" mapstructure:"no_upload"`
|
||||
Force bool `yaml:"force" json:"force" mapstructure:"force"`
|
||||
Recursive bool `yaml:"recursive" json:"recursive" mapstructure:"recursive"`
|
||||
Replace bool `yaml:"replace" json:"replace" mapstructure:"replace"`
|
||||
Password string `yaml:"-" json:"-" mapstructure:"password"` // password for the private key
|
||||
FulcioURL string `yaml:"fulcio_url" json:"fulcioUrl" mapstructure:"fulcio_url"`
|
||||
FulcioIdentityToken string `yaml:"fulcio_identity_token" json:"fulcio_identity_token" mapstructure:"fulcio_identity_token"`
|
||||
InsecureSkipFulcioVerify bool `yaml:"insecure_skip_verify" json:"insecure_skip_verify" mapstructure:"insecure_skip_verify"`
|
||||
RekorURL string `yaml:"rekor_url" json:"rekorUrl" mapstructure:"rekor_url"`
|
||||
OIDCIssuer string `yaml:"oidc_issuer" json:"oidcIssuer" mapstructure:"oidc_issuer"`
|
||||
OIDCClientID string `yaml:"oidc_client_id" json:"oidcClientId" mapstructure:"oidc_client_id"`
|
||||
OIDCRedirectURL string `yaml:"oidc_redirect_url" json:"OIDCRedirectURL" mapstructure:"oidc_redirect_url"`
|
||||
}
|
||||
|
||||
func (cfg *attest) parseConfigValues() error {
|
||||
if cfg.Key != "" {
|
||||
expandedPath, err := homedir.Expand(cfg.Key)
|
||||
if cfg.KeyRef != "" {
|
||||
expandedPath, err := homedir.Expand(cfg.KeyRef)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to expand key path=%q: %w", cfg.Key, err)
|
||||
return fmt.Errorf("unable to expand key path=%q: %w", cfg.KeyRef, err)
|
||||
}
|
||||
cfg.Key = expandedPath
|
||||
cfg.KeyRef = expandedPath
|
||||
}
|
||||
|
||||
if cfg.Password == "" {
|
||||
@ -34,5 +47,10 @@ func (cfg *attest) parseConfigValues() error {
|
||||
}
|
||||
|
||||
func (cfg attest) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("attest.key", "")
|
||||
v.SetDefault("attest.password", "")
|
||||
v.SetDefault("attest.fulcio_url", options.DefaultFulcioURL)
|
||||
v.SetDefault("attest.rekor_url", options.DefaultRekorURL)
|
||||
v.SetDefault("attest.oidc_issuer", options.DefaultOIDCIssuerURL)
|
||||
v.SetDefault("attest.oidc_client_id", "sigstore")
|
||||
}
|
||||
|
||||
@ -2,11 +2,11 @@ SPDXVersion: SPDX-2.2
|
||||
DataLicense: CC0-1.0
|
||||
SPDXID: SPDXRef-DOCUMENT
|
||||
DocumentName: .
|
||||
DocumentNamespace: https://anchore.com/syft/dir/644e30b7-5a31-4d0b-a903-b96c757921c2
|
||||
DocumentNamespace: https://anchore.com/syft/dir/8fbb3714-785d-4e3e-95cf-44a258bc65b0
|
||||
LicenseListVersion: 3.16
|
||||
Creator: Organization: Anchore, Inc
|
||||
Creator: Tool: syft-[not provided]
|
||||
Created: 2022-04-19T15:10:19Z
|
||||
Created: 2022-05-02T15:27:05Z
|
||||
|
||||
##### Package: @at-sign
|
||||
|
||||
|
||||
@ -31,4 +31,7 @@ const (
|
||||
|
||||
// ImportStarted is a partybus event that occurs when an SBOM upload process has begun
|
||||
ImportStarted partybus.EventType = "syft-import-started-event"
|
||||
|
||||
// UploadAttestation is a partybus event that occurs when syft uploads an attestation to an OCI registry (+ any transparency log)
|
||||
UploadAttestation partybus.EventType = "syft-upload-attestation"
|
||||
)
|
||||
|
||||
@ -151,3 +151,16 @@ func ParseImportStarted(e partybus.Event) (string, progress.StagedProgressable,
|
||||
|
||||
return host, prog, nil
|
||||
}
|
||||
|
||||
func ParseUploadAttestation(e partybus.Event) (progress.StagedProgressable, error) {
|
||||
if err := checkEventType(e.Type, event.UploadAttestation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prog, ok := e.Value.(progress.StagedProgressable)
|
||||
if !ok {
|
||||
return nil, newPayloadErr(e.Type, "Value", e.Value)
|
||||
}
|
||||
|
||||
return prog, nil
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ func TestAttestCmd(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "can encode syft.json as the predicate given a password",
|
||||
args: []string{"attest", "-o", "json", img},
|
||||
args: []string{"attest", "-o", "json", "--key", "cosign.key", img},
|
||||
assertions: []traitAssertion{
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
@ -34,7 +34,7 @@ func TestAttestCmd(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "can encode syft.json as the predicate given a blank password",
|
||||
args: []string{"attest", "-o", "json", img},
|
||||
args: []string{"attest", "-o", "json", "--key", "cosign.key", img},
|
||||
assertions: []traitAssertion{
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
|
||||
@ -59,9 +59,11 @@ func TestCosignWorkflow(t *testing.T) {
|
||||
"attest",
|
||||
"-o",
|
||||
"json",
|
||||
"--key",
|
||||
"cosign.key",
|
||||
img,
|
||||
},
|
||||
// cosign attach attestation --attestation image_latest_sbom_attestation.json caphill4/attest:latest
|
||||
// cosign attach attestation
|
||||
cosignAttachArgs: []string{
|
||||
"attach",
|
||||
"attestation",
|
||||
@ -69,7 +71,7 @@ func TestCosignWorkflow(t *testing.T) {
|
||||
attestationFile,
|
||||
img,
|
||||
},
|
||||
// cosign verify-attestation -key cosign.pub caphill4/attest:latest
|
||||
// cosign verify-attestation
|
||||
cosignVerifyArgs: []string{
|
||||
"verify-attestation",
|
||||
"-key",
|
||||
|
||||
10
test/cli/test-fixtures/attestation/Dockerfile.ctfe_init
Normal file
10
test/cli/test-fixtures/attestation/Dockerfile.ctfe_init
Normal file
@ -0,0 +1,10 @@
|
||||
FROM golang:1.17.8@sha256:c7c94588b6445f5254fbc34df941afa10de04706deb330e62831740c9f0f2030 AS builder
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
RUN go install github.com/google/trillian/cmd/createtree@v1.3.10
|
||||
ADD ./config/logid.sh /root/
|
||||
ADD ./config/ctfe /root/ctfe
|
||||
RUN chmod +x /root/logid.sh
|
||||
|
||||
CMD /root/logid.sh
|
||||
9
test/cli/test-fixtures/attestation/config/config.json
Normal file
9
test/cli/test-fixtures/attestation/config/config.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"OIDCIssuers": {
|
||||
"http://dex-idp:8888/auth": {
|
||||
"IssuerURL": "http://dex-idp:8888/auth",
|
||||
"ClientID": "fulcio",
|
||||
"Type": "email"
|
||||
}
|
||||
}
|
||||
}
|
||||
11
test/cli/test-fixtures/attestation/config/ctfe/ct_server.cfg
Normal file
11
test/cli/test-fixtures/attestation/config/ctfe/ct_server.cfg
Normal file
@ -0,0 +1,11 @@
|
||||
config {
|
||||
log_id: %LOGID%
|
||||
prefix: "test"
|
||||
roots_pem_file: "/etc/config/root.pem"
|
||||
private_key: {
|
||||
[type.googleapis.com/keyspb.PEMKeyFile] {
|
||||
path: "/etc/config/privkey.pem"
|
||||
password: "foobar"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
-----BEGIN EC PRIVATE KEY-----
|
||||
Proc-Type: 4,ENCRYPTED
|
||||
DEK-Info: DES-CBC,05BAAA9143C46320
|
||||
|
||||
AttbquLclNy7ZEnlDFpReZvV2PZKuv89YMWqDvGGtnBVw+3eXYIa54Xli1CyXEPn
|
||||
qNGvibjIxj+Q19+VhA3n42SE2fHyULHKPZHebSL5qcVvZTqmbtAe/dZNH1SiGG2f
|
||||
bWauIw0oeHhXW5i9isxrLggPMRmPA65Ii3W7gyWFmjE=
|
||||
-----END EC PRIVATE KEY-----
|
||||
@ -0,0 +1,4 @@
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbbQiLx6GKy6ivhc11wJGbQjc2VX/
|
||||
mnuk5d670MTXR3p+LIAcxd5MhqIHpLmyYJ5mDKLEoZ/pC0nPuje3JueBcA==
|
||||
-----END PUBLIC KEY-----
|
||||
@ -0,0 +1,48 @@
|
||||
#
|
||||
# Copyright 2021 The Sigstore Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
issuer: http://dex-idp:8888/auth
|
||||
|
||||
storage:
|
||||
type: memory
|
||||
|
||||
web:
|
||||
http: dex-idp:8888
|
||||
|
||||
frontend:
|
||||
issuer: Fulcio in Docker Compose
|
||||
|
||||
expiry:
|
||||
signingKeys: "24h"
|
||||
idTokens: "1m"
|
||||
authRequests: "24h"
|
||||
|
||||
oauth2:
|
||||
responseTypes: [ "code" ]
|
||||
alwaysShowLoginScreen: false
|
||||
skipApprovalScreen: true
|
||||
|
||||
connectors:
|
||||
- type: mockCallback
|
||||
id: approved
|
||||
name: AlwaysApprovesOIDCProvider
|
||||
|
||||
staticClients:
|
||||
- id: fulcio
|
||||
public: true
|
||||
name: 'Fulcio in Docker Compose'
|
||||
|
||||
# Dex's issuer URL + "/callback"
|
||||
redirectURI: http://dex-idp:8888/auth/callback
|
||||
54
test/cli/test-fixtures/attestation/config/logid.sh
Normal file
54
test/cli/test-fixtures/attestation/config/logid.sh
Normal file
@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Copyright 2021 The Sigstore Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
function get_log_id() {
|
||||
curl -s --retry-connrefused --retry 10 http://trillian-log-server:8095/metrics |grep "^quota_acquired_tokens{spec=\"trees"|head -1|awk ' { print $1 } '|sed -e 's/[^0-9]*//g' > /tmp/logid
|
||||
}
|
||||
|
||||
function get_ephemeral_ca() {
|
||||
curl -s --retry-connrefused --retry 10 http://fulcio-server:5555/api/v1/rootCert > /etc/config/root.pem
|
||||
}
|
||||
|
||||
function create_log () {
|
||||
/go/bin/createtree -admin_server trillian-log-server:8096 > /tmp/logid
|
||||
echo -n "Created log ID " && cat /tmp/logid
|
||||
}
|
||||
|
||||
function update_config() {
|
||||
cat /root/ctfe/ct_server.cfg | sed -e "s/%LOGID%/"`cat /tmp/logid`"/g" > /etc/config/ct_server.cfg
|
||||
cp /root/ctfe/*.pem /etc/config/
|
||||
}
|
||||
|
||||
# check to see if log id exists; if so, use that
|
||||
echo -n "Checking for existing configuration..."
|
||||
echo "Checking for preexisting logs..."
|
||||
get_log_id
|
||||
# else create one
|
||||
if ! [[ -s /tmp/logid ]]; then
|
||||
echo "No log found; let's create one..."
|
||||
create_log
|
||||
fi
|
||||
echo "Updating config with current log"
|
||||
update_config
|
||||
|
||||
configid=`cat /etc/config/ct_server.cfg|grep log_id|awk ' { print $2 } '`
|
||||
echo "Exisiting configuration uses log ID $configid, exiting"
|
||||
|
||||
echo "Grabing fulcio root pem file"
|
||||
get_ephemeral_ca
|
||||
|
||||
echo "Finished ct_server configuration"
|
||||
@ -0,0 +1,48 @@
|
||||
#
|
||||
# Copyright 2021 The Sigstore Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
issuer: http://dex-idp:8888/auth
|
||||
|
||||
storage:
|
||||
type: memory
|
||||
|
||||
web:
|
||||
http: 0.0.0.0:8888
|
||||
|
||||
frontend:
|
||||
issuer: Fulcio in Docker Compose
|
||||
|
||||
expiry:
|
||||
signingKeys: "24h"
|
||||
idTokens: "1m"
|
||||
authRequests: "24h"
|
||||
|
||||
oauth2:
|
||||
responseTypes: [ "code" ]
|
||||
alwaysShowLoginScreen: true
|
||||
skipApprovalScreen: true
|
||||
|
||||
connectors:
|
||||
- type: mockCallback
|
||||
id: https://any.valid.url/
|
||||
name: AlwaysApprovesOIDCProvider
|
||||
|
||||
staticClients:
|
||||
- id: fulcio
|
||||
public: true
|
||||
name: 'Fulcio in Docker Compose'
|
||||
|
||||
# Dex's issuer URL + "/callback"
|
||||
redirectURI: http://dex-idp:8888/auth/callback
|
||||
116
test/cli/test-fixtures/attestation/docker-compose.yaml
Normal file
116
test/cli/test-fixtures/attestation/docker-compose.yaml
Normal file
@ -0,0 +1,116 @@
|
||||
version: '3.2'
|
||||
services:
|
||||
fulcio-server:
|
||||
image: fulcio_fulcio-server
|
||||
command: [
|
||||
"fulcio-server",
|
||||
"serve",
|
||||
"--host=0.0.0.0",
|
||||
"--port=5555",
|
||||
"--ca=ephemeralca",
|
||||
"--ct-log-url=http://ct_server:6962/test",
|
||||
]
|
||||
restart: always # keep the server running
|
||||
ports:
|
||||
- "5555:5555"
|
||||
volumes:
|
||||
- ./config/config.json:/etc/fulcio-config/config.json:z
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:5555/ping"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
depends_on:
|
||||
- dex-idp
|
||||
mysql:
|
||||
image: gcr.io/trillian-opensource-ci/db_server:3c8193ebb2d7fedb44d18e9c810d0d2e4dbb7e4d
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=password
|
||||
- MYSQL_DATABASE=test
|
||||
- MYSQL_USER=test
|
||||
- MYSQL_PASSWORD=password
|
||||
restart: always # keep the MySQL server running
|
||||
healthcheck:
|
||||
test: ["CMD", "/etc/init.d/mysql", "status"]
|
||||
interval: 30s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
ctfe_init:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile.ctfe_init
|
||||
depends_on:
|
||||
- trillian-log-server
|
||||
- fulcio-server
|
||||
volumes:
|
||||
- ctfeConfig:/etc/config/:rw
|
||||
ct_server:
|
||||
image: gcr.io/trillian-opensource-ci/ctfe
|
||||
volumes:
|
||||
- ctfeConfig:/etc/config/:rw
|
||||
command: [
|
||||
"--log_config" ,"/etc/config/ct_server.cfg",
|
||||
"--log_rpc_server", "trillian-log-server:8096",
|
||||
"--http_endpoint", "0.0.0.0:6962",
|
||||
"--alsologtostderr",
|
||||
]
|
||||
restart: always # retry while ctfe_init is running
|
||||
depends_on:
|
||||
- trillian-log-server
|
||||
- trillian-log-signer
|
||||
- ctfe_init
|
||||
ports:
|
||||
- "6962:6962"
|
||||
dex-idp:
|
||||
image: dexidp/dex:v2.30.0
|
||||
user: root
|
||||
command: [
|
||||
"dex",
|
||||
"serve",
|
||||
"/etc/config/docker-compose-config.yaml",
|
||||
]
|
||||
restart: always # keep the server running
|
||||
ports:
|
||||
- "8888:8888"
|
||||
volumes:
|
||||
- ./config/dex:/etc/config/:ro
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8888/auth/healthz"]
|
||||
interval: 10s
|
||||
timeout: 3s
|
||||
retries: 3
|
||||
start_period: 5s
|
||||
trillian-log-server:
|
||||
image: gcr.io/trillian-opensource-ci/log_server
|
||||
command: [
|
||||
"--storage_system=mysql",
|
||||
"--mysql_uri=test:password@tcp(mysql:3306)/test",
|
||||
"--rpc_endpoint=0.0.0.0:8096",
|
||||
"--http_endpoint=0.0.0.0:8095",
|
||||
"--alsologtostderr",
|
||||
]
|
||||
restart: always # retry while mysql is starting up
|
||||
ports:
|
||||
- "8096:8096"
|
||||
- "8095:8095"
|
||||
depends_on:
|
||||
- mysql
|
||||
trillian-log-signer:
|
||||
image: gcr.io/trillian-opensource-ci/log_signer
|
||||
command: [
|
||||
"--storage_system=mysql",
|
||||
"--mysql_uri=test:password@tcp(mysql:3306)/test",
|
||||
"--rpc_endpoint=0.0.0.0:8095",
|
||||
"--http_endpoint=0.0.0.0:8097",
|
||||
"--force_master",
|
||||
"--alsologtostderr",
|
||||
]
|
||||
restart: always # retry while mysql is starting up
|
||||
ports:
|
||||
- "8097:8097"
|
||||
depends_on:
|
||||
- mysql
|
||||
volumes:
|
||||
ctfeConfig: {}
|
||||
@ -226,6 +226,48 @@ func FetchImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Even
|
||||
return err
|
||||
}
|
||||
|
||||
func UploadAttestationHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error {
|
||||
prog, err := syftEventParsers.ParseUploadAttestation(event)
|
||||
if err != nil {
|
||||
return fmt.Errorf("bad %s event: %w", event.Type, err)
|
||||
}
|
||||
|
||||
line, err := fr.Append()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wg.Add(1)
|
||||
|
||||
formatter, spinner := startProcess()
|
||||
stream := progress.Stream(ctx, prog, interval)
|
||||
title := tileFormat.Sprint("Uploading attestation")
|
||||
|
||||
formatFn := func(p progress.Progress) {
|
||||
progStr, err := formatter.Format(p)
|
||||
spin := color.Magenta.Sprint(spinner.Next())
|
||||
if err != nil {
|
||||
_, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err))
|
||||
} else {
|
||||
auxInfo := auxInfoFormat.Sprintf("[%s]", prog.Stage())
|
||||
_, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s %s", spin, title, progStr, auxInfo))
|
||||
}
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
formatFn(progress.Progress{})
|
||||
for p := range stream {
|
||||
formatFn(p)
|
||||
}
|
||||
|
||||
spin := color.Green.Sprint(completedStatus)
|
||||
title = tileFormat.Sprint("Uploaded attestation")
|
||||
_, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate, spin, title))
|
||||
}()
|
||||
return err
|
||||
}
|
||||
|
||||
// ReadImageHandler periodically writes a the image read/parse/build-tree status in the form of a progress bar.
|
||||
func ReadImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error {
|
||||
_, prog, err := stereoEventParsers.ParseReadImage(event)
|
||||
@ -355,8 +397,8 @@ func SecretsCatalogerStartedHandler(ctx context.Context, fr *frame.Frame, event
|
||||
return err
|
||||
}
|
||||
|
||||
//nolint:dupl
|
||||
// FileMetadataCatalogerStartedHandler shows the intermittent secrets searching progress.
|
||||
// nolint:dupl
|
||||
func FileMetadataCatalogerStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error {
|
||||
prog, err := syftEventParsers.ParseFileMetadataCatalogingStarted(event)
|
||||
if err != nil {
|
||||
|
||||
@ -27,7 +27,16 @@ func NewHandler() *Handler {
|
||||
// RespondsTo indicates if the handler is capable of handling the given event.
|
||||
func (r *Handler) RespondsTo(event partybus.Event) bool {
|
||||
switch event.Type {
|
||||
case stereoscopeEvent.PullDockerImage, stereoscopeEvent.ReadImage, stereoscopeEvent.FetchImage, syftEvent.PackageCatalogerStarted, syftEvent.SecretsCatalogerStarted, syftEvent.FileDigestsCatalogerStarted, syftEvent.FileMetadataCatalogerStarted, syftEvent.FileIndexingStarted, syftEvent.ImportStarted:
|
||||
case stereoscopeEvent.PullDockerImage,
|
||||
stereoscopeEvent.ReadImage,
|
||||
stereoscopeEvent.FetchImage,
|
||||
syftEvent.UploadAttestation,
|
||||
syftEvent.PackageCatalogerStarted,
|
||||
syftEvent.SecretsCatalogerStarted,
|
||||
syftEvent.FileDigestsCatalogerStarted,
|
||||
syftEvent.FileMetadataCatalogerStarted,
|
||||
syftEvent.FileIndexingStarted,
|
||||
syftEvent.ImportStarted:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@ -46,6 +55,9 @@ func (r *Handler) Handle(ctx context.Context, fr *frame.Frame, event partybus.Ev
|
||||
case stereoscopeEvent.FetchImage:
|
||||
return FetchImageHandler(ctx, fr, event, wg)
|
||||
|
||||
case syftEvent.UploadAttestation:
|
||||
return UploadAttestationHandler(ctx, fr, event, wg)
|
||||
|
||||
case syftEvent.PackageCatalogerStarted:
|
||||
return PackageCatalogerStartedHandler(ctx, fr, event, wg)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user