mirror of
https://github.com/anchore/syft.git
synced 2026-10-18 00:57:22 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91e2fd8532 | ||
|
|
24f08e7738 | ||
|
|
a83506628c | ||
|
|
42f8601919 | ||
|
|
d2d532f4a8 | ||
|
|
1cea0ecd5c | ||
|
|
d2f053bc71 | ||
|
|
2fc344aba4 | ||
|
|
8b6c576d78 | ||
|
|
ab289933da | ||
|
|
37927b8b23 | ||
|
|
03d51c36d0 | ||
|
|
0bd3558fb2 | ||
|
|
4ce2edda9e |
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()))
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)...,
|
||||
)
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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 == "" {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
@@ -31,4 +31,7 @@ const (
|
||||
|
||||
// ImportStarted is a partybus event that occurs when an SBOM upload process has begun
|
||||
ImportStarted partybus.EventType = "syft-import-started-event"
|
||||
|
||||
// UploadAttestation is a partybus event that occurs when syft uploads an attestation to an OCI registry (+ any transparency log)
|
||||
UploadAttestation partybus.EventType = "syft-upload-attestation"
|
||||
)
|
||||
|
||||
@@ -151,3 +151,16 @@ func ParseImportStarted(e partybus.Event) (string, progress.StagedProgressable,
|
||||
|
||||
return host, prog, nil
|
||||
}
|
||||
|
||||
func ParseUploadAttestation(e partybus.Event) (progress.StagedProgressable, error) {
|
||||
if err := checkEventType(e.Type, event.UploadAttestation); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
prog, ok := e.Value.(progress.StagedProgressable)
|
||||
if !ok {
|
||||
return nil, newPayloadErr(e.Type, "Value", e.Value)
|
||||
}
|
||||
|
||||
return prog, nil
|
||||
}
|
||||
|
||||
+1
-1
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"},
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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{}),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ func TestAttestCmd(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "can encode syft.json as the predicate given a password",
|
||||
args: []string{"attest", "-o", "json", img},
|
||||
args: []string{"attest", "-o", "json", "--key", "cosign.key", img},
|
||||
assertions: []traitAssertion{
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
@@ -34,7 +34,7 @@ func TestAttestCmd(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "can encode syft.json as the predicate given a blank password",
|
||||
args: []string{"attest", "-o", "json", img},
|
||||
args: []string{"attest", "-o", "json", "--key", "cosign.key", img},
|
||||
assertions: []traitAssertion{
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
|
||||
@@ -59,9 +59,11 @@ func TestCosignWorkflow(t *testing.T) {
|
||||
"attest",
|
||||
"-o",
|
||||
"json",
|
||||
"--key",
|
||||
"cosign.key",
|
||||
img,
|
||||
},
|
||||
// cosign attach attestation --attestation image_latest_sbom_attestation.json caphill4/attest:latest
|
||||
// cosign attach attestation
|
||||
cosignAttachArgs: []string{
|
||||
"attach",
|
||||
"attestation",
|
||||
@@ -69,7 +71,7 @@ func TestCosignWorkflow(t *testing.T) {
|
||||
attestationFile,
|
||||
img,
|
||||
},
|
||||
// cosign verify-attestation -key cosign.pub caphill4/attest:latest
|
||||
// cosign verify-attestation
|
||||
cosignVerifyArgs: []string{
|
||||
"verify-attestation",
|
||||
"-key",
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
@@ -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
@@ -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...)
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user