Compare commits

...
14 Commits
Author SHA1 Message Date
Keith Zantow 91e2fd8532 Fix potential race condition during event subscription (#993) 2022-05-11 18:35:55 -04:00
Jonas XavierandKeith Zantow 24f08e7738 Convert between SBOM formats (#964)
* add convert command

Signed-off-by: Jonas Galvão Xavier <jonas.agx@gmail.com>

* mvp

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* fix hanging bug

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* validate SBOM formats for conversion

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* move convert cmd to new structure

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* remove bin

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* drop event loop from convert cmd

extract SBOM type from document namespace

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* validate SPDX in tests

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* documenting convert cmd

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* support output format=file.json notation

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* test convertible formats

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* fix typo

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* clean up

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* more clean up and docs

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* feedback changes

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* nit

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* feedback changes

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* re-use more code

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* undo encode-decode cycle test

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* remove unnecessary test constraint

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* fix readme

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* try verbose

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* cleaner README and no table conversion

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* simpler conversion

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* feedback changes and cleanup

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* nit space fix

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* use defer

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

* feedback changes

Signed-off-by: Jonas Xavier <jonasx@anchore.com>

Co-authored-by: Keith Zantow <kzantow@gmail.com>
2022-05-09 17:28:33 -07:00
Christopher Angelo Phillips a83506628c Add README updates for Keyless features (#988) 2022-05-09 16:07:28 +00:00
Jonas Xavier 42f8601919 Fix tests: add timeout to long-running failures, update SPDX license list (#989) 2022-05-09 11:48:44 -04:00
Christopher Angelo PhillipsandAlex Goodman d2d532f4a8 835 - Keyless Support for SBOM Attestations (#910)
Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
2022-05-06 18:06:32 -04:00
Christian KotzbauerandAlex Goodman 1cea0ecd5c feat: add initial dotnet-support (#951)
* feat: add initial dotnet-support

Signed-off-by: Christian Kotzbauer <git@ckotzbauer.de>

* fix: add path, sha512 and hashpath

Signed-off-by: Christian Kotzbauer <git@ckotzbauer.de>

* fix: add missing dot

Signed-off-by: Christian Kotzbauer <git@ckotzbauer.de>

* fix: lint warnings

Signed-off-by: Christian Kotzbauer <git@ckotzbauer.de>

* fix CLI test package counts to account for dotnet

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* fix: updated packagurl-go

Signed-off-by: Christian Kotzbauer <git@ckotzbauer.de>

* tidy go.sum

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

* update json schema

Signed-off-by: Alex Goodman <alex.goodman@anchore.com>

Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
2022-05-05 15:32:02 -04:00
Alex Goodman d2f053bc71 unblock timeout for power-user select CLI tests (#985)
* update to use shared secretsFixture to prevent race

Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com>
2022-05-05 15:12:11 +00:00
Jonas Xavier 2fc344aba4 golang cataloger - main module version as is (#986)
Signed-off-by: Jonas Xavier <jonasx@anchore.com>
2022-05-05 00:01:00 -07:00
Steven Maude 8b6c576d78 Fix github-json output option (#967)
* Fix "bad output format" for `github-json` output

Signed-off-by: Steven Maude <git@stevenmaude.co.uk>

* Update formats in README

Signed-off-by: Steven Maude <git@stevenmaude.co.uk>

* Run `make lint-fix`

Signed-off-by: Steven Maude <git@stevenmaude.co.uk>
2022-05-04 17:25:40 -07:00
Jonas Xavier ab289933da read Go main module version as is - (devel) (#981)
* read Go main module version as is - (devel)

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>

* fix package test with default (devel) main module

Signed-off-by: Jonas Galvão Xavier <jonasx@anchore.com>
2022-05-04 10:47:13 -07:00
Dan Luhring 37927b8b23 reduce logging severity for non-Go binaries (#983) 2022-05-03 09:38:14 -04:00
Christopher Angelo Phillips 03d51c36d0 golang.org/x/crypto upgrade (#979) 2022-05-02 21:33:40 +00:00
Dan Luhring 0bd3558fb2 reduce noise of log output (#976) 2022-05-02 14:54:30 +00:00
Christopher Angelo Phillips 4ce2edda9e add version info and remove double config call (#977) 2022-05-02 14:54:10 +00:00
83 changed files with 3540 additions and 253 deletions
+54 -1
View File
@@ -25,12 +25,14 @@ A CLI tool and Go library for generating a Software Bill of Materials (SBOM) fro
- Linux distribution identification
- Works seamlessly with [Grype](https://github.com/anchore/grype) (a fast, modern vulnerability scanner)
- Able to create signed SBOM attestations using the [in-toto specification](https://github.com/in-toto/attestation/blob/main/spec/README.md)
- Convert between SBOM formats, such as CycloneDX, SPDX, and Syft's own format.
### Supported Ecosystems
- Alpine (apk)
- Dart (pubs)
- Debian (dpkg)
- Dotnet (deps.json)
- Go (go.mod, Go binaries)
- Java (jar, ear, war, par, sar)
- JavaScript (npm, yarn)
@@ -92,9 +94,59 @@ The above output includes only software that is visible in the container (i.e.,
syft <image> --scope all-layers
```
#### Format conversion (experimental)
The ability to convert existing SBOMs means you can create SBOMs in different formats quickly, without the need to regenerate the SBOM from scratch, which may take significantly more time.
```
syft convert <ORIGINAL-SBOM-FILE> -o <NEW-SBOM-FORMAT>[=<NEW-SBOM-FILE>]
```
This feature is experimental and data might be lost when converting formats. Packages are the main SBOM component easily transferable across formats, whereas files and relationships, as well as other information Syft doesn't support, are more likely to be lost.
We support formats with wide community usage AND good encode/decode support by Syft. The supported formats are:
- Syft JSON
- SPDX 2.2 JSON
- SPDX 2.2 tag-value
- CycloneDX 1.4 JSON
- CycloneDX 1.4 XML
Conversion example:
```sh
syft alpine:latest -o syft-json=sbom.syft.json # generate a syft SBOM
syft convert sbom.syft.json -o cyclonedx-json=sbom.cdx.json # convert it to CycloneDX
```
#### SBOM attestation
To generate an SBOM attestation for a container image:
### Keyless support
Syft supports generating attestations using cosign's [keyless](https://github.com/sigstore/cosign/blob/main/KEYLESS.md) signatures.
To use this feature with a format like CycloneDX json simply run:
```
syft attest --output cyclonedx-json <IMAGE WITH OCI WRITE ACCESS>
```
This command will open a web browser and allow the user to authenticate their OIDC identity as the root of trust for the attestation (Github, Google, Microsoft).
After authenticating, Syft will upload the attestation to the OCI registry specified by the image that the user has write access to.
You will need to make sure your credentials are configured for the OCI registry you are uploading to so that the attestation can write successfully.
Users can then verify the attestation(or any image with attestations) by running:
```
COSIGN_EXPERIMENTAL=1 cosign verify-attestation <IMAGE_WITH_ATTESTATIONS>
```
Users should see that the uploaded attestation claims are validated, the claims exist within the transparency log, and certificates on the attestations were verified against [fulcio](https://github.com/SigStore/fulcio).
There will also be a printout of the certificates subject `<user identity>` and the certificate issuer URL: `<provider of user identity (Github, Google, Microsoft)>`:
```
Certificate subject: test.email@testdomain.com
Certificate issuer URL: https://accounts.google.com
```
### Local private key support
To generate an SBOM attestation for a container image using a local private key:
```
syft attest --output [FORMAT] --key [KEY] [SOURCE] [flags]
```
@@ -160,6 +212,7 @@ Where the `formats` available are:
- `cyclonedx-json`: A JSON report conforming to the [CycloneDX 1.4 specification](https://cyclonedx.org/specification/overview/).
- `spdx-tag-value`: A tag-value formatted report conforming to the [SPDX 2.2 specification](https://spdx.github.io/spdx-spec/).
- `spdx-json`: A JSON report conforming to the [SPDX 2.2 JSON Schema](https://github.com/spdx/spdx-spec/blob/v2.2/schemas/spdx-schema.json).
- `github`: A JSON report conforming to GitHub's dependency snapshot format.
- `table`: A columnar summary (default).
#### Multiple outputs
+14 -4
View File
@@ -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"
)
@@ -49,14 +50,23 @@ func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions) *c
RunE: func(cmd *cobra.Command, args []string) error {
// this MUST be called first to make sure app config decodes
// the viper object correctly
newLogWrapper(app)
logApplicationConfig(app)
if app.CheckForAppUpdate {
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)
},
}
+166 -24
View File
@@ -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)
@@ -81,11 +98,12 @@ func Run(ctx context.Context, app *config.Application, args []string) error {
eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()
return eventloop.EventLoop(
execWorker(app, *si, format, predicateType, sv),
eventloop.SetupSignals(),
eventBus.Subscribe(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
@@ -144,7 +162,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 +171,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 +181,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 +212,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 +326,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
}
+4
View File
@@ -46,6 +46,7 @@ func New() (*cobra.Command, error) {
// root options are also passed to the attestCmd so that a user provided config location can be discovered
attestCmd := Attest(v, app, ro)
poweruserCmd := PowerUser(v, app, ro)
convertCmd := Convert(v, app, ro)
// rootCmd is currently an alias for the packages command
rootCmd := &cobra.Command{
@@ -84,6 +85,7 @@ func New() (*cobra.Command, error) {
// Add sub-commands.
rootCmd.AddCommand(packagesCmd)
rootCmd.AddCommand(attestCmd)
rootCmd.AddCommand(convertCmd)
rootCmd.AddCommand(poweruserCmd)
rootCmd.AddCommand(Completion())
rootCmd.AddCommand(Version(v, app))
@@ -123,6 +125,8 @@ func checkForApplicationUpdate() {
}
func logApplicationConfig(app *config.Application) {
versionInfo := version.FromBuild()
log.Infof("syft version: %+v", versionInfo.Version)
log.Debugf("application config:\n%+v", color.Magenta.Sprint(app.String()))
}
+47
View File
@@ -0,0 +1,47 @@
package cli
import (
"fmt"
"github.com/anchore/syft/cmd/syft/cli/convert"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/config"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
const (
convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout in table format, by default
{{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output goes to a file named img.cdx.json
`
)
func Convert(v *viper.Viper, app *config.Application, ro *options.RootOptions) *cobra.Command {
cmd := &cobra.Command{
Use: "convert [SOURCE-SBOM] -o [FORMAT]",
Short: "Convert between SBOM formats",
Long: "[Experimental] Convert SBOM files to, and from, SPDX, CycloneDX and Syft's format. For more info about data loss between formats see https://github.com/anchore/syft#format-conversion-experimental",
Example: internal.Tprintf(convertExample, map[string]interface{}{
"appName": internal.ApplicationName,
"command": "convert",
}),
Args: func(cmd *cobra.Command, args []string) error {
if err := app.LoadAllValues(v, ro.Config); err != nil {
return fmt.Errorf("invalid application config: %w", err)
}
newLogWrapper(app)
logApplicationConfig(app)
return validateArgs(cmd, args)
},
SilenceUsage: true,
SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error {
if app.CheckForAppUpdate {
checkForApplicationUpdate()
}
return convert.Run(cmd.Context(), app, args)
},
}
return cmd
}
+41
View File
@@ -0,0 +1,41 @@
package convert
import (
"context"
"fmt"
"os"
"github.com/anchore/syft/cmd/syft/cli/options"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft"
)
func Run(ctx context.Context, app *config.Application, args []string) error {
log.Warn("convert is an experimental feature, run `syft convert -h` for help")
writer, err := options.MakeWriter(app.Outputs, app.File)
if err != nil {
return err
}
defer func() {
if err := writer.Close(); err != nil {
log.Warnf("unable to write to report destination: %w", err)
}
}()
// this can only be a SBOM file
userInput := args[0]
f, err := os.Open(userInput)
if err != nil {
return fmt.Errorf("failed to open SBOM file: %w", err)
}
defer f.Close()
sbom, _, err := syft.Decode(f)
if err != nil {
return fmt.Errorf("failed to decode SBOM: %w", err)
}
return writer.Write(*sbom)
}
+52 -3
View File
@@ -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
View 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
View 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
View 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
}
+62
View File
@@ -0,0 +1,62 @@
package options
import (
"fmt"
"strings"
"github.com/anchore/syft/internal/formats/table"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/sbom"
"github.com/hashicorp/go-multierror"
)
// makeWriter creates a sbom.Writer for output or returns an error. this will either return a valid writer
// or an error but neither both and if there is no error, sbom.Writer.Close() should be called
func MakeWriter(outputs []string, defaultFile string) (sbom.Writer, error) {
outputOptions, err := parseOutputs(outputs, defaultFile)
if err != nil {
return nil, err
}
writer, err := sbom.NewWriter(outputOptions...)
if err != nil {
return nil, err
}
return writer, nil
}
// parseOptions utility to parse command-line option strings and retain the existing behavior of default format and file
func parseOutputs(outputs []string, defaultFile string) (out []sbom.WriterOption, errs error) {
// always should have one option -- we generally get the default of "table", but just make sure
if len(outputs) == 0 {
outputs = append(outputs, string(table.ID))
}
for _, name := range outputs {
name = strings.TrimSpace(name)
// split to at most two parts for <format>=<file>
parts := strings.SplitN(name, "=", 2)
// the format name is the first part
name = parts[0]
// default to the --file or empty string if not specified
file := defaultFile
// If a file is specified as part of the output formatName, use that
if len(parts) > 1 {
file = parts[1]
}
format := syft.FormatByName(name)
if format == nil {
errs = multierror.Append(errs, fmt.Errorf("bad output format: '%s'", name))
continue
}
out = append(out, sbom.NewWriterOption(format, file))
}
return out, errs
}
+34
View File
@@ -0,0 +1,34 @@
package options
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsSupportedFormat(t *testing.T) {
tests := []struct {
outputs []string
wantErr assert.ErrorAssertionFunc
}{
{
outputs: []string{"json"},
wantErr: assert.NoError,
},
{
outputs: []string{"table", "json"},
wantErr: assert.NoError,
},
{
outputs: []string{"unknown"},
wantErr: func(t assert.TestingT, err error, bla ...interface{}) bool {
return assert.ErrorContains(t, err, "bad output format: 'unknown'")
},
},
}
for _, tt := range tests {
_, err := MakeWriter(tt.outputs, "")
tt.wantErr(t, err)
}
}
+3 -56
View File
@@ -5,7 +5,6 @@ import (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/anchore/stereoscope"
"github.com/anchore/syft/cmd/syft/cli/eventloop"
@@ -14,7 +13,6 @@ import (
"github.com/anchore/syft/internal/anchore"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/formats/table"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/ui"
"github.com/anchore/syft/internal/version"
@@ -23,12 +21,11 @@ import (
"github.com/anchore/syft/syft/event"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
"github.com/hashicorp/go-multierror"
"github.com/wagoodman/go-partybus"
)
func Run(ctx context.Context, app *config.Application, args []string) error {
writer, err := makeWriter(app.Outputs, app.File)
writer, err := options.MakeWriter(app.Outputs, app.File)
if err != nil {
return err
}
@@ -49,11 +46,12 @@ func Run(ctx context.Context, app *config.Application, args []string) error {
eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()
return eventloop.EventLoop(
execWorker(app, *si, writer),
eventloop.SetupSignals(),
eventBus.Subscribe(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
@@ -187,54 +185,3 @@ func runPackageSbomUpload(src *source.Source, s sbom.SBOM, app *config.Applicati
return nil
}
// makeWriter creates a sbom.Writer for output or returns an error. this will either return a valid writer
// or an error but neither both and if there is no error, sbom.Writer.Close() should be called
func makeWriter(outputs []string, defaultFile string) (sbom.Writer, error) {
outputOptions, err := parseOptions(outputs, defaultFile)
if err != nil {
return nil, err
}
writer, err := sbom.NewWriter(outputOptions...)
if err != nil {
return nil, err
}
return writer, nil
}
// parseOptions utility to parse command-line option strings and retain the existing behavior of default format and file
func parseOptions(outputs []string, defaultFile string) (out []sbom.WriterOption, errs error) {
// always should have one option -- we generally get the default of "table", but just make sure
if len(outputs) == 0 {
outputs = append(outputs, string(table.ID))
}
for _, name := range outputs {
name = strings.TrimSpace(name)
// split to at most two parts for <format>=<file>
parts := strings.SplitN(name, "=", 2)
// the format name is the first part
name = parts[0]
// default to the --file or empty string if not specified
file := defaultFile
// If a file is specified as part of the output formatName, use that
if len(parts) > 1 {
file = parts[1]
}
format := syft.FormatByName(name)
if format == nil {
errs = multierror.Append(errs, fmt.Errorf("bad output format: '%s'", name))
continue
}
out = append(out, sbom.NewWriterOption(format, file))
}
return out, errs
}
+2 -1
View File
@@ -53,11 +53,12 @@ func Run(ctx context.Context, app *config.Application, args []string) error {
eventBus := partybus.NewBus()
stereoscope.SetBus(eventBus)
syft.SetBus(eventBus)
subscription := eventBus.Subscribe()
return eventloop.EventLoop(
execWorker(app, *si, writer),
eventloop.SetupSignals(),
eventBus.Subscribe(),
subscription,
stereoscope.Cleanup,
ui.Select(options.IsVerbose(app), app.Quiet)...,
)
+55 -55
View File
@@ -13,7 +13,7 @@ require (
github.com/anchore/go-rpmdb v0.0.0-20210914181456-a9c52348da63
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b
github.com/anchore/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7
github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270
github.com/antihax/optional v1.0.0
github.com/bmatcuk/doublestar/v4 v4.0.2
@@ -56,8 +56,10 @@ require (
require (
github.com/docker/docker v20.10.12+incompatible
github.com/google/go-containerregistry v0.8.1-0.20220209165246-a44adc326839
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
)
@@ -79,6 +81,7 @@ require (
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
github.com/Microsoft/go-winio v0.5.1 // indirect
github.com/PaesslerAG/gval v1.0.0 // indirect
github.com/PaesslerAG/jsonpath v0.1.1 // indirect
github.com/PuerkitoBio/purell v1.1.1 // indirect
@@ -114,17 +117,28 @@ require (
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490 // indirect
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect
github.com/containerd/containerd v1.5.10 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect
github.com/coreos/go-oidc/v3 v3.1.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20210823021906-dc406ceaf94b // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
github.com/docker/cli v20.10.12+incompatible // indirect
github.com/docker/distribution v2.8.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/envoyproxy/go-control-plane v0.10.1 // indirect
github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect
github.com/fatih/color v1.13.0 // indirect
github.com/form3tech-oss/jwt-go v3.2.5+incompatible // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/fullstorydev/grpcurl v1.8.2 // indirect
github.com/gabriel-vasile/mimetype v1.4.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-logr/logr v1.2.2 // indirect
@@ -142,11 +156,15 @@ require (
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.0 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/go-stack/stack v1.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.3.0 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/certificate-transparency-go v1.1.2 // indirect
github.com/google/go-github/v42 v42.0.0 // indirect
@@ -160,6 +178,7 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.2.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
@@ -173,19 +192,29 @@ require (
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.4.0 // indirect
github.com/hashicorp/golang-lru v0.5.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/vault/api v1.5.0 // indirect
github.com/hashicorp/vault/sdk v0.4.1 // indirect
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
github.com/imdario/mergo v0.3.12 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jedisct1/go-minisign v0.0.0-20210703085342-c1f07ee84431 // indirect
github.com/jhump/protoreflect v1.9.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.14.2 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/letsencrypt/boulder v0.0.0-20220331220046-b23ab962616e // indirect
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
@@ -193,10 +222,15 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/pierrec/lz4/v4 v4.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.12.1 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
@@ -209,18 +243,26 @@ 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/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.0.0 // indirect
github.com/stretchr/objx v0.3.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tent/canonical-json-go v0.0.0-20130607151641-96e4ba3a7613 // indirect
github.com/thales-e-security/pool v0.0.2 // indirect
github.com/theupdateframework/go-tuf v0.0.0-20220211205608-f0c3294f63b9 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/urfave/cli v1.22.5 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/xanzy/go-gitlab v0.62.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
github.com/zeebo/errs v1.2.2 // indirect
@@ -251,14 +293,24 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
go.uber.org/zap v1.21.0 // indirect
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect
golang.org/x/tools v0.1.10 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/api v0.74.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf // indirect
google.golang.org/grpc v1.45.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
k8s.io/api v0.23.5 // indirect
k8s.io/apimachinery v0.23.5 // indirect
k8s.io/client-go v0.23.5 // indirect
@@ -273,61 +325,9 @@ require (
)
require (
github.com/Microsoft/go-winio v0.5.1 // indirect
// we are hinting brotli to latest due to warning when installing archiver v3:
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
github.com/andybalholm/brotli v1.0.4 // indirect
github.com/containerd/containerd v1.5.10 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.10.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/docker/cli v20.10.12+incompatible // indirect
github.com/docker/distribution v2.8.0+incompatible // indirect
github.com/docker/docker-credential-helpers v0.6.4 // indirect
github.com/docker/go-connections v0.4.0 // indirect
github.com/docker/go-units v0.4.0 // indirect
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.0 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
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/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/klauspost/compress v1.14.2 // indirect
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
github.com/mattn/go-runewidth v0.0.13 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect
github.com/pierrec/lz4/v4 v4.1.2 // indirect
github.com/pkg/errors v0.9.1
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/stretchr/objx v0.3.0 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/ulikunitz/xz v0.5.10 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect
golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/genproto v0.0.0-20220405205423-9d709892a2bf // indirect
google.golang.org/grpc v1.45.0 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect
)
+4 -4
View File
@@ -272,8 +272,8 @@ github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0v
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b h1:YJWYt/6KQXR9JR46lLHrTTYi8rcye42tKcyjREA/hvA=
github.com/anchore/packageurl-go v0.1.1-0.20220314153042-1bcd40e5206b/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7 h1:kDrYkTSM9uIxaX/P9s0F4nKYNM+hnSgLJdLpqvsaQ/g=
github.com/anchore/packageurl-go v0.1.1-0.20220428202044-a072fa3cb6d7/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270 h1:NmxPDR6vo3xjwCL6o+tpF1vUad/BVo+WaVSwueB9W9w=
github.com/anchore/stereoscope v0.0.0-20220406160859-c03a18a6b270/go.mod h1:yoCLUZY0k/pYLNIy0L80p2Ko0PKVNXm8rHtgxp4OiSc=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
@@ -2415,8 +2415,8 @@ golang.org/x/crypto v0.0.0-20211202192323-5770296d904e/go.mod h1:IxCIyHEi3zRg3s0
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292 h1:f+lwQ+GtmgoY+A2YaQxlSOnDjXcQ7ZRLWOHbC6HtRqE=
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f h1:OeJjE6G4dgCY4PIXvIRQbE8+RX+uXZyGhUy/ksMGJoc=
golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+1 -1
View File
@@ -146,7 +146,7 @@ func (cfg *Application) parseLogLevelOption() error {
cfg.Verbosity = 1
}
default:
cfg.Log.LevelOpt = logrus.InfoLevel
cfg.Log.LevelOpt = logrus.WarnLevel
}
if cfg.Log.Level == "" {
+25 -7
View File
@@ -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")
}
+1 -1
View File
@@ -6,5 +6,5 @@ const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "3.2.2"
JSONSchemaVersion = "3.2.3"
)
@@ -22,7 +22,7 @@ func GetValidator(format cyclonedx.BOMFileFormat) sbom.Validator {
return err
}
// random JSON does not necessarily cause an error (e.g. SPDX)
if (cyclonedx.BOM{} == *bom) {
if (cyclonedx.BOM{} == *bom || bom.Components == nil) {
return fmt.Errorf("not a valid CycloneDX document")
}
return nil
@@ -139,7 +139,7 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc
for _, r := range relationships {
exists := isExpressiblePackageRelationship(r.Type)
if !exists {
log.Warnf("unable to convert relationship from CycloneDX 1.3 JSON, dropping: %+v", r)
log.Debugf("unable to convert relationship from CycloneDX 1.4 JSON, dropping: %+v", r)
continue
}
@@ -1,22 +1,21 @@
package spdxhelpers
import (
"fmt"
"path"
"strings"
"github.com/anchore/syft/syft/source"
)
func DocumentName(srcMetadata source.Metadata) (string, error) {
func DocumentName(srcMetadata source.Metadata) string {
switch srcMetadata.Scheme {
case source.ImageScheme:
return cleanName(srcMetadata.ImageMetadata.UserInput), nil
return cleanName(srcMetadata.ImageMetadata.UserInput)
case source.DirectoryScheme, source.FileScheme:
return cleanName(srcMetadata.Path), nil
return cleanName(srcMetadata.Path)
default:
return "unknown"
}
return "", fmt.Errorf("unable to determine document name from scheme=%q", srcMetadata.Scheme)
}
func cleanName(name string) string {
@@ -5,8 +5,6 @@ import (
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/source"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
@@ -59,8 +57,7 @@ func Test_DocumentName(t *testing.T) {
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual, err := DocumentName(test.srcMetadata)
require.NoError(t, err)
actual := DocumentName(test.srcMetadata)
assert.True(t, strings.HasPrefix(actual, test.expected), fmt.Sprintf("actual name %q", actual))
// track each scheme tested (passed or not)
@@ -10,12 +10,9 @@ import (
"github.com/google/uuid"
)
func DocumentNameAndNamespace(srcMetadata source.Metadata) (string, string, error) {
name, err := DocumentName(srcMetadata)
if err != nil {
return "", "", err
}
return name, DocumentNamespace(name, srcMetadata), nil
func DocumentNameAndNamespace(srcMetadata source.Metadata) (string, string) {
name := DocumentName(srcMetadata)
return name, DocumentNamespace(name, srcMetadata)
}
func DocumentNamespace(name string, srcMetadata source.Metadata) string {
@@ -17,6 +17,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from pubspec manifest"
case pkg.DebPkg:
answer = "acquired package info from DPKG DB"
case pkg.DotnetPkg:
answer = "acquired package info from dotnet project assets file"
case pkg.NpmPkg:
answer = "acquired package info from installed node module manifest file"
case pkg.PythonPkg:
@@ -134,6 +134,14 @@ func Test_SourceInfo(t *testing.T) {
"from pubspec manifest",
},
},
{
input: pkg.Package{
Type: pkg.DotnetPkg,
},
expected: []string{
"from dotnet project assets file",
},
},
}
var pkgTypes []pkg.Type
for _, test := range tests {
@@ -2,6 +2,7 @@ package spdxhelpers
import (
"errors"
"net/url"
"strconv"
"strings"
@@ -24,7 +25,13 @@ func ToSyftModel(doc *spdx.Document2_2) (*sbom.SBOM, error) {
spdxIDMap := make(map[string]interface{})
src := source.Metadata{Scheme: source.UnknownScheme}
if doc.CreationInfo != nil {
src.Scheme = extractSchemeFromNamespace(doc.CreationInfo.DocumentNamespace)
}
s := &sbom.SBOM{
Source: src,
Artifacts: sbom.Artifacts{
PackageCatalog: pkg.NewCatalog(),
FileMetadata: map[source.Coordinates]source.FileMetadata{},
@@ -42,6 +49,30 @@ func ToSyftModel(doc *spdx.Document2_2) (*sbom.SBOM, error) {
return s, nil
}
// NOTE(jonas): SPDX doesn't inform what an SBOM is about,
// image, directory, for example. This is our best effort to determine
// the scheme. Syft-generated SBOMs have in the namespace
// field a type encoded, which we try to identify here.
func extractSchemeFromNamespace(ns string) source.Scheme {
u, err := url.Parse(ns)
if err != nil {
return source.UnknownScheme
}
parts := strings.Split(u.Path, "/")
for _, p := range parts {
switch p {
case "file":
return source.FileScheme
case "image":
return source.ImageScheme
case "dir":
return source.DirectoryScheme
}
}
return source.UnknownScheme
}
func findLinuxReleaseByPURL(doc *spdx.Document2_2) *linux.Release {
for _, p := range doc.Packages {
purlValue := findPURLValue(p)
@@ -4,8 +4,10 @@ import (
"testing"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/source"
"github.com/spdx/tools-golang/spdx"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestToSyftModel(t *testing.T) {
@@ -195,3 +197,39 @@ func Test_extractMetadata(t *testing.T) {
})
}
}
func TestExtractSourceFromNamespaces(t *testing.T) {
tests := []struct {
namespace string
expected source.Scheme
}{
{
namespace: "https://anchore.com/syft/file/d42b01d0-7325-409b-b03f-74082935c4d3",
expected: source.FileScheme,
},
{
namespace: "https://anchore.com/syft/image/d42b01d0-7325-409b-b03f-74082935c4d3",
expected: source.ImageScheme,
},
{
namespace: "https://anchore.com/syft/dir/d42b01d0-7325-409b-b03f-74082935c4d3",
expected: source.DirectoryScheme,
},
{
namespace: "https://another-host/blob/123",
expected: source.UnknownScheme,
},
{
namespace: "bla bla",
expected: source.UnknownScheme,
},
{
namespace: "",
expected: source.UnknownScheme,
},
}
for _, tt := range tests {
require.Equal(t, tt.expected, extractSchemeFromNamespace(tt.namespace))
}
}
+1 -4
View File
@@ -8,10 +8,7 @@ import (
)
func encoder(output io.Writer, s sbom.SBOM) error {
doc, err := toFormatModel(s)
if err != nil {
return err
}
doc := toFormatModel(s)
enc := json.NewEncoder(output)
// prevent > and < from being escaped in the payload
@@ -20,11 +20,8 @@ import (
)
// toFormatModel creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results.
func toFormatModel(s sbom.SBOM) (*model.Document, error) {
name, namespace, err := spdxhelpers.DocumentNameAndNamespace(s.Source)
if err != nil {
return nil, err
}
func toFormatModel(s sbom.SBOM) *model.Document {
name, namespace := spdxhelpers.DocumentNameAndNamespace(s.Source)
return &model.Document{
Element: model.Element{
@@ -46,7 +43,7 @@ func toFormatModel(s sbom.SBOM) (*model.Document, error) {
Packages: toPackages(s.Artifacts.PackageCatalog, s.Relationships),
Files: toFiles(s),
Relationships: toRelationships(s.Relationships),
}, nil
}
}
func toPackages(catalog *pkg.Catalog, relationships []artifact.Relationship) []model.Package {
+1 -4
View File
@@ -8,9 +8,6 @@ import (
)
func encoder(output io.Writer, s sbom.SBOM) error {
model, err := toFormatModel(s)
if err != nil {
return err
}
model := toFormatModel(s)
return tvsaver.Save2_2(model, output)
}
@@ -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
@@ -16,11 +16,9 @@ import (
// toFormatModel creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results.
// nolint:funlen
func toFormatModel(s sbom.SBOM) (*spdx.Document2_2, error) {
name, namespace, err := spdxhelpers.DocumentNameAndNamespace(s.Source)
if err != nil {
return nil, err
}
func toFormatModel(s sbom.SBOM) *spdx.Document2_2 {
name, namespace := spdxhelpers.DocumentNameAndNamespace(s.Source)
return &spdx.Document2_2{
CreationInfo: &spdx.CreationInfo2_2{
// 2.1: SPDX Version; should be in the format "SPDX-2.2"
@@ -86,7 +84,7 @@ func toFormatModel(s sbom.SBOM) (*spdx.Document2_2, error) {
DocumentComment: "",
},
Packages: toFormatPackages(s.Artifacts.PackageCatalog),
}, nil
}
}
// packages populates all Package Information from the package Catalog (see https://spdx.github.io/spdx-spec/3-package-information/)
@@ -136,6 +136,12 @@ func (p *Package) UnmarshalJSON(b []byte) error {
return err
}
p.Metadata = payload
case pkg.DotnetDepsMetadataType:
var payload pkg.DotnetDepsMetadata
if err := json.Unmarshal(unpacker.Metadata, &payload); err != nil {
return err
}
p.Metadata = payload
default:
log.Warnf("unknown package metadata type=%q for packageID=%q", p.MetadataType, p.ID)
}
@@ -88,7 +88,7 @@
}
},
"schema": {
"version": "3.2.2",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json"
"version": "3.2.3",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.3.json"
}
}
@@ -184,7 +184,7 @@
}
},
"schema": {
"version": "3.2.2",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json"
"version": "3.2.3",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.3.json"
}
}
@@ -111,7 +111,7 @@
}
},
"schema": {
"version": "3.2.2",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.2.json"
"version": "3.2.3",
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-3.2.3.json"
}
}
+9 -2
View File
@@ -1,9 +1,9 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at 2022-02-07 09:34:56.622482 -0500 EST m=+0.351558414
// This file was generated by robots at 2022-05-09 10:01:37.625063 -0400 EDT m=+0.423632834
// using data from https://spdx.org/licenses/licenses.json
package spdxlicense
const Version = "3.16"
const Version = "3.17"
var licenseIDs = map[string]string{
"0bsd": "0BSD",
@@ -75,6 +75,9 @@ var licenseIDs = map[string]string{
"apsl-2": "APSL-2.0",
"apsl-2.0": "APSL-2.0",
"apsl-2.0.0": "APSL-2.0",
"arphic-1999": "Arphic-1999",
"arphic-1999.0": "Arphic-1999",
"arphic-1999.0.0": "Arphic-1999",
"artistic-1": "Artistic-1.0",
"artistic-1-cl8": "Artistic-1.0-cl8",
"artistic-1-perl": "Artistic-1.0-Perl",
@@ -87,9 +90,11 @@ var licenseIDs = map[string]string{
"artistic-2": "Artistic-2.0",
"artistic-2.0": "Artistic-2.0",
"artistic-2.0.0": "Artistic-2.0",
"baekmuk": "Baekmuk",
"bahyph": "Bahyph",
"barr": "Barr",
"beerware": "Beerware",
"bitstream-vera": "Bitstream-Vera",
"bittorrent-1": "BitTorrent-1.0",
"bittorrent-1.0": "BitTorrent-1.0",
"bittorrent-1.0.0": "BitTorrent-1.0",
@@ -609,6 +614,7 @@ var licenseIDs = map[string]string{
"jasper-2.0.0": "JasPer-2.0",
"jpnic": "JPNIC",
"json": "JSON",
"kicad-libraries-exception": "KiCad-libraries-exception",
"lal-1": "LAL-1.2",
"lal-1.2": "LAL-1.2",
"lal-1.2.0": "LAL-1.2",
@@ -710,6 +716,7 @@ var licenseIDs = map[string]string{
"mpl-2.0-no-copyleft-exception": "MPL-2.0-no-copyleft-exception",
"mpl-2.0.0": "MPL-2.0",
"mpl-2.0.0-no-copyleft-exception": "MPL-2.0-no-copyleft-exception",
"mplus": "mplus",
"ms-pl": "MS-PL",
"ms-rl": "MS-RL",
"mtll": "MTLL",
+1
View File
@@ -38,6 +38,7 @@ type artifactMetadataContainer struct {
Go pkg.GolangBinMetadata
Php pkg.PhpComposerJSONMetadata
Dart pkg.DartPubMetadata
Dotnet pkg.DotnetDepsMetadata
}
func main() {
File diff suppressed because it is too large Load Diff
+3
View File
@@ -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"
)
+13
View File
@@ -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
}
+1 -1
View File
@@ -74,7 +74,7 @@ func FormatByName(name string) sbom.Format {
return FormatByID(cyclonedxxml.ID)
case "cyclonedxjson":
return FormatByID(cyclonedxjson.ID)
case "github":
case "github", "githubjson":
return FormatByID(github.ID)
case "spdx", "spdxtv", "spdxtagvalue":
return FormatByID(spdx22tagvalue.ID)
+12
View File
@@ -8,6 +8,7 @@ import (
"github.com/anchore/syft/internal/formats/cyclonedxjson"
"github.com/anchore/syft/internal/formats/cyclonedxxml"
"github.com/anchore/syft/internal/formats/github"
"github.com/anchore/syft/internal/formats/spdx22json"
"github.com/anchore/syft/internal/formats/spdx22tagvalue"
"github.com/anchore/syft/internal/formats/syftjson"
@@ -169,6 +170,17 @@ func TestFormatByName(t *testing.T) {
name: "syft-json",
want: syftjson.ID,
},
// GitHub JSON
{
name: "github",
want: github.ID,
},
{
name: "github-json",
want: github.ID,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+4
View File
@@ -11,6 +11,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/apkdb"
"github.com/anchore/syft/syft/pkg/cataloger/dart"
"github.com/anchore/syft/syft/pkg/cataloger/deb"
"github.com/anchore/syft/syft/pkg/cataloger/dotnet"
"github.com/anchore/syft/syft/pkg/cataloger/golang"
"github.com/anchore/syft/syft/pkg/cataloger/java"
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
@@ -44,6 +45,7 @@ func ImageCatalogers(cfg Config) []Cataloger {
java.NewJavaCataloger(cfg.Java()),
apkdb.NewApkdbCataloger(),
golang.NewGoModuleBinaryCataloger(),
dotnet.NewDotnetDepsCataloger(),
}
}
@@ -63,6 +65,7 @@ func DirectoryCatalogers(cfg Config) []Cataloger {
golang.NewGoModFileCataloger(),
rust.NewCargoLockCataloger(),
dart.NewPubspecLockCataloger(),
dotnet.NewDotnetDepsCataloger(),
}
}
@@ -83,5 +86,6 @@ func AllCatalogers(cfg Config) []Cataloger {
golang.NewGoModFileCataloger(),
rust.NewCargoLockCataloger(),
dart.NewPubspecLockCataloger(),
dotnet.NewDotnetDepsCataloger(),
}
}
+14
View File
@@ -0,0 +1,14 @@
package dotnet
import (
"github.com/anchore/syft/syft/pkg/cataloger/common"
)
// NewDotnetDepsCataloger returns a new Dotnet cataloger object base on deps json files.
func NewDotnetDepsCataloger() *common.GenericCataloger {
globParsers := map[string]common.ParserFn{
"**/*.deps.json": parseDotnetDeps,
}
return common.NewGenericCataloger(nil, globParsers, "dotnet-deps-cataloger")
}
@@ -0,0 +1,72 @@
package dotnet
import (
"encoding/json"
"fmt"
"io"
"strings"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/common"
)
// integrity check
var _ common.ParserFn = parseDotnetDeps
type dotnetDeps struct {
Libraries map[string]dotnetDepsLibrary `json:"libraries"`
}
type dotnetDepsLibrary struct {
Type string `json:"type"`
Path string `json:"path"`
Sha512 string `json:"sha512"`
HashPath string `json:"hashPath"`
}
func parseDotnetDeps(path string, reader io.Reader) ([]*pkg.Package, []artifact.Relationship, error) {
var packages []*pkg.Package
dec := json.NewDecoder(reader)
var p dotnetDeps
if err := dec.Decode(&p); err != nil {
return nil, nil, fmt.Errorf("failed to parse deps.json file: %w", err)
}
for nameVersion, lib := range p.Libraries {
dotnetPkg := newDotnetDepsPackage(nameVersion, lib)
if dotnetPkg != nil {
packages = append(packages, dotnetPkg)
}
}
return packages, nil, nil
}
func newDotnetDepsPackage(nameVersion string, lib dotnetDepsLibrary) *pkg.Package {
if lib.Type != "package" {
return nil
}
splitted := strings.Split(nameVersion, "/")
name := splitted[0]
version := splitted[1]
return &pkg.Package{
Name: name,
Version: version,
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: &pkg.DotnetDepsMetadata{
Name: name,
Version: version,
Path: lib.Path,
Sha512: lib.Sha512,
HashPath: lib.HashPath,
},
}
}
@@ -0,0 +1,199 @@
package dotnet
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/pkg"
)
func assertPackagesEqual(t *testing.T, actual []*pkg.Package, expected map[string]*pkg.Package) {
assert.Len(t, actual, len(expected))
}
func TestParseDotnetDeps(t *testing.T) {
expected := map[string]*pkg.Package{
"AWSSDK.Core": {
Name: "AWSSDK.Core",
Version: "3.7.10.6",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "AWSSDK.Core",
Version: "3.7.10.6",
Sha512: "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==",
Path: "awssdk.core/3.7.10.6",
HashPath: "awssdk.core.3.7.10.6.nupkg.sha512",
},
},
"Microsoft.Extensions.DependencyInjection": {
Name: "Microsoft.Extensions.DependencyInjection",
Version: "6.0.0",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.DependencyInjection",
Version: "6.0.0",
Sha512: "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==",
Path: "microsoft.extensions.dependencyinjection/6.0.0",
HashPath: "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512",
},
},
"Microsoft.Extensions.DependencyInjection.Abstractions": {
Name: "Microsoft.Extensions.DependencyInjection.Abstractions",
Version: "6.0.0",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.DependencyInjection",
Version: "6.0.0",
Sha512: "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==",
Path: "microsoft.extensions.dependencyinjection.abstractions/6.0.0",
HashPath: "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512",
},
},
"Microsoft.Extensions.Logging": {
Name: "Microsoft.Extensions.Logging",
Version: "6.0.0",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Logging",
Version: "6.0.0",
Sha512: "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==",
Path: "microsoft.extensions.logging/6.0.0",
HashPath: "microsoft.extensions.logging.6.0.0.nupkg.sha512",
},
},
"Microsoft.Extensions.Logging.Abstractions": {
Name: "Microsoft.Extensions.Logging.Abstractions",
Version: "6.0.0",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Logging",
Version: "6.0.0",
Sha512: "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==",
Path: "microsoft.extensions.logging.abstractions/6.0.0",
HashPath: "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512",
},
},
"Microsoft.Extensions.Options": {
Name: "Microsoft.Extensions.Options",
Version: "6.0.0",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Options",
Version: "6.0.0",
Sha512: "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==",
Path: "microsoft.extensions.options/6.0.0",
HashPath: "microsoft.extensions.options.6.0.0.nupkg.sha512",
},
},
"Microsoft.Extensions.Primitives": {
Name: "Microsoft.Extensions.Primitives",
Version: "6.0.0",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Microsoft.Extensions.Primitives",
Version: "6.0.0",
Sha512: "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==",
Path: "microsoft.extensions.primitives/6.0.0",
HashPath: "microsoft.extensions.primitives.6.0.0.nupkg.sha512",
},
},
"Newtonsoft.Json": {
Name: "Newtonsoft.Json",
Version: "13.0.1",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Newtonsoft.Json",
Version: "13.0.1",
Sha512: "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
Path: "newtonsoft.json/13.0.1",
HashPath: "newtonsoft.json.13.0.1.nupkg.sha512",
},
},
"Serilog": {
Name: "Serilog",
Version: "2.10.0",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Serilog",
Version: "2.10.0",
Sha512: "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==",
Path: "serilog/2.10.0",
HashPath: "serilog.2.10.0.nupkg.sha512",
},
},
"Serilog.Sinks.Console": {
Name: "Serilog.Sinks.Console",
Version: "4.0.1",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "Serilog.Sinks.Console",
Version: "4.0.1",
Sha512: "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==",
Path: "serilog.sinks.console/4.0.1",
HashPath: "serilog.sinks.console.4.0.1.nupkg.sha512",
},
},
"System.Diagnostics.DiagnosticSource": {
Name: "System.Diagnostics.DiagnosticSource",
Version: "6.0.0",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "System.Diagnostics.DiagnosticSource",
Version: "6.0.0",
Sha512: "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==",
Path: "system.diagnostics.diagnosticsource/6.0.0",
HashPath: "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512",
},
},
"System.Runtime.CompilerServices.Unsafe": {
Name: "System.Runtime.CompilerServices.Unsafe",
Version: "6.0.0",
Language: pkg.Dotnet,
Type: pkg.DotnetPkg,
MetadataType: pkg.DotnetDepsMetadataType,
Metadata: pkg.DotnetDepsMetadata{
Name: "System.Runtime.CompilerServices.Unsafe",
Version: "6.0.0",
Sha512: "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
Path: "system.runtime.compilerservices.unsafe/6.0.0",
HashPath: "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512",
},
},
}
fixture, err := os.Open("test-fixtures/TestLibrary.deps.json")
if err != nil {
t.Fatalf("failed to open fixture: %+v", err)
}
actual, _, err := parseDotnetDeps(fixture.Name(), fixture)
if err != nil {
t.Fatalf("failed to parse deps.json: %+v", err)
}
assertPackagesEqual(t, actual, expected)
}
@@ -0,0 +1,235 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v6.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v6.0": {
"TestLibrary/1.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "6.0.0",
"Microsoft.Extensions.Logging": "6.0.0",
"Newtonsoft.Json": "13.0.1",
"Serilog": "2.10.0",
"Serilog.Sinks.Console": "4.0.1",
"TestCommon": "1.0.0"
},
"runtime": {
"TestLibrary.dll": {}
}
},
"AWSSDK.Core/3.7.10.6": {
"runtime": {
"lib/netcoreapp3.1/AWSSDK.Core.dll": {
"assemblyVersion": "3.3.0.0",
"fileVersion": "3.7.10.6"
}
}
},
"Microsoft.Extensions.DependencyInjection/6.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
},
"runtime": {
"lib/net6.0/Microsoft.Extensions.DependencyInjection.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0": {
"runtime": {
"lib/net6.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.Logging/6.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "6.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"Microsoft.Extensions.Logging.Abstractions": "6.0.0",
"Microsoft.Extensions.Options": "6.0.0",
"System.Diagnostics.DiagnosticSource": "6.0.0"
},
"runtime": {
"lib/netstandard2.1/Microsoft.Extensions.Logging.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.Logging.Abstractions/6.0.0": {
"runtime": {
"lib/net6.0/Microsoft.Extensions.Logging.Abstractions.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.Options/6.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"Microsoft.Extensions.Primitives": "6.0.0"
},
"runtime": {
"lib/netstandard2.1/Microsoft.Extensions.Options.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.Primitives/6.0.0": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
},
"runtime": {
"lib/net6.0/Microsoft.Extensions.Primitives.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"Serilog/2.10.0": {
"runtime": {
"lib/netstandard2.1/Serilog.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.10.0.0"
}
}
},
"Serilog.Sinks.Console/4.0.1": {
"dependencies": {
"Serilog": "2.10.0"
},
"runtime": {
"lib/net5.0/Serilog.Sinks.Console.dll": {
"assemblyVersion": "4.0.1.0",
"fileVersion": "4.0.1.0"
}
}
},
"System.Diagnostics.DiagnosticSource/6.0.0": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {},
"TestCommon/1.0.0": {
"dependencies": {
"AWSSDK.Core": "3.7.10.6"
},
"runtime": {
"TestCommon.dll": {}
}
}
}
},
"libraries": {
"TestLibrary/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"AWSSDK.Core/3.7.10.6": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==",
"path": "awssdk.core/3.7.10.6",
"hashPath": "awssdk.core.3.7.10.6.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==",
"path": "microsoft.extensions.dependencyinjection/6.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==",
"path": "microsoft.extensions.dependencyinjection.abstractions/6.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Logging/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==",
"path": "microsoft.extensions.logging/6.0.0",
"hashPath": "microsoft.extensions.logging.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Logging.Abstractions/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==",
"path": "microsoft.extensions.logging.abstractions/6.0.0",
"hashPath": "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Options/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==",
"path": "microsoft.extensions.options/6.0.0",
"hashPath": "microsoft.extensions.options.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Primitives/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==",
"path": "microsoft.extensions.primitives/6.0.0",
"hashPath": "microsoft.extensions.primitives.6.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"Serilog/2.10.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==",
"path": "serilog/2.10.0",
"hashPath": "serilog.2.10.0.nupkg.sha512"
},
"Serilog.Sinks.Console/4.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==",
"path": "serilog.sinks.console/4.0.1",
"hashPath": "serilog.sinks.console.4.0.1.nupkg.sha512"
},
"System.Diagnostics.DiagnosticSource/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==",
"path": "system.diagnostics.diagnosticsource/6.0.0",
"hashPath": "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512"
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
"path": "system.runtime.compilerservices.unsafe/6.0.0",
"hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
},
"TestCommon/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
@@ -29,7 +29,6 @@ var (
func makeGoMainPackage(mod *debug.BuildInfo, arch string, location source.Location) pkg.Package {
gbs := getBuildSettings(mod.Settings)
main := newGoBinaryPackage(&mod.Main, mod.GoVersion, arch, location, gbs)
main.Version = ""
if v, ok := gbs["vcs.revision"]; ok {
main.Version = v
@@ -133,6 +133,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
FoundBy: catalogerName,
Language: pkg.Go,
Type: pkg.GoModulePkg,
Version: "(devel)",
Locations: source.NewLocationSet(
source.Location{
Coordinates: source.Coordinates{
@@ -212,7 +213,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
arch: archDetails,
mod: &debug.BuildInfo{
GoVersion: goCompiledVersion,
Main: debug.Module{Path: "github.com/anchore/syft"},
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
Settings: []debug.BuildSetting{
{Key: "GOARCH", Value: archDetails},
{Key: "GOOS", Value: "darwin"},
@@ -226,7 +227,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
arch: archDetails,
mod: &debug.BuildInfo{
GoVersion: goCompiledVersion,
Main: debug.Module{Path: "github.com/anchore/syft"},
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
Settings: []debug.BuildSetting{
{Key: "GOARCH", Value: archDetails},
{Key: "GOOS", Value: "darwin"},
@@ -296,7 +297,7 @@ func TestBuildGoPkgInfo(t *testing.T) {
arch: archDetails,
mod: &debug.BuildInfo{
GoVersion: goCompiledVersion,
Main: debug.Module{Path: "github.com/anchore/syft"},
Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"},
Settings: []debug.BuildSetting{
{Key: "GOARCH", Value: archDetails},
{Key: "GOOS", Value: "darwin"},
+5 -1
View File
@@ -41,7 +41,11 @@ func scanFile(reader unionReader, filename string) ([]*debug.BuildInfo, []string
}
// in this case we could not read the or parse the file, but not explicitly because it is not a
// go-compiled binary (though it still might be).
log.Warnf("golang cataloger: failed to read buildinfo (file=%q): %v", filename, err)
// TODO: We should change this back to "warn" eventually.
// But right now it's catching too many cases where the reader IS NOT a Go binary at all.
// It'd be great to see how we can get those cases to be detected and handled above before we get to
// this point in execution.
log.Infof("golang cataloger: unable to read buildinfo (file=%q): %v", filename, err)
return nil, nil
}
+27
View File
@@ -0,0 +1,27 @@
package pkg
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/linux"
)
type DotnetDepsMetadata struct {
Name string `mapstructure:"name" json:"name"`
Version string `mapstructure:"version" json:"version"`
Path string `mapstructure:"path" json:"path"`
Sha512 string `mapstructure:"sha512" json:"sha512"`
HashPath string `mapstructure:"hashPath" json:"hashPath"`
}
func (m DotnetDepsMetadata) PackageURL(_ *linux.Release) string {
var qualifiers packageurl.Qualifiers
return packageurl.NewPackageURL(
packageurl.TypeDotnet,
"",
m.Name,
m.Version,
qualifiers,
"",
).ToString()
}
+4
View File
@@ -20,6 +20,7 @@ const (
Go Language = "go"
Rust Language = "rust"
Dart Language = "dart"
Dotnet Language = "dotnet"
)
// AllLanguages is a set of all programming languages detected by syft.
@@ -32,6 +33,7 @@ var AllLanguages = []Language{
Go,
Rust,
Dart,
Dotnet,
}
// String returns the string representation of the language.
@@ -66,6 +68,8 @@ func LanguageByName(name string) Language {
return Rust
case packageurl.TypePub, string(Dart):
return Dart
case packageurl.TypeDotnet:
return Dotnet
default:
return UnknownLanguage
}
+4
View File
@@ -34,6 +34,10 @@ func TestLanguageFromPURL(t *testing.T) {
purl: "pkg:pub/util@1.2.34",
want: Dart,
},
{
purl: "pkg:dotnet/Microsoft.CodeAnalysis.Razor@2.2.0",
want: Dotnet,
},
{
purl: "pkg:cargo/clap@2.33.0",
want: Rust,
+3
View File
@@ -18,6 +18,7 @@ const (
NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata"
RpmdbMetadataType MetadataType = "RpmdbMetadata"
DartPubMetadataType MetadataType = "DartPubMetadata"
DotnetDepsMetadataType MetadataType = "DotnetDepsMetadata"
PythonPackageMetadataType MetadataType = "PythonPackageMetadata"
RustCargoPackageMetadataType MetadataType = "RustCargoPackageMetadata"
KbPackageMetadataType MetadataType = "KbPackageMetadata"
@@ -33,6 +34,7 @@ var AllMetadataTypes = []MetadataType{
NpmPackageJSONMetadataType,
RpmdbMetadataType,
DartPubMetadataType,
DotnetDepsMetadataType,
PythonPackageMetadataType,
RustCargoPackageMetadataType,
KbPackageMetadataType,
@@ -48,6 +50,7 @@ var MetadataTypeByName = map[MetadataType]reflect.Type{
NpmPackageJSONMetadataType: reflect.TypeOf(NpmPackageJSONMetadata{}),
RpmdbMetadataType: reflect.TypeOf(RpmdbMetadata{}),
DartPubMetadataType: reflect.TypeOf(DartPubMetadata{}),
DotnetDepsMetadataType: reflect.TypeOf(DotnetDepsMetadata{}),
PythonPackageMetadataType: reflect.TypeOf(PythonPackageMetadata{}),
RustCargoPackageMetadataType: reflect.TypeOf(CargoMetadata{}),
KbPackageMetadataType: reflect.TypeOf(KbPackageMetadata{}),
+6
View File
@@ -21,6 +21,7 @@ const (
RustPkg Type = "rust-crate"
KbPkg Type = "msrc-kb"
DartPubPkg Type = "dart-pub"
DotnetPkg Type = "dotnet"
)
// AllPkgs represents all supported package types
@@ -38,6 +39,7 @@ var AllPkgs = []Type{
RustPkg,
KbPkg,
DartPubPkg,
DotnetPkg,
}
// PackageURLType returns the PURL package type for the current package.
@@ -65,6 +67,8 @@ func (t Type) PackageURLType() string {
return "cargo"
case DartPubPkg:
return packageurl.TypePub
case DotnetPkg:
return packageurl.TypeDotnet
default:
// TODO: should this be a "generic" purl type instead?
return ""
@@ -104,6 +108,8 @@ func TypeByName(name string) Type {
return RustPkg
case packageurl.TypePub:
return DartPubPkg
case packageurl.TypeDotnet:
return DotnetPkg
default:
return UnknownPkg
}
+5
View File
@@ -51,6 +51,11 @@ func TestTypeFromPURL(t *testing.T) {
purl: "pkg:pub/util@1.2.34?hosted_url=pub.hosted.org",
expected: DartPubPkg,
},
{
purl: "pkg:dotnet/Microsoft.CodeAnalysis.Razor@2.2.0",
expected: DotnetPkg,
},
{
purl: "pkg:composer/laravel/laravel@5.5.0",
expected: PhpComposerPkg,
+14
View File
@@ -39,6 +39,20 @@ func TestPackageURL(t *testing.T) {
},
expected: "pkg:pub/name@0.2.0?hosted_url=pub.hosted.org",
},
{
name: "dotnet",
pkg: Package{
Name: "Microsoft.CodeAnalysis.Razor",
Version: "2.2.0",
Type: DotnetPkg,
Metadata: DotnetDepsMetadata{
Name: "Microsoft.CodeAnalysis.Razor",
Version: "2.2.0",
},
},
expected: "pkg:dotnet/Microsoft.CodeAnalysis.Razor@2.2.0",
},
{
name: "python",
pkg: Package{
+1 -1
View File
@@ -338,7 +338,7 @@ func (r directoryResolver) FilesByPath(userPaths ...string) ([]Location, error)
// we should be resolving symlinks and preserving this information as a VirtualPath to the real file
evaluatedPath, err := filepath.EvalSymlinks(userStrPath)
if err != nil {
log.Warnf("directory resolver unable to evaluate symlink for path=%q : %+v", userPath, err)
log.Debugf("directory resolver unable to evaluate symlink for path=%q : %+v", userPath, err)
continue
}
+2 -2
View File
@@ -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,
},
+4 -2
View File
@@ -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",
+1 -1
View File
@@ -96,7 +96,7 @@ func TestPackagesCmdFlags(t *testing.T) {
name: "squashed-scope-flag",
args: []string{"packages", "-o", "json", "-s", "squashed", coverageImage},
assertions: []traitAssertion{
assertPackageCount(20),
assertPackageCount(32),
assertSuccessfulReturnCode,
},
},
+25 -24
View File
@@ -6,6 +6,7 @@ import (
)
func TestPowerUserCmdFlags(t *testing.T) {
secretsFixture := getFixtureImage(t, "image-secrets")
tests := []struct {
name string
args []string
@@ -32,30 +33,9 @@ func TestPowerUserCmdFlags(t *testing.T) {
assertSuccessfulReturnCode,
},
},
{
name: "defaut-secrets-results-w-reveal-values",
env: map[string]string{
"SYFT_SECRETS_REVEAL_VALUES": "true",
},
args: []string{"power-user", "docker-archive:" + getFixtureImage(t, "image-secrets")},
assertions: []traitAssertion{
assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something
assertInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key
assertSuccessfulReturnCode,
},
},
{
name: "default-secret-results-dont-reveal-values",
args: []string{"power-user", "docker-archive:" + getFixtureImage(t, "image-secrets")},
assertions: []traitAssertion{
assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something
assertNotInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key
assertSuccessfulReturnCode,
},
},
{
name: "content-cataloger-wired-up",
args: []string{"power-user", "docker-archive:" + getFixtureImage(t, "image-secrets")},
args: []string{"power-user", "docker-archive:" + secretsFixture},
env: map[string]string{
"SYFT_FILE_CONTENTS_GLOBS": "/api-key.txt",
},
@@ -75,12 +55,33 @@ func TestPowerUserCmdFlags(t *testing.T) {
assertSuccessfulReturnCode,
},
},
{
name: "default-secrets-results-w-reveal-values",
env: map[string]string{
"SYFT_SECRETS_REVEAL_VALUES": "true",
},
args: []string{"power-user", "docker-archive:" + secretsFixture},
assertions: []traitAssertion{
assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something
assertInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key
assertSuccessfulReturnCode,
},
},
{
name: "default-secret-results-dont-reveal-values",
args: []string{"power-user", "docker-archive:" + secretsFixture},
assertions: []traitAssertion{
assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something
assertNotInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key
assertSuccessfulReturnCode,
},
},
{
name: "default-secrets-dir-results-w-reveal-values",
env: map[string]string{
"SYFT_SECRETS_REVEAL_VALUES": "true",
},
args: []string{"power-user", "dir:test-fixtures/image-secrets"},
args: []string{"power-user", "dir:test-fixtures/image-secrets-dir"},
assertions: []traitAssertion{
assertInOutput(`"classification": "generic-api-key"`), // proof of the secrets cataloger finding something
assertInOutput(`"12345A7a901b345678901234567890123456789012345678901234567890"`), // proof of the secrets cataloger finding the api key
@@ -91,7 +92,7 @@ func TestPowerUserCmdFlags(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cmd, stdout, stderr := runSyft(t, test.env, test.args...)
cmd, stdout, stderr := runSyftSafe(t, test.env, test.args...)
for _, traitFn := range test.assertions {
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
}
@@ -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
@@ -0,0 +1,9 @@
{
"OIDCIssuers": {
"http://dex-idp:8888/auth": {
"IssuerURL": "http://dex-idp:8888/auth",
"ClientID": "fulcio",
"Type": "email"
}
}
}
@@ -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
@@ -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
@@ -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: {}
@@ -0,0 +1,2 @@
FROM scratch
ADD api-key.txt .
@@ -0,0 +1 @@
some_ApI-kEy = "12345A7a901b345678901234567890123456789012345678901234567890"
+2 -2
View File
@@ -152,7 +152,7 @@ func assertVerifyAttestation(coverageImage string) traitAssertion {
coverageImage, // TODO which remote image to use?
)
stdout, stderr := runCommand(attachCmd, nil)
stdout, stderr, _ := runCommand(attachCmd, nil)
if attachCmd.ProcessState.ExitCode() != 0 {
tb.Log("STDOUT", stdout)
tb.Log("STDERR", stderr)
@@ -165,7 +165,7 @@ func assertVerifyAttestation(coverageImage string) traitAssertion {
coverageImage, // TODO which remote image to use?
)
stdout, stderr = runCommand(verifyCmd, nil)
stdout, stderr, _ = runCommand(verifyCmd, nil)
if attachCmd.ProcessState.ExitCode() != 0 {
tb.Log("STDOUT", stdout)
tb.Log("STDERR", stderr)
+65 -8
View File
@@ -10,6 +10,7 @@ import (
"path/filepath"
"runtime"
"strings"
"syscall"
"testing"
"time"
@@ -24,7 +25,7 @@ func setupPKI(t *testing.T, pw string) func() {
cosignPath := filepath.Join(repoRoot(t), ".tmp/cosign")
cmd := exec.Command(cosignPath, "generate-key-pair")
stdout, stderr := runCommand(cmd, nil)
stdout, stderr, _ := runCommand(cmd, nil)
if cmd.ProcessState.ExitCode() != 0 {
t.Log("STDOUT", stdout)
t.Log("STDERR", stderr)
@@ -57,7 +58,7 @@ func getFixtureImage(t testing.TB, fixtureImageName string) string {
func pullDockerImage(t testing.TB, image string) {
cmd := exec.Command("docker", "pull", image)
stdout, stderr := runCommand(cmd, nil)
stdout, stderr, _ := runCommand(cmd, nil)
if cmd.ProcessState.ExitCode() != 0 {
t.Log("STDOUT", stdout)
t.Log("STDERR", stderr)
@@ -80,11 +81,24 @@ func runSyftInDocker(t testing.TB, env map[string]string, image string, args ...
args...,
)
cmd := exec.Command("docker", allArgs...)
stdout, stderr := runCommand(cmd, env)
stdout, stderr, _ := runCommand(cmd, env)
return cmd, stdout, stderr
}
func runSyft(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, string, string) {
return runSyftCommand(t, env, true, args...)
}
func runSyftSafe(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, string, string) {
return runSyftCommand(t, env, false, args...)
}
func runSyftCommand(t testing.TB, env map[string]string, expectError bool, args ...string) (*exec.Cmd, string, string) {
cancel := make(chan bool, 1)
defer func() {
cancel <- true
}()
cmd := getSyftCommand(t, args...)
if env == nil {
env = make(map[string]string)
@@ -93,7 +107,45 @@ func runSyft(t testing.TB, env map[string]string, args ...string) (*exec.Cmd, st
// we should not have tests reaching out for app update checks
env["SYFT_CHECK_FOR_APP_UPDATE"] = "false"
stdout, stderr := runCommand(cmd, env)
timeout := func() {
select {
case <-cancel:
return
case <-time.After(60 * time.Second):
}
if cmd != nil && cmd.Process != nil {
// get a stack trace printed
err := cmd.Process.Signal(syscall.SIGABRT)
if err != nil {
t.Errorf("error aborting: %+v", err)
}
}
}
go timeout()
stdout, stderr, err := runCommand(cmd, env)
if !expectError && err != nil && stdout == "" {
t.Errorf("error running syft: %+v", err)
t.Errorf("STDOUT: %s", stdout)
t.Errorf("STDERR: %s", stderr)
// this probably indicates a timeout
args = append(args, "-vv")
cmd = getSyftCommand(t, args...)
go timeout()
stdout, stderr, err = runCommand(cmd, env)
if err != nil {
t.Errorf("error rerunning syft: %+v", err)
t.Errorf("STDOUT: %s", stdout)
t.Errorf("STDERR: %s", stderr)
}
}
return cmd, stdout, stderr
}
@@ -103,7 +155,12 @@ func runCosign(t testing.TB, env map[string]string, args ...string) (*exec.Cmd,
env = make(map[string]string)
}
stdout, stderr := runCommand(cmd, env)
stdout, stderr, err := runCommand(cmd, env)
if err != nil {
t.Errorf("error running cosign: %+v", err)
}
return cmd, stdout, stderr
}
@@ -111,7 +168,7 @@ func getCosignCommand(t testing.TB, args ...string) *exec.Cmd {
return exec.Command(filepath.Join(repoRoot(t), ".tmp/cosign"), args...)
}
func runCommand(cmd *exec.Cmd, env map[string]string) (string, string) {
func runCommand(cmd *exec.Cmd, env map[string]string) (string, string, error) {
if env != nil {
cmd.Env = append(os.Environ(), envMapToSlice(env)...)
}
@@ -120,9 +177,9 @@ func runCommand(cmd *exec.Cmd, env map[string]string) (string, string) {
cmd.Stderr = &stderr
// ignore errors since this may be what the test expects
cmd.Run()
err := cmd.Run()
return stdout.String(), stderr.String()
return stdout.String(), stderr.String(), err
}
func envMapToSlice(env map[string]string) (envList []string) {
@@ -199,6 +199,25 @@ var dirOnlyTestCases = []testCase{
"analyzer": "0.40.7",
},
},
{
name: "find dotnet packages",
pkgType: pkg.DotnetPkg,
pkgLanguage: pkg.Dotnet,
pkgInfo: map[string]string{
"AWSSDK.Core": "3.7.10.6",
"Microsoft.Extensions.DependencyInjection": "6.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"Microsoft.Extensions.Logging": "6.0.0",
"Microsoft.Extensions.Logging.Abstractions": "6.0.0",
"Microsoft.Extensions.Options": "6.0.0",
"Microsoft.Extensions.Primitives": "6.0.0",
"Newtonsoft.Json": "13.0.1",
"Serilog": "2.10.0",
"Serilog.Sinks.Console": "4.0.1",
"System.Diagnostics.DiagnosticSource": "6.0.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0",
},
},
}
var commonTestCases = []testCase{
@@ -66,6 +66,7 @@ func TestPkgCoverageImage(t *testing.T) {
definedLanguages.Remove(pkg.Go.String())
definedLanguages.Remove(pkg.Rust.String())
definedLanguages.Remove(pkg.Dart.String())
definedLanguages.Remove(pkg.Dotnet.String())
observedPkgs := internal.NewStringSet()
definedPkgs := internal.NewStringSet()
@@ -78,6 +79,7 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Remove(string(pkg.GoModulePkg))
definedPkgs.Remove(string(pkg.RustPkg))
definedPkgs.Remove(string(pkg.DartPubPkg))
definedPkgs.Remove(string(pkg.DotnetPkg))
var cases []testCase
cases = append(cases, commonTestCases...)
+78
View File
@@ -0,0 +1,78 @@
package integration
import (
"context"
"io/ioutil"
"os"
"testing"
"github.com/anchore/syft/cmd/syft/cli/convert"
"github.com/anchore/syft/internal/config"
"github.com/anchore/syft/internal/formats/cyclonedxjson"
"github.com/anchore/syft/internal/formats/cyclonedxxml"
"github.com/anchore/syft/internal/formats/spdx22json"
"github.com/anchore/syft/internal/formats/spdx22tagvalue"
"github.com/anchore/syft/internal/formats/syftjson"
"github.com/anchore/syft/internal/formats/table"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
"github.com/stretchr/testify/require"
)
var convertibleFormats = []sbom.Format{
syftjson.Format(),
spdx22json.Format(),
spdx22tagvalue.Format(),
cyclonedxjson.Format(),
cyclonedxxml.Format(),
}
// TestConvertCmd tests if the converted SBOM is a valid document according
// to spec.
// TODO: This test can, but currently does not, check the converted SBOM content. It
// might be useful to do that in the future, once we gather a better understanding of
// what users expect from the convert command.
func TestConvertCmd(t *testing.T) {
for _, format := range convertibleFormats {
t.Run(format.ID().String(), func(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope)
format := syft.FormatByID(syftjson.ID)
f, err := ioutil.TempFile("", "test-convert-sbom-")
require.NoError(t, err)
defer func() {
err := f.Close()
require.NoError(t, err)
os.Remove(f.Name())
}()
err = format.Encode(f, sbom)
require.NoError(t, err)
stdr, stdw, err := os.Pipe()
require.NoError(t, err)
originalStdout := os.Stdout
os.Stdout = stdw
ctx := context.Background()
app := &config.Application{Outputs: []string{format.ID().String()}}
err = convert.Run(ctx, app, []string{f.Name()})
require.NoError(t, err)
stdw.Close()
out, err := ioutil.ReadAll(stdr)
require.NoError(t, err)
os.Stdout = originalStdout
formatFound := syft.IdentifyFormat(out)
if format.ID() == table.ID {
require.Nil(t, formatFound)
return
}
require.Equal(t, format.ID(), formatFound.ID())
})
}
}
@@ -0,0 +1,235 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v6.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v6.0": {
"TestLibrary/1.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "6.0.0",
"Microsoft.Extensions.Logging": "6.0.0",
"Newtonsoft.Json": "13.0.1",
"Serilog": "2.10.0",
"Serilog.Sinks.Console": "4.0.1",
"TestCommon": "1.0.0"
},
"runtime": {
"TestLibrary.dll": {}
}
},
"AWSSDK.Core/3.7.10.6": {
"runtime": {
"lib/netcoreapp3.1/AWSSDK.Core.dll": {
"assemblyVersion": "3.3.0.0",
"fileVersion": "3.7.10.6"
}
}
},
"Microsoft.Extensions.DependencyInjection/6.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
},
"runtime": {
"lib/net6.0/Microsoft.Extensions.DependencyInjection.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0": {
"runtime": {
"lib/net6.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.Logging/6.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection": "6.0.0",
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"Microsoft.Extensions.Logging.Abstractions": "6.0.0",
"Microsoft.Extensions.Options": "6.0.0",
"System.Diagnostics.DiagnosticSource": "6.0.0"
},
"runtime": {
"lib/netstandard2.1/Microsoft.Extensions.Logging.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.Logging.Abstractions/6.0.0": {
"runtime": {
"lib/net6.0/Microsoft.Extensions.Logging.Abstractions.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.Options/6.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "6.0.0",
"Microsoft.Extensions.Primitives": "6.0.0"
},
"runtime": {
"lib/netstandard2.1/Microsoft.Extensions.Options.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Microsoft.Extensions.Primitives/6.0.0": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
},
"runtime": {
"lib/net6.0/Microsoft.Extensions.Primitives.dll": {
"assemblyVersion": "6.0.0.0",
"fileVersion": "6.0.21.52210"
}
}
},
"Newtonsoft.Json/13.0.1": {
"runtime": {
"lib/netstandard2.0/Newtonsoft.Json.dll": {
"assemblyVersion": "13.0.0.0",
"fileVersion": "13.0.1.25517"
}
}
},
"Serilog/2.10.0": {
"runtime": {
"lib/netstandard2.1/Serilog.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.10.0.0"
}
}
},
"Serilog.Sinks.Console/4.0.1": {
"dependencies": {
"Serilog": "2.10.0"
},
"runtime": {
"lib/net5.0/Serilog.Sinks.Console.dll": {
"assemblyVersion": "4.0.1.0",
"fileVersion": "4.0.1.0"
}
}
},
"System.Diagnostics.DiagnosticSource/6.0.0": {
"dependencies": {
"System.Runtime.CompilerServices.Unsafe": "6.0.0"
}
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {},
"TestCommon/1.0.0": {
"dependencies": {
"AWSSDK.Core": "3.7.10.6"
},
"runtime": {
"TestCommon.dll": {}
}
}
}
},
"libraries": {
"TestLibrary/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"AWSSDK.Core/3.7.10.6": {
"type": "package",
"serviceable": true,
"sha512": "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==",
"path": "awssdk.core/3.7.10.6",
"hashPath": "awssdk.core.3.7.10.6.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==",
"path": "microsoft.extensions.dependencyinjection/6.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==",
"path": "microsoft.extensions.dependencyinjection.abstractions/6.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Logging/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==",
"path": "microsoft.extensions.logging/6.0.0",
"hashPath": "microsoft.extensions.logging.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Logging.Abstractions/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==",
"path": "microsoft.extensions.logging.abstractions/6.0.0",
"hashPath": "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Options/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==",
"path": "microsoft.extensions.options/6.0.0",
"hashPath": "microsoft.extensions.options.6.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Primitives/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==",
"path": "microsoft.extensions.primitives/6.0.0",
"hashPath": "microsoft.extensions.primitives.6.0.0.nupkg.sha512"
},
"Newtonsoft.Json/13.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==",
"path": "newtonsoft.json/13.0.1",
"hashPath": "newtonsoft.json.13.0.1.nupkg.sha512"
},
"Serilog/2.10.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==",
"path": "serilog/2.10.0",
"hashPath": "serilog.2.10.0.nupkg.sha512"
},
"Serilog.Sinks.Console/4.0.1": {
"type": "package",
"serviceable": true,
"sha512": "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==",
"path": "serilog.sinks.console/4.0.1",
"hashPath": "serilog.sinks.console.4.0.1.nupkg.sha512"
},
"System.Diagnostics.DiagnosticSource/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==",
"path": "system.diagnostics.diagnosticsource/6.0.0",
"hashPath": "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512"
},
"System.Runtime.CompilerServices.Unsafe/6.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==",
"path": "system.runtime.compilerservices.unsafe/6.0.0",
"hashPath": "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512"
},
"TestCommon/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
+43 -1
View File
@@ -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 {
+13 -1
View File
@@ -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)