mirror of
https://github.com/anchore/syft.git
synced 2026-10-12 14:17:20 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b06a2d4f27 | ||
|
|
a5dd485672 | ||
|
|
078dbedfb6 | ||
|
|
9f60d32369 | ||
|
|
a3dc0fa97d | ||
|
|
f999604a29 | ||
|
|
a49c7e3c53 | ||
|
|
b3ca75646c | ||
|
|
3308079158 | ||
|
|
1279bd0b08 | ||
|
|
f89f58b825 | ||
|
|
a70cfa6dd7 | ||
|
|
3323ce2b6b | ||
|
|
003d28ad48 | ||
|
|
9d9669e62f | ||
|
|
39737a2825 | ||
|
|
f2617285d0 | ||
|
|
07d3c9af52 | ||
|
|
5123f073c7 | ||
|
|
991af0d857 | ||
|
|
a86dd3704e | ||
|
|
4af32c5bee | ||
|
|
640099ce2e | ||
|
|
b2ab4671b9 | ||
|
|
ad322b3314 | ||
|
|
4a8a9ce290 | ||
|
|
635904fcb6 | ||
|
|
1e75cb0418 | ||
|
|
afc0c1acd9 | ||
|
|
edac8c7bf7 | ||
|
|
d2f28e0eb1 | ||
|
|
1d1a7de314 |
@@ -7,10 +7,10 @@ name: "CodeQL Security Scan"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [main]
|
||||
branches:
|
||||
# only run when there are pushes to the main branch (not on PRs)
|
||||
- main
|
||||
|
||||
schedule:
|
||||
- cron: '0 0 * * 3'
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@ name: "Validations"
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
|
||||
env:
|
||||
|
||||
@@ -19,7 +19,6 @@ CHANGELOG.md
|
||||
.tmp/
|
||||
coverage.txt
|
||||
bin/
|
||||
.goreleaser.yaml
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
dist: snapshot
|
||||
release:
|
||||
prerelease: auto
|
||||
draft: true
|
||||
|
||||
env:
|
||||
# required to support multi architecture docker builds
|
||||
- DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
release:
|
||||
prerelease: auto
|
||||
draft: true
|
||||
|
||||
before:
|
||||
hooks:
|
||||
@@ -94,3 +93,41 @@ brews:
|
||||
description: *description
|
||||
license: "Apache License 2.0"
|
||||
|
||||
dockers:
|
||||
- image_templates:
|
||||
- "anchore/syft:latest"
|
||||
- "anchore/syft:{{ .Tag }}-amd64"
|
||||
- "anchore/syft:v{{ .Major }}-amd64"
|
||||
- "anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64"
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- "--build-arg=BUILD_DATE={{.Date}}"
|
||||
- "--build-arg=BUILD_VERSION={{.Version}}"
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
- image_templates:
|
||||
- "anchore/syft:{{ .Tag }}-arm64v8"
|
||||
- "anchore/syft:v{{ .Major }}-arm64v8"
|
||||
- "anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8"
|
||||
goarch: arm64
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64/v8"
|
||||
- "--build-arg=BUILD_DATE={{.Date}}"
|
||||
- "--build-arg=BUILD_VERSION={{.Version}}"
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
docker_manifests:
|
||||
- name_template: anchore/syft:{{ .Tag }}
|
||||
image_templates:
|
||||
- anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64
|
||||
- anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8
|
||||
- name_template: anchore/syft:latest
|
||||
image_templates:
|
||||
- anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64
|
||||
- anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8
|
||||
@@ -1,24 +0,0 @@
|
||||
TARGET=../goreleaser.yaml
|
||||
|
||||
.PHONY: snapshot-config
|
||||
snapshot-config:
|
||||
cat snapshot.yaml > $(TARGET)
|
||||
cat env-skip-signing.yaml >> $(TARGET)
|
||||
cat main.yaml >> $(TARGET)
|
||||
cat docker.yaml >> $(TARGET)
|
||||
cat $(TARGET)
|
||||
|
||||
.PHONY: snapshot-with-signing-config
|
||||
snapshot-with-signing-config:
|
||||
# we leave out docker entirely such that this can easily be tested in CI
|
||||
cat snapshot.yaml > $(TARGET)
|
||||
cat env-default.yaml >> $(TARGET)
|
||||
cat main.yaml >> $(TARGET)
|
||||
cat $(TARGET)
|
||||
|
||||
.PHONY: release-config
|
||||
release-config:
|
||||
cat env-default.yaml > $(TARGET)
|
||||
cat docker.yaml >> $(TARGET)
|
||||
cat main.yaml >> $(TARGET)
|
||||
cat $(TARGET)
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
dockers:
|
||||
- image_templates:
|
||||
- "anchore/syft:latest"
|
||||
- "anchore/syft:{{ .Tag }}-amd64"
|
||||
- "anchore/syft:v{{ .Major }}-amd64"
|
||||
- "anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64"
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/amd64"
|
||||
- "--build-arg=BUILD_DATE={{.Date}}"
|
||||
- "--build-arg=BUILD_VERSION={{.Version}}"
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
- image_templates:
|
||||
- "anchore/syft:{{ .Tag }}-arm64v8"
|
||||
- "anchore/syft:v{{ .Major }}-arm64v8"
|
||||
- "anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8"
|
||||
goarch: arm64
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/arm64/v8"
|
||||
- "--build-arg=BUILD_DATE={{.Date}}"
|
||||
- "--build-arg=BUILD_VERSION={{.Version}}"
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
docker_manifests:
|
||||
- name_template: anchore/syft:{{ .Tag }}
|
||||
image_templates:
|
||||
- anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64
|
||||
- anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8
|
||||
- name_template: anchore/syft:latest
|
||||
image_templates:
|
||||
- anchore/syft:v{{ .Major }}.{{ .Minor }}-amd64
|
||||
- anchore/syft:v{{ .Major }}.{{ .Minor }}-arm64v8
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
env:
|
||||
# required to support multi architecture docker builds
|
||||
- DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
env:
|
||||
# required to support multi architecture docker builds
|
||||
- DOCKER_CLI_EXPERIMENTAL=enabled
|
||||
- SKIP_SIGNING=true
|
||||
@@ -1,91 +0,0 @@
|
||||
release:
|
||||
prerelease: auto
|
||||
draft: true
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- ./.github/scripts/apple-signing/setup.sh {{ .IsSnapshot }}
|
||||
|
||||
builds:
|
||||
- id: linux-build
|
||||
binary: syft
|
||||
goos:
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
# set the modified timestamp on the output binary to the git timestamp to ensure a reproducible build
|
||||
mod_timestamp: &build-timestamp '{{ .CommitTimestamp }}'
|
||||
env: &build-env
|
||||
- CGO_ENABLED=0
|
||||
ldflags: &build-ldflags |
|
||||
-w
|
||||
-s
|
||||
-extldflags '-static'
|
||||
-X github.com/anchore/syft/internal/version.version={{.Version}}
|
||||
-X github.com/anchore/syft/internal/version.gitCommit={{.Commit}}
|
||||
-X github.com/anchore/syft/internal/version.buildDate={{.Date}}
|
||||
-X github.com/anchore/syft/internal/version.gitDescription={{.Summary}}
|
||||
|
||||
- id: darwin-build
|
||||
binary: syft
|
||||
goos:
|
||||
- darwin
|
||||
goarch:
|
||||
- amd64
|
||||
- arm64
|
||||
mod_timestamp: *build-timestamp
|
||||
env: *build-env
|
||||
ldflags: *build-ldflags
|
||||
hooks:
|
||||
post:
|
||||
# we must have signing as a build hook instead of the signs section. The signs section must register a new
|
||||
# asset, where we want to replace an existing asset. A post-build hook has the advantage of not needing to
|
||||
# unpackage and repackage a tar.gz with a signed binary
|
||||
- ./.github/scripts/apple-signing/sign.sh "{{ .Path }}" "{{ .IsSnapshot }}" "{{ .Target }}"
|
||||
|
||||
- id: windows-build
|
||||
binary: syft
|
||||
goos:
|
||||
- windows
|
||||
goarch:
|
||||
- amd64
|
||||
mod_timestamp: *build-timestamp
|
||||
env: *build-env
|
||||
ldflags: *build-ldflags
|
||||
|
||||
archives:
|
||||
- id: linux-archives
|
||||
builds:
|
||||
- linux-build
|
||||
|
||||
# note: the signing process is depending on tar.gz archives. If this format changes then .github/scripts/apple-signing/*.sh will need to be adjusted
|
||||
- id: darwin-archives
|
||||
builds:
|
||||
- darwin-build
|
||||
|
||||
- id: windows-archives
|
||||
format: zip
|
||||
builds:
|
||||
- windows-build
|
||||
|
||||
nfpms:
|
||||
- license: "Apache 2.0"
|
||||
maintainer: "Anchore, Inc"
|
||||
homepage: &website "https://github.com/anchore/syft"
|
||||
description: &description "A tool that generates a Software Bill Of Materials (SBOM) from container images and filesystems"
|
||||
formats:
|
||||
- rpm
|
||||
- deb
|
||||
|
||||
brews:
|
||||
- tap:
|
||||
owner: anchore
|
||||
name: homebrew-syft
|
||||
ids:
|
||||
- darwin-archives
|
||||
- linux-archives
|
||||
homepage: *website
|
||||
description: *description
|
||||
license: "Apache License 2.0"
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
dist: snapshot
|
||||
@@ -25,7 +25,7 @@ COVERAGE_THRESHOLD := 62
|
||||
|
||||
# CI cache busting values; change these if you want CI to not use previous stored cache
|
||||
INTEGRATION_CACHE_BUSTER="894d8ca"
|
||||
CLI_CACHE_BUSTER="894d8ca"
|
||||
CLI_CACHE_BUSTER="e5cdfd8"
|
||||
BOOTSTRAP_CACHE="c7afb99ad"
|
||||
|
||||
## Build variables
|
||||
@@ -211,13 +211,13 @@ integration-fingerprint:
|
||||
.PHONY: java-packages-fingerprint
|
||||
java-packages-fingerprint:
|
||||
$(call title,Java test fixture fingerprint)
|
||||
cd syft/pkg/cataloger/java/test-fixtures/java-builds && \
|
||||
cd syft/cataloger/packages/java/test-fixtures/java-builds && \
|
||||
make packages.fingerprint
|
||||
|
||||
.PHONY: fixtures
|
||||
fixtures:
|
||||
$(call title,Generating test fixtures)
|
||||
cd syft/pkg/cataloger/java/test-fixtures/java-builds && make
|
||||
cd syft/cataloger/packages/java/test-fixtures/java-builds && make
|
||||
|
||||
.PHONY: generate-json-schema
|
||||
generate-json-schema: ## Generate a new json schema
|
||||
@@ -234,24 +234,25 @@ build: $(SNAPSHOTDIR) ## Build release snapshot binaries and packages
|
||||
$(SNAPSHOTDIR): ## Build snapshot release binaries and packages
|
||||
$(call title,Building snapshot artifacts)
|
||||
|
||||
cd .goreleaser && make snapshot-config
|
||||
# create a config with the dist dir overridden
|
||||
echo "dist: $(SNAPSHOTDIR)" > $(TEMPDIR)/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> $(TEMPDIR)/goreleaser.yaml
|
||||
|
||||
# build release snapshots
|
||||
bash -c "$(SNAPSHOT_CMD) --skip-sign"
|
||||
bash -c "SKIP_SIGNING=true $(SNAPSHOT_CMD) --skip-sign --config $(TEMPDIR)/goreleaser.yaml"
|
||||
|
||||
.PHONY: snapshot-with-signing
|
||||
snapshot-with-signing: ## Build snapshot release binaries and packages (with dummy signing)
|
||||
$(call title,Building snapshot artifacts (+ signing))
|
||||
|
||||
cd .goreleaser && make snapshot-with-signing-config
|
||||
# create a config with the dist dir overridden
|
||||
echo "dist: $(SNAPSHOTDIR)" > $(TEMPDIR)/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> $(TEMPDIR)/goreleaser.yaml
|
||||
|
||||
rm -f .github/scripts/apple-signing/log/*.txt
|
||||
|
||||
# remove the keychain with the trusted self-signed cert automatically (from failed previous runs)
|
||||
.github/scripts/apple-signing/cleanup.sh
|
||||
|
||||
# build release snapshots
|
||||
bash -c "$(SNAPSHOT_CMD) || (cat .github/scripts/apple-signing/log/*.txt && false)"
|
||||
bash -c "$(SNAPSHOT_CMD) --config $(TEMPDIR)/goreleaser.yaml || (cat .github/scripts/apple-signing/log/*.txt && false)"
|
||||
|
||||
# remove the keychain with the trusted self-signed cert automatically
|
||||
.github/scripts/apple-signing/cleanup.sh
|
||||
@@ -315,13 +316,16 @@ CHANGELOG.md:
|
||||
release: clean-dist CHANGELOG.md ## Build and publish final binaries and packages. Intended to be run only on macOS.
|
||||
$(call title,Publishing release artifacts)
|
||||
|
||||
cd .goreleaser && make release-config
|
||||
# create a config with the dist dir overridden
|
||||
echo "dist: $(DISTDIR)" > $(TEMPDIR)/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> $(TEMPDIR)/goreleaser.yaml
|
||||
|
||||
rm -f .github/scripts/apple-signing/log/*.txt
|
||||
|
||||
# note: notarization cannot be done in parallel, thus --parallelism 1
|
||||
bash -c "\
|
||||
$(RELEASE_CMD) \
|
||||
--config $(TEMPDIR)/goreleaser.yaml \
|
||||
--parallelism 1 \
|
||||
--release-notes <(cat CHANGELOG.md)\
|
||||
|| (cat .github/scripts/apple-signing/log/*.txt && false)"
|
||||
|
||||
@@ -114,6 +114,7 @@ Sources can be explicitly provided with a scheme:
|
||||
|
||||
```
|
||||
docker:yourrepo/yourimage:tag use images from the Docker daemon
|
||||
podman:yourrepo/yourimage:tag use images from the Podman daemon
|
||||
docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save"
|
||||
oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise)
|
||||
oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# See documentation here: https://github.com/artifacthub/hub/blob/v1.6.0/docs/metadata/artifacthub-repo.yml
|
||||
|
||||
owners:
|
||||
- name: wagoodman
|
||||
email: wagoodman@gmail.com
|
||||
+82
-29
@@ -9,6 +9,10 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/formats/cyclonedxjson"
|
||||
"github.com/anchore/syft/internal/formats/spdx22json"
|
||||
"github.com/anchore/syft/internal/formats/syftjson"
|
||||
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/internal"
|
||||
@@ -17,9 +21,8 @@ import (
|
||||
"github.com/anchore/syft/internal/ui"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/event"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
v1 "github.com/google/go-containerregistry/pkg/v1"
|
||||
"github.com/in-toto/in-toto-golang/in_toto"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pkg/profile"
|
||||
@@ -27,7 +30,6 @@ import (
|
||||
"github.com/sigstore/cosign/pkg/cosign"
|
||||
"github.com/sigstore/cosign/pkg/cosign/attestation"
|
||||
"github.com/sigstore/sigstore/pkg/signature/dsse"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
@@ -51,7 +53,11 @@ const (
|
||||
intotoJSONDsseType = `application/vnd.in-toto+json`
|
||||
)
|
||||
|
||||
var attestFormats = []format.Option{format.SPDXJSONOption, format.CycloneDxJSONOption, format.JSONOption}
|
||||
var attestFormats = []sbom.FormatID{
|
||||
syftjson.ID,
|
||||
spdx22json.ID,
|
||||
cyclonedxjson.ID,
|
||||
}
|
||||
|
||||
var (
|
||||
attestCmd = &cobra.Command{
|
||||
@@ -125,24 +131,36 @@ func selectPassFunc(keypath string) (cosign.PassFunc, error) {
|
||||
func attestExec(ctx context.Context, _ *cobra.Command, args []string) error {
|
||||
// can only be an image for attestation or OCI DIR
|
||||
userInput := args[0]
|
||||
fs := afero.NewOsFs()
|
||||
parsedScheme, _, _, err := source.DetectScheme(fs, image.DetectSource, userInput)
|
||||
si, err := source.ParseInput(userInput, appConfig.Platform, false)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not generate source input for attest command: %w", err)
|
||||
}
|
||||
|
||||
if parsedScheme != source.ImageScheme {
|
||||
return fmt.Errorf("attest command can only be used with image sources but discovered %q when given %q", parsedScheme, userInput)
|
||||
switch si.Type {
|
||||
case source.ImageType, source.UnknownType:
|
||||
// at this point we know that it cannot be dir: or file: schemes, so we will assume that the unknown scheme could represent an image
|
||||
si.Type = source.ImageType
|
||||
default:
|
||||
return fmt.Errorf("attest command can only be used with image sources but discovered %q when given %q", si.Type, userInput)
|
||||
}
|
||||
|
||||
if len(appConfig.Output) > 1 {
|
||||
// if the original detection was from a local daemon we want to short circuit
|
||||
// that and attempt to generate the image source from a registry source instead
|
||||
switch si.ImageSource {
|
||||
case image.UnknownSource, image.OciRegistrySource:
|
||||
si.ImageSource = image.OciRegistrySource
|
||||
default:
|
||||
return fmt.Errorf("attest command can only be used with image sources fetch directly from the registry, but discovered an image source of %q when given %q", si.ImageSource, userInput)
|
||||
}
|
||||
|
||||
if len(appConfig.Outputs) > 1 {
|
||||
return fmt.Errorf("unable to generate attestation for more than one output")
|
||||
}
|
||||
|
||||
output := format.ParseOption(appConfig.Output[0])
|
||||
predicateType := assertPredicateType(output)
|
||||
format := syft.FormatByName(appConfig.Outputs[0])
|
||||
predicateType := formatPredicateType(format)
|
||||
if predicateType == "" {
|
||||
return fmt.Errorf("could not produce attestation predicate for given format: %q. Available formats: %+v", output, attestFormats)
|
||||
return fmt.Errorf("could not produce attestation predicate for given format: %q. Available formats: %+v", formatAliases(format.ID()), formatAliases(attestFormats...))
|
||||
}
|
||||
|
||||
passFunc, err := selectPassFunc(appConfig.Attest.Key)
|
||||
@@ -162,7 +180,7 @@ func attestExec(ctx context.Context, _ *cobra.Command, args []string) error {
|
||||
defer sv.Close()
|
||||
|
||||
return eventLoop(
|
||||
attestationExecWorker(userInput, output, predicateType, sv),
|
||||
attestationExecWorker(*si, format, predicateType, sv),
|
||||
setupSignals(),
|
||||
eventSubscription,
|
||||
stereoscope.Cleanup,
|
||||
@@ -170,18 +188,38 @@ func attestExec(ctx context.Context, _ *cobra.Command, args []string) error {
|
||||
)
|
||||
}
|
||||
|
||||
func attestationExecWorker(userInput string, output format.Option, predicateType string, sv *sign.SignerVerifier) <-chan error {
|
||||
func attestationExecWorker(si source.Input, format sbom.Format, predicateType string, sv *sign.SignerVerifier) <-chan error {
|
||||
errs := make(chan error)
|
||||
go func() {
|
||||
defer close(errs)
|
||||
|
||||
s, src, err := generateSBOM(userInput, errs)
|
||||
catalogingOpts, err := appConfig.ToCatalogingOptions()
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
||||
sbomBytes, err := syft.Encode(*s, output)
|
||||
src, cleanup, err := source.NewFromRegistry(si, appConfig.Registry.ToOptions(), appConfig.Exclusions)
|
||||
if cleanup != nil {
|
||||
defer cleanup()
|
||||
}
|
||||
if err != nil {
|
||||
errs <- fmt.Errorf("failed to construct source from user input %q: %w", si.UserInput, err)
|
||||
return
|
||||
}
|
||||
|
||||
s, err := syft.Catalog(src, catalogingOpts...)
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
errs <- fmt.Errorf("no SBOM produced for %q", si.UserInput)
|
||||
return
|
||||
}
|
||||
|
||||
sbomBytes, err := syft.Encode(*s, format)
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
@@ -196,24 +234,34 @@ func attestationExecWorker(userInput string, output format.Option, predicateType
|
||||
return errs
|
||||
}
|
||||
|
||||
func assertPredicateType(output format.Option) string {
|
||||
switch output {
|
||||
case format.SPDXJSONOption:
|
||||
func formatPredicateType(format sbom.Format) string {
|
||||
switch format.ID() {
|
||||
case spdx22json.ID:
|
||||
return in_toto.PredicateSPDX
|
||||
// Tentative see https://github.com/in-toto/attestation/issues/82
|
||||
case format.CycloneDxJSONOption:
|
||||
case cyclonedxjson.ID:
|
||||
// Tentative see https://github.com/in-toto/attestation/issues/82
|
||||
return "https://cyclonedx.org/bom"
|
||||
case format.JSONOption:
|
||||
case syftjson.ID:
|
||||
return "https://syft.dev/bom"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
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 generateAttestation(predicate []byte, src *source.Source, sv *sign.SignerVerifier, predicateType string) error {
|
||||
h, err := v1.NewHash(src.Image.Metadata.ManifestDigest)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not hash manifest digest for image")
|
||||
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")
|
||||
case 1:
|
||||
default:
|
||||
return fmt.Errorf("cannot generate attestation since multiple repo digests were found for the image: %+v", src.Image.Metadata.RepoDigests)
|
||||
}
|
||||
|
||||
wrapped := dsse.WrapSigner(sv, intotoJSONDsseType)
|
||||
@@ -221,7 +269,7 @@ func generateAttestation(predicate []byte, src *source.Source, sv *sign.SignerVe
|
||||
sh, err := attestation.GenerateStatement(attestation.GenerateOpts{
|
||||
Predicate: bytes.NewBuffer(predicate),
|
||||
Type: predicateType,
|
||||
Digest: h.Hex,
|
||||
Digest: findValidDigest(src.Image.Metadata.RepoDigests),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -264,8 +312,13 @@ func setAttestFlags(flags *pflag.FlagSet) {
|
||||
|
||||
// in-toto attestations only support JSON predicates, so not all SBOM formats that syft can output are supported
|
||||
flags.StringP(
|
||||
"output", "o", string(format.JSONOption),
|
||||
fmt.Sprintf("the SBOM format encapsulated within the attestation, available options=%v", attestFormats),
|
||||
"output", "o", formatAliases(syftjson.ID)[0],
|
||||
fmt.Sprintf("the SBOM format encapsulated within the attestation, available options=%v", formatAliases(attestFormats...)),
|
||||
)
|
||||
|
||||
flags.StringP(
|
||||
"platform", "", "",
|
||||
"an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+7
-3
@@ -72,8 +72,8 @@ func initCmdAliasBindings() {
|
||||
panic(err)
|
||||
}
|
||||
case attestCmd:
|
||||
// the --output option is an independently defined flag, but a shared config option
|
||||
if err = bindSharedOutputConfigOption(attestCmd.Flags()); err != nil {
|
||||
// the --output and --platform options are independently defined flags, but a shared config option
|
||||
if err = bindSharedConfigOption(attestCmd.Flags()); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// even though the root command or packages command is NOT being run, we still need default bindings
|
||||
@@ -90,11 +90,15 @@ func initCmdAliasBindings() {
|
||||
}
|
||||
}
|
||||
|
||||
func bindSharedOutputConfigOption(flags *pflag.FlagSet) error {
|
||||
func bindSharedConfigOption(flags *pflag.FlagSet) error {
|
||||
if err := viper.BindPFlag("output", flags.Lookup("output")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := viper.BindPFlag("platform", flags.Lookup("platform")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func formatAliases(ids ...sbom.FormatID) (aliases []string) {
|
||||
for _, id := range ids {
|
||||
switch id {
|
||||
case syft.JSONFormatID:
|
||||
aliases = append(aliases, "syft-json")
|
||||
case syft.TextFormatID:
|
||||
aliases = append(aliases, "text")
|
||||
case syft.TableFormatID:
|
||||
aliases = append(aliases, "table")
|
||||
case syft.SPDXJSONFormatID:
|
||||
aliases = append(aliases, "spdx-json")
|
||||
case syft.SPDXTagValueFormatID:
|
||||
aliases = append(aliases, "spdx-tag-value")
|
||||
case syft.CycloneDxXMLFormatID:
|
||||
aliases = append(aliases, "cyclonedx-xml")
|
||||
case syft.CycloneDxJSONFormatID:
|
||||
aliases = append(aliases, "cyclonedx-json")
|
||||
default:
|
||||
aliases = append(aliases, string(id))
|
||||
}
|
||||
}
|
||||
return aliases
|
||||
}
|
||||
+12
-20
@@ -4,9 +4,10 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/formats"
|
||||
"github.com/anchore/syft/internal/output"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/internal/formats/table"
|
||||
|
||||
"github.com/anchore/syft/syft"
|
||||
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/hashicorp/go-multierror"
|
||||
)
|
||||
@@ -19,7 +20,7 @@ func makeWriter(outputs []string, defaultFile string) (sbom.Writer, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
writer, err := output.MakeWriter(outputOptions...)
|
||||
writer, err := sbom.NewWriter(outputOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -28,10 +29,10 @@ func makeWriter(outputs []string, defaultFile string) (sbom.Writer, error) {
|
||||
}
|
||||
|
||||
// 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 []output.WriterOption, errs error) {
|
||||
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(format.TableOption))
|
||||
outputs = append(outputs, string(table.ID))
|
||||
}
|
||||
|
||||
for _, name := range outputs {
|
||||
@@ -40,33 +41,24 @@ func parseOptions(outputs []string, defaultFile string) (out []output.WriterOpti
|
||||
// split to at most two parts for <format>=<file>
|
||||
parts := strings.SplitN(name, "=", 2)
|
||||
|
||||
// the format option is the first part
|
||||
// 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 option, use that
|
||||
// If a file is specified as part of the output formatName, use that
|
||||
if len(parts) > 1 {
|
||||
file = parts[1]
|
||||
}
|
||||
|
||||
option := format.ParseOption(name)
|
||||
if option == format.UnknownFormatOption {
|
||||
format := syft.FormatByName(name)
|
||||
if format == nil {
|
||||
errs = multierror.Append(errs, fmt.Errorf("bad output format: '%s'", name))
|
||||
continue
|
||||
}
|
||||
|
||||
encoder := formats.ByOption(option)
|
||||
if encoder == nil {
|
||||
errs = multierror.Append(errs, fmt.Errorf("unknown format: %s", outputFormat))
|
||||
continue
|
||||
}
|
||||
|
||||
out = append(out, output.WriterOption{
|
||||
Format: *encoder,
|
||||
Path: file,
|
||||
})
|
||||
out = append(out, sbom.NewWriterOption(format, file))
|
||||
}
|
||||
return out, errs
|
||||
}
|
||||
|
||||
+51
-65
@@ -10,13 +10,11 @@ import (
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/anchore"
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"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"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/event"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/pkg/profile"
|
||||
@@ -27,7 +25,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
packagesExample = ` {{.appName}} {{.command}} alpine:latest a summary of discovered packages
|
||||
packagesExample = ` {{.appName}} {{.command}} alpine:latest a summary of discovered packages
|
||||
{{.appName}} {{.command}} alpine:latest -o json show all possible cataloging details
|
||||
{{.appName}} {{.command}} alpine:latest -o cyclonedx show a CycloneDX formatted SBOM
|
||||
{{.appName}} {{.command}} alpine:latest -o cyclonedx-json show a CycloneDX JSON formatted SBOM
|
||||
@@ -42,6 +40,7 @@ const (
|
||||
|
||||
schemeHelpHeader = "You can also explicitly specify the scheme to use:"
|
||||
imageSchemeHelp = ` {{.appName}} {{.command}} docker:yourrepo/yourimage:tag explicitly use the Docker daemon
|
||||
{{.appName}} {{.command}} podman:yourrepo/yourimage:tag explicitly use the Podman daemon
|
||||
{{.appName}} {{.command}} registry:yourrepo/yourimage:tag pull image directly from a registry (no container runtime required)
|
||||
{{.appName}} {{.command}} docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save"
|
||||
{{.appName}} {{.command}} oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise)
|
||||
@@ -95,12 +94,12 @@ func init() {
|
||||
func setPackageFlags(flags *pflag.FlagSet) {
|
||||
// Formatting & Input options //////////////////////////////////////////////
|
||||
flags.StringP(
|
||||
"scope", "s", cataloger.DefaultSearchConfig().Scope.String(),
|
||||
"scope", "s", syft.DefaultCatalogingConfig().DefaultScope.String(),
|
||||
fmt.Sprintf("selection of layers to catalog, options=%v", source.AllScopes))
|
||||
|
||||
flags.StringArrayP(
|
||||
"output", "o", []string{string(format.TableOption)},
|
||||
fmt.Sprintf("report output format, options=%v", format.AllOptions),
|
||||
"output", "o", formatAliases(table.ID),
|
||||
fmt.Sprintf("report output format, options=%v", formatAliases(syft.FormatIDs()...)),
|
||||
)
|
||||
|
||||
flags.StringP(
|
||||
@@ -108,6 +107,17 @@ func setPackageFlags(flags *pflag.FlagSet) {
|
||||
"file to write the default report output to (default is STDOUT)",
|
||||
)
|
||||
|
||||
flags.StringP(
|
||||
"platform", "", "",
|
||||
"an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')",
|
||||
)
|
||||
|
||||
flags.StringArrayP(
|
||||
"cataloger", "", nil,
|
||||
// TODO: have a nice way for a user to get the set of catalogers that exist
|
||||
"set which catalogers to use when generating an SBOM",
|
||||
)
|
||||
|
||||
// Upload options //////////////////////////////////////////////////////////
|
||||
flags.StringP(
|
||||
"host", "H", "",
|
||||
@@ -149,7 +159,7 @@ func bindPackagesConfigOptions(flags *pflag.FlagSet) error {
|
||||
if err := bindExclusivePackagesConfigOptions(flags); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := bindSharedOutputConfigOption(flags); err != nil {
|
||||
if err := bindSharedConfigOption(flags); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -161,7 +171,11 @@ func bindExclusivePackagesConfigOptions(flags *pflag.FlagSet) error {
|
||||
|
||||
// note: output is not included since this configuration option is shared between multiple subcommands
|
||||
|
||||
if err := viper.BindPFlag("package.cataloger.scope", flags.Lookup("scope")); err != nil {
|
||||
if err := viper.BindPFlag("scope", flags.Lookup("scope")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := viper.BindPFlag("catalogers", flags.Lookup("cataloger")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -215,7 +229,7 @@ func validateInputArgs(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
func packagesExec(_ *cobra.Command, args []string) error {
|
||||
writer, err := makeWriter(appConfig.Output, appConfig.File)
|
||||
writer, err := makeWriter(appConfig.Outputs, appConfig.File)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -228,9 +242,13 @@ func packagesExec(_ *cobra.Command, args []string) error {
|
||||
|
||||
// could be an image or a directory, with or without a scheme
|
||||
userInput := args[0]
|
||||
si, err := source.ParseInput(userInput, appConfig.Platform, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not generate source input for packages command: %w", err)
|
||||
}
|
||||
|
||||
return eventLoop(
|
||||
packagesExecWorker(userInput, writer),
|
||||
packagesExecWorker(*si, writer),
|
||||
setupSignals(),
|
||||
eventSubscription,
|
||||
stereoscope.Cleanup,
|
||||
@@ -249,57 +267,35 @@ func isVerbose() (result bool) {
|
||||
return appConfig.CliOptions.Verbosity > 0 || isPipedInput
|
||||
}
|
||||
|
||||
func generateSBOM(userInput string, errs chan error) (*sbom.SBOM, *source.Source, error) {
|
||||
tasks, err := tasks()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
src, cleanup, err := source.New(userInput, appConfig.Registry.ToOptions(), appConfig.Exclusions)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
||||
}
|
||||
if cleanup != nil {
|
||||
defer cleanup()
|
||||
}
|
||||
|
||||
s := sbom.SBOM{
|
||||
Source: src.Metadata,
|
||||
Descriptor: sbom.Descriptor{
|
||||
Name: internal.ApplicationName,
|
||||
Version: version.FromBuild().Version,
|
||||
Configuration: appConfig,
|
||||
},
|
||||
}
|
||||
|
||||
buildRelationships(&s, src, tasks, errs)
|
||||
|
||||
return &s, src, nil
|
||||
}
|
||||
|
||||
func buildRelationships(s *sbom.SBOM, src *source.Source, tasks []task, errs chan error) {
|
||||
var relationships []<-chan artifact.Relationship
|
||||
for _, task := range tasks {
|
||||
c := make(chan artifact.Relationship)
|
||||
relationships = append(relationships, c)
|
||||
go runTask(task, &s.Artifacts, src, c, errs)
|
||||
}
|
||||
|
||||
s.Relationships = append(s.Relationships, mergeRelationships(relationships...)...)
|
||||
}
|
||||
|
||||
func packagesExecWorker(userInput string, writer sbom.Writer) <-chan error {
|
||||
func packagesExecWorker(si source.Input, writer sbom.Writer) <-chan error {
|
||||
errs := make(chan error)
|
||||
go func() {
|
||||
defer close(errs)
|
||||
s, src, err := generateSBOM(userInput, errs)
|
||||
|
||||
catalogingOpts, err := appConfig.ToCatalogingOptions()
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
||||
src, cleanup, err := source.New(si, appConfig.Registry.ToOptions(), appConfig.Exclusions)
|
||||
if cleanup != nil {
|
||||
defer cleanup()
|
||||
}
|
||||
if err != nil {
|
||||
errs <- fmt.Errorf("failed to construct source from user input %q: %w", si.UserInput, err)
|
||||
return
|
||||
}
|
||||
|
||||
s, err := syft.Catalog(src, catalogingOpts...)
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
||||
if s == nil {
|
||||
panic("nil sbomb returned with no error")
|
||||
errs <- fmt.Errorf("no SBOM produced for %q", si.UserInput)
|
||||
return
|
||||
}
|
||||
|
||||
if appConfig.Anchore.Host != "" {
|
||||
@@ -317,20 +313,10 @@ func packagesExecWorker(userInput string, writer sbom.Writer) <-chan error {
|
||||
return errs
|
||||
}
|
||||
|
||||
func mergeRelationships(cs ...<-chan artifact.Relationship) (relationships []artifact.Relationship) {
|
||||
for _, c := range cs {
|
||||
for n := range c {
|
||||
relationships = append(relationships, n)
|
||||
}
|
||||
}
|
||||
|
||||
return relationships
|
||||
}
|
||||
|
||||
func runPackageSbomUpload(src *source.Source, s sbom.SBOM) error {
|
||||
log.Infof("uploading results to %s", appConfig.Anchore.Host)
|
||||
|
||||
if src.Metadata.Scheme != source.ImageScheme {
|
||||
if src.Metadata.Scheme != source.ImageType {
|
||||
return fmt.Errorf("unable to upload results: only images are supported")
|
||||
}
|
||||
|
||||
|
||||
+30
-30
@@ -2,6 +2,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/anchore/syft/syft"
|
||||
"os"
|
||||
|
||||
"github.com/anchore/stereoscope"
|
||||
@@ -9,10 +10,7 @@ import (
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"github.com/anchore/syft/internal/formats/syftjson"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/output"
|
||||
"github.com/anchore/syft/internal/ui"
|
||||
"github.com/anchore/syft/internal/version"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/event"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
@@ -26,7 +24,7 @@ const powerUserExample = ` {{.appName}} {{.command}} <image>
|
||||
|
||||
DEPRECATED - THIS COMMAND WILL BE REMOVED in v1.0.0
|
||||
|
||||
Only image sources are supported (e.g. docker: , docker-archive: , oci: , etc.), the directory source (dir:) is not supported.
|
||||
Only image sources are supported (e.g. docker: , podman: , docker-archive: , oci: , etc.), the directory source (dir:) is not supported.
|
||||
|
||||
All behavior is controlled via application configuration and environment variables (see https://github.com/anchore/syft#configuration)
|
||||
`
|
||||
@@ -74,10 +72,12 @@ func powerUserExec(_ *cobra.Command, args []string) error {
|
||||
// could be an image or a directory, with or without a scheme
|
||||
userInput := args[0]
|
||||
|
||||
writer, err := output.MakeWriter(output.WriterOption{
|
||||
Format: syftjson.Format(),
|
||||
Path: appConfig.File,
|
||||
})
|
||||
writer, err := sbom.NewWriter(
|
||||
sbom.NewWriterOption(
|
||||
syftjson.Format(),
|
||||
appConfig.File,
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -105,17 +105,25 @@ func powerUserExecWorker(userInput string, writer sbom.Writer) <-chan error {
|
||||
go func() {
|
||||
defer close(errs)
|
||||
|
||||
appConfig.Secrets.Cataloger.Enabled = true
|
||||
appConfig.FileMetadata.Cataloger.Enabled = true
|
||||
appConfig.FileContents.Cataloger.Enabled = true
|
||||
appConfig.FileClassification.Cataloger.Enabled = true
|
||||
tasks, err := tasks()
|
||||
// TODO: replace
|
||||
//appConfig.Secrets.Cataloger.Enabled = true
|
||||
//appConfig.FileMetadata.Cataloger.Enabled = true
|
||||
//appConfig.FileContents.Cataloger.Enabled = true
|
||||
//appConfig.FileClassification.Cataloger.Enabled = true
|
||||
|
||||
catalogingOpts, err := appConfig.ToCatalogingOptions()
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
||||
src, cleanup, err := source.New(userInput, appConfig.Registry.ToOptions(), appConfig.Exclusions)
|
||||
si, err := source.ParseInput(userInput, appConfig.Platform, true)
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
||||
src, cleanup, err := source.New(*si, appConfig.Registry.ToOptions(), appConfig.Exclusions)
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
@@ -124,28 +132,20 @@ func powerUserExecWorker(userInput string, writer sbom.Writer) <-chan error {
|
||||
defer cleanup()
|
||||
}
|
||||
|
||||
s := sbom.SBOM{
|
||||
Source: src.Metadata,
|
||||
Descriptor: sbom.Descriptor{
|
||||
Name: internal.ApplicationName,
|
||||
Version: version.FromBuild().Version,
|
||||
Configuration: appConfig,
|
||||
},
|
||||
s, err := syft.Catalog(src, catalogingOpts...)
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
||||
var relationships []<-chan artifact.Relationship
|
||||
for _, task := range tasks {
|
||||
c := make(chan artifact.Relationship)
|
||||
relationships = append(relationships, c)
|
||||
|
||||
go runTask(task, &s.Artifacts, src, c, errs)
|
||||
if s == nil {
|
||||
errs <- fmt.Errorf("no SBOM produced for %q", si.UserInput)
|
||||
return
|
||||
}
|
||||
|
||||
s.Relationships = append(s.Relationships, mergeRelationships(relationships...)...)
|
||||
|
||||
bus.Publish(partybus.Event{
|
||||
Type: event.Exit,
|
||||
Value: func() error { return writer.Write(s) },
|
||||
Value: func() error { return writer.Write(*s) },
|
||||
})
|
||||
}()
|
||||
|
||||
|
||||
-231
@@ -1,231 +0,0 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type task func(*sbom.Artifacts, *source.Source) ([]artifact.Relationship, error)
|
||||
|
||||
func tasks() ([]task, error) {
|
||||
var tasks []task
|
||||
|
||||
generators := []func() (task, error){
|
||||
generateCatalogPackagesTask,
|
||||
generateCatalogFileMetadataTask,
|
||||
generateCatalogFileDigestsTask,
|
||||
generateCatalogSecretsTask,
|
||||
generateCatalogFileClassificationsTask,
|
||||
generateCatalogContentsTask,
|
||||
}
|
||||
|
||||
for _, generator := range generators {
|
||||
task, err := generator()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if task != nil {
|
||||
tasks = append(tasks, task)
|
||||
}
|
||||
}
|
||||
|
||||
return tasks, nil
|
||||
}
|
||||
|
||||
func generateCatalogPackagesTask() (task, error) {
|
||||
if !appConfig.Package.Cataloger.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
|
||||
packageCatalog, relationships, theDistro, err := syft.CatalogPackages(src, appConfig.Package.ToConfig())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results.PackageCatalog = packageCatalog
|
||||
results.LinuxDistribution = theDistro
|
||||
|
||||
return relationships, nil
|
||||
}
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
func generateCatalogFileMetadataTask() (task, error) {
|
||||
if !appConfig.FileMetadata.Cataloger.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
metadataCataloger := file.NewMetadataCataloger()
|
||||
|
||||
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
|
||||
resolver, err := src.FileResolver(appConfig.FileMetadata.Cataloger.ScopeOpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := metadataCataloger.Catalog(resolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results.FileMetadata = result
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
func generateCatalogFileDigestsTask() (task, error) {
|
||||
if !appConfig.FileMetadata.Cataloger.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
supportedHashAlgorithms := make(map[string]crypto.Hash)
|
||||
for _, h := range []crypto.Hash{
|
||||
crypto.MD5,
|
||||
crypto.SHA1,
|
||||
crypto.SHA256,
|
||||
} {
|
||||
supportedHashAlgorithms[file.DigestAlgorithmName(h)] = h
|
||||
}
|
||||
|
||||
var hashes []crypto.Hash
|
||||
for _, hashStr := range appConfig.FileMetadata.Digests {
|
||||
name := file.CleanDigestAlgorithmName(hashStr)
|
||||
hashObj, ok := supportedHashAlgorithms[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported hash algorithm: %s", hashStr)
|
||||
}
|
||||
hashes = append(hashes, hashObj)
|
||||
}
|
||||
|
||||
digestsCataloger, err := file.NewDigestsCataloger(hashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
|
||||
resolver, err := src.FileResolver(appConfig.FileMetadata.Cataloger.ScopeOpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := digestsCataloger.Catalog(resolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results.FileDigests = result
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
func generateCatalogSecretsTask() (task, error) {
|
||||
if !appConfig.Secrets.Cataloger.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
patterns, err := file.GenerateSearchPatterns(file.DefaultSecretsPatterns, appConfig.Secrets.AdditionalPatterns, appConfig.Secrets.ExcludePatternNames)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secretsCataloger, err := file.NewSecretsCataloger(patterns, appConfig.Secrets.RevealValues, appConfig.Secrets.SkipFilesAboveSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
|
||||
resolver, err := src.FileResolver(appConfig.Secrets.Cataloger.ScopeOpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := secretsCataloger.Catalog(resolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results.Secrets = result
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
func generateCatalogFileClassificationsTask() (task, error) {
|
||||
if !appConfig.FileClassification.Cataloger.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TODO: in the future we could expose out the classifiers via configuration
|
||||
classifierCataloger, err := file.NewClassificationCataloger(file.DefaultClassifiers)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
|
||||
resolver, err := src.FileResolver(appConfig.FileClassification.Cataloger.ScopeOpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := classifierCataloger.Catalog(resolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results.FileClassifications = result
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
func generateCatalogContentsTask() (task, error) {
|
||||
if !appConfig.FileContents.Cataloger.Enabled {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
contentsCataloger, err := file.NewContentsCataloger(appConfig.FileContents.Globs, appConfig.FileContents.SkipFilesAboveSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
task := func(results *sbom.Artifacts, src *source.Source) ([]artifact.Relationship, error) {
|
||||
resolver, err := src.FileResolver(appConfig.FileContents.Cataloger.ScopeOpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result, err := contentsCataloger.Catalog(resolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results.FileContents = result
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return task, nil
|
||||
}
|
||||
|
||||
func runTask(t task, a *sbom.Artifacts, src *source.Source, c chan<- artifact.Relationship, errs chan<- error) {
|
||||
defer close(c)
|
||||
|
||||
relationships, err := t(a, src)
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
|
||||
for _, relationship := range relationships {
|
||||
c <- relationship
|
||||
}
|
||||
}
|
||||
+13
-12
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var outputFormat string
|
||||
var versionCmdOutputFormat string
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
@@ -20,23 +20,24 @@ var versionCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
func init() {
|
||||
versionCmd.Flags().StringVarP(&outputFormat, "output", "o", "text", "format to show version information (available=[text, json])")
|
||||
versionCmd.Flags().StringVarP(&versionCmdOutputFormat, "output", "o", "text", "format to show version information (available=[text, json])")
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
||||
|
||||
func printVersion(_ *cobra.Command, _ []string) {
|
||||
versionInfo := version.FromBuild()
|
||||
|
||||
switch outputFormat {
|
||||
switch versionCmdOutputFormat {
|
||||
case "text":
|
||||
fmt.Println("Application: ", internal.ApplicationName)
|
||||
fmt.Println("Version: ", versionInfo.Version)
|
||||
fmt.Println("BuildDate: ", versionInfo.BuildDate)
|
||||
fmt.Println("GitCommit: ", versionInfo.GitCommit)
|
||||
fmt.Println("GitDescription: ", versionInfo.GitDescription)
|
||||
fmt.Println("Platform: ", versionInfo.Platform)
|
||||
fmt.Println("GoVersion: ", versionInfo.GoVersion)
|
||||
fmt.Println("Compiler: ", versionInfo.Compiler)
|
||||
fmt.Println("Application: ", internal.ApplicationName)
|
||||
fmt.Println("Version: ", versionInfo.Version)
|
||||
fmt.Println("JsonSchemaVersion: ", internal.JSONSchemaVersion)
|
||||
fmt.Println("BuildDate: ", versionInfo.BuildDate)
|
||||
fmt.Println("GitCommit: ", versionInfo.GitCommit)
|
||||
fmt.Println("GitDescription: ", versionInfo.GitDescription)
|
||||
fmt.Println("Platform: ", versionInfo.Platform)
|
||||
fmt.Println("GoVersion: ", versionInfo.GoVersion)
|
||||
fmt.Println("Compiler: ", versionInfo.Compiler)
|
||||
|
||||
case "json":
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
@@ -54,7 +55,7 @@ func printVersion(_ *cobra.Command, _ []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
default:
|
||||
fmt.Printf("unsupported output format: %s\n", outputFormat)
|
||||
fmt.Printf("unsupported output format: %s\n", versionCmdOutputFormat)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ module github.com/anchore/syft
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
github.com/CycloneDX/cyclonedx-go v0.4.0
|
||||
github.com/CycloneDX/cyclonedx-go v0.5.0
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/acobaugh/osrelease v0.1.0
|
||||
github.com/adrg/xdg v0.2.1
|
||||
@@ -13,10 +13,9 @@ require (
|
||||
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.0.0-20210922164639-b3fa992ebd29
|
||||
github.com/anchore/stereoscope v0.0.0-20220217141419-c6f02aed9ed2
|
||||
github.com/anchore/stereoscope v0.0.0-20220307154759-8a5a70c227d3
|
||||
github.com/antihax/optional v1.0.0
|
||||
github.com/bmatcuk/doublestar/v4 v4.0.2
|
||||
github.com/docker/docker v20.10.12+incompatible
|
||||
github.com/dustin/go-humanize v1.0.0
|
||||
github.com/facebookincubator/nvdtools v0.1.4
|
||||
github.com/go-test/deep v1.0.8
|
||||
@@ -56,7 +55,10 @@ require (
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require github.com/sigstore/cosign v1.5.2-0.20220222220736-ec79daf63c24
|
||||
require (
|
||||
github.com/docker/docker v20.10.12+incompatible
|
||||
github.com/sigstore/cosign v1.6.0
|
||||
)
|
||||
|
||||
require (
|
||||
bitbucket.org/creachadair/shell v0.0.6 // indirect
|
||||
@@ -125,21 +127,21 @@ require (
|
||||
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
|
||||
github.com/go-openapi/analysis v0.20.1 // indirect
|
||||
github.com/go-openapi/errors v0.20.1 // indirect
|
||||
github.com/go-openapi/analysis v0.21.2 // indirect
|
||||
github.com/go-openapi/errors v0.20.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/loads v0.21.0 // indirect
|
||||
github.com/go-openapi/runtime v0.23.0 // indirect
|
||||
github.com/go-openapi/loads v0.21.1 // indirect
|
||||
github.com/go-openapi/runtime v0.23.1 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.2 // indirect
|
||||
github.com/go-openapi/swag v0.21.1 // indirect
|
||||
github.com/go-openapi/validate v0.20.3 // indirect
|
||||
github.com/go-openapi/validate v0.21.0 // indirect
|
||||
github.com/go-piv/piv-go v1.9.0 // indirect
|
||||
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-stack/stack v1.8.0 // indirect
|
||||
github.com/go-stack/stack v1.8.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.2.0 // indirect
|
||||
github.com/golang/glog v1.0.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
@@ -170,7 +172,7 @@ require (
|
||||
github.com/hashicorp/go-version v1.4.0 // indirect
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/vault/api v1.3.1 // indirect
|
||||
github.com/hashicorp/vault/sdk v0.3.0 // indirect
|
||||
github.com/hashicorp/vault/sdk v0.4.1 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/jedisct1/go-minisign v0.0.0-20210703085342-c1f07ee84431 // indirect
|
||||
@@ -200,21 +202,21 @@ require (
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/ryanuber/go-glob v1.0.0 // indirect
|
||||
github.com/sassoftware/relic v0.0.0-20210427151427-dfb082b79b74 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.3.0 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.3.1 // indirect
|
||||
github.com/segmentio/ksuid v1.0.4 // indirect
|
||||
github.com/shibumi/go-pathspec v1.3.0 // indirect
|
||||
github.com/sigstore/fulcio v0.1.2-0.20220114150912-86a2036f9bc7 // indirect
|
||||
github.com/sigstore/rekor v0.4.1-0.20220114213500-23f583409af3 // indirect
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/soheilhy/cmux v0.1.5 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.0.0-beta.11 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.0.0-beta.12 // 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/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
|
||||
github.com/urfave/cli v1.22.5 // indirect
|
||||
github.com/xanzy/go-gitlab v0.55.1 // indirect
|
||||
github.com/xanzy/go-gitlab v0.56.0 // 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
|
||||
@@ -230,7 +232,7 @@ require (
|
||||
go.etcd.io/etcd/server/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/tests/v3 v3.5.0 // indirect
|
||||
go.etcd.io/etcd/v3 v3.5.0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.7.5 // indirect
|
||||
go.mongodb.org/mongo-driver v1.8.3 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.opentelemetry.io/contrib v1.3.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0 // indirect
|
||||
@@ -244,10 +246,11 @@ require (
|
||||
go.opentelemetry.io/proto/otlp v0.12.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.7.0 // indirect
|
||||
go.uber.org/zap v1.20.0 // indirect
|
||||
go.uber.org/zap v1.21.0 // indirect
|
||||
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
|
||||
golang.org/x/tools v0.1.8 // indirect
|
||||
google.golang.org/api v0.70.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/cheggaaa/pb.v1 v1.0.28 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
|
||||
@@ -270,11 +273,11 @@ require (
|
||||
// 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.9 // 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.7.1+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
|
||||
@@ -285,7 +288,7 @@ require (
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-containerregistry v0.8.1-0.20220209165246-a44adc326839
|
||||
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
|
||||
@@ -321,7 +324,6 @@ require (
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c // indirect
|
||||
google.golang.org/grpc v1.44.0 // indirect
|
||||
google.golang.org/protobuf v1.27.1 // indirect
|
||||
gopkg.in/ini.v1 v1.66.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||
)
|
||||
|
||||
@@ -186,8 +186,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
|
||||
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
|
||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/CycloneDX/cyclonedx-go v0.4.0 h1:Wz4QZ9B4RXGWIWTypVLEOVJgOdFfy5mcS5PGNzUkZxU=
|
||||
github.com/CycloneDX/cyclonedx-go v0.4.0/go.mod h1:rmRcf//gT7PIzovatusbWi377xqCg1FS4jyST0GH20E=
|
||||
github.com/CycloneDX/cyclonedx-go v0.5.0 h1:RWCnu2OrWUTF5C9DA3L0qVziUD2HlxSUWcL2OXlxfqE=
|
||||
github.com/CycloneDX/cyclonedx-go v0.5.0/go.mod h1:nQXAzrejxO39b14JFz2SvsUElegYfwBDowIzqjdUMk4=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo=
|
||||
@@ -234,9 +234,11 @@ github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v
|
||||
github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
|
||||
github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk=
|
||||
github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/ReneKroon/ttlcache/v2 v2.10.0/go.mod h1:mBxvsNY+BT8qLLd6CuAJubbKo6r0jh3nb5et22bbfGY=
|
||||
@@ -282,8 +284,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.0.0-20210922164639-b3fa992ebd29 h1:K9LfnxwhqvihqU0+MF325FNy7fsKV9EGaUxdfR4gnWk=
|
||||
github.com/anchore/packageurl-go v0.0.0-20210922164639-b3fa992ebd29/go.mod h1:Oc1UkGaJwY6ND6vtAqPSlYrptKRJngHwkwB6W7l1uP0=
|
||||
github.com/anchore/stereoscope v0.0.0-20220217141419-c6f02aed9ed2 h1:QuvMG+rqqJmtFRL+jqj5pFgjQcJSnEHEbtj1lKowLLQ=
|
||||
github.com/anchore/stereoscope v0.0.0-20220217141419-c6f02aed9ed2/go.mod h1:QpDHHV2h1NNfu7klzU75XC8RvSlaPK6HHgi0dy8A6sk=
|
||||
github.com/anchore/stereoscope v0.0.0-20220307154759-8a5a70c227d3 h1:Kx2jlMdENAf4cVjYGYLI+fiavVhzhtmU89GUYDITJ1w=
|
||||
github.com/anchore/stereoscope v0.0.0-20220307154759-8a5a70c227d3/go.mod h1:XESZQTgFETDBatmyoet6XZ0zVknoIMDSAhj2INj2a5w=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.0.0/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
@@ -434,8 +436,8 @@ github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D
|
||||
github.com/bombsimon/wsl/v3 v3.3.0/go.mod h1:st10JtZYLE4D5sC7b8xV4zTKZwAQjCH/Hy2Pm1FNZIc=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
|
||||
github.com/bradleyfalzon/ghinstallation/v2 v2.0.3/go.mod h1:tlgi+JWCXnKFx/Y4WtnDbZEINo31N5bcvnCoqieefmk=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.6.0 h1:knToPYa2xtfg42U3I6punFEjaGFKWQRXJwj0JTv4mTs=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.7.0 h1:AT0vOjO68RcLyenLCHOGZzSNiuto7ziqzq6Q1/3xzMQ=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.7.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||
github.com/breml/bidichk v0.1.1/go.mod h1:zbfeitpevDUGI7V91Uzzuwrn4Vls8MoBMrwtt78jmso=
|
||||
github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
@@ -550,8 +552,9 @@ github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoT
|
||||
github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g=
|
||||
github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c=
|
||||
github.com/containerd/containerd v1.5.8/go.mod h1:YdFSv5bTFLpG2HIYmfqDpSYYTDX+mc5qtSuYx1YUb/s=
|
||||
github.com/containerd/containerd v1.5.9 h1:rs6Xg1gtIxaeyG+Smsb/0xaSDu1VgFhOCKBXxMxbsF4=
|
||||
github.com/containerd/containerd v1.5.9/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ=
|
||||
github.com/containerd/containerd v1.5.10 h1:3cQ2uRVCkJVcx5VombsE7105Gl9Wrl7ORAO3+4+ogf4=
|
||||
github.com/containerd/containerd v1.5.10/go.mod h1:fvQqCfadDGga5HZyn3j4+dx56qj2I9YwBrlSdalvJYQ=
|
||||
github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
|
||||
@@ -679,8 +682,9 @@ github.com/docker/cli v20.10.12+incompatible h1:lZlz0uzG+GH+c0plStMUdF/qk3ppmgns
|
||||
github.com/docker/cli v20.10.12+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||
github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/distribution v2.8.0+incompatible h1:l9EaZDICImO1ngI+uTifW+ZYvvz7fKISBAKpg+MbWbY=
|
||||
github.com/docker/distribution v2.8.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v20.10.10+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v20.10.11+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v20.10.12+incompatible h1:CEeNmFM0QZIsJCZKMkZx0ZcahTiewkrgiwfYD+dfl1U=
|
||||
@@ -831,8 +835,9 @@ github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2
|
||||
github.com/go-openapi/analysis v0.19.10/go.mod h1:qmhS3VNFxBlquFJ0RGoDtylO9y4pgTAUNE9AEEMdlJQ=
|
||||
github.com/go-openapi/analysis v0.19.16/go.mod h1:GLInF007N83Ad3m8a/CbQ5TPzdnGT7workfHwuVjNVk=
|
||||
github.com/go-openapi/analysis v0.20.0/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og=
|
||||
github.com/go-openapi/analysis v0.20.1 h1:zdVbw8yoD4SWZeq+cWdGgquaB0W4VrsJvDJHJND/Ktc=
|
||||
github.com/go-openapi/analysis v0.20.1/go.mod h1:BMchjvaHDykmRMsK40iPtvyOfFdMMxlOmQr9FBZk+Og=
|
||||
github.com/go-openapi/analysis v0.21.2 h1:hXFrOYFHUAMQdu6zwAiKKJHJQ8kqZs1ux/ru1P1wLJU=
|
||||
github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY=
|
||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
|
||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
|
||||
@@ -841,14 +846,17 @@ github.com/go-openapi/errors v0.19.6/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpX
|
||||
github.com/go-openapi/errors v0.19.7/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.20.1 h1:j23mMDtRxMwIobkpId7sWh7Ddcx4ivaoqUbfXx5P+a8=
|
||||
github.com/go-openapi/errors v0.20.1/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
|
||||
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
|
||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
|
||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
|
||||
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
|
||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
@@ -866,8 +874,9 @@ github.com/go-openapi/loads v0.19.6/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hs
|
||||
github.com/go-openapi/loads v0.19.7/go.mod h1:brCsvE6j8mnbmGBh103PT/QLHfbyDxA4hsKvYBNEGVc=
|
||||
github.com/go-openapi/loads v0.20.0/go.mod h1:2LhKquiE513rN5xC6Aan6lYOSddlL8Mp20AW9kpviM4=
|
||||
github.com/go-openapi/loads v0.20.2/go.mod h1:hTVUotJ+UonAMMZsvakEgmWKgtulweO9vYP2bQYKA/o=
|
||||
github.com/go-openapi/loads v0.21.0 h1:jYtUO4wwP7psAweisP/MDoOpdzsYEESdoPcsWjHDR68=
|
||||
github.com/go-openapi/loads v0.21.0/go.mod h1:rHYve9nZrQ4CJhyeIIFJINGCg1tQpx2yJrrNo8sf1ws=
|
||||
github.com/go-openapi/loads v0.21.1 h1:Wb3nVZpdEzDTcly8S4HMkey6fjARRzb7iEaySimlDW0=
|
||||
github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g=
|
||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
|
||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
|
||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
|
||||
@@ -875,8 +884,9 @@ github.com/go-openapi/runtime v0.19.15/go.mod h1:dhGWCTKRXlAfGnQG0ONViOZpjfg0m2g
|
||||
github.com/go-openapi/runtime v0.19.16/go.mod h1:5P9104EJgYcizotuXhEuUrzVc+j1RiSjahULvYmlv98=
|
||||
github.com/go-openapi/runtime v0.19.24/go.mod h1:Lm9YGCeecBnUUkFTxPC4s1+lwrkJ0pthx8YvyjCfkgk=
|
||||
github.com/go-openapi/runtime v0.21.0/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs=
|
||||
github.com/go-openapi/runtime v0.23.0 h1:HX6ET2sHCIvaKeDDQoU01CtO1ekg5EkekHSkLTtWXH0=
|
||||
github.com/go-openapi/runtime v0.23.0/go.mod h1:aQg+kaIQEn+A2CRSY1TxbM8+sT9g2V3aLc1FbIAnbbs=
|
||||
github.com/go-openapi/runtime v0.23.1 h1:/Drg9R96eMmgKJHVWZADz78XbE39/6QiIiB45mc+epo=
|
||||
github.com/go-openapi/runtime v0.23.1/go.mod h1:AKurw9fNre+h3ELZfk6ILsfvPN+bvvlaU/M9q/r9hpk=
|
||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
|
||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
|
||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
|
||||
@@ -903,6 +913,7 @@ github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+W
|
||||
github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
|
||||
github.com/go-openapi/strfmt v0.21.2 h1:5NDNgadiX1Vhemth/TH4gCGopWSTdDjxl60H3B7f+os=
|
||||
github.com/go-openapi/strfmt v0.21.2/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k=
|
||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
|
||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
@@ -922,8 +933,9 @@ github.com/go-openapi/validate v0.19.10/go.mod h1:RKEZTUWDkxKQxN2jDT7ZnZi2bhZlbN
|
||||
github.com/go-openapi/validate v0.19.12/go.mod h1:Rzou8hA/CBw8donlS6WNEUQupNvUZ0waH08tGe6kAQ4=
|
||||
github.com/go-openapi/validate v0.19.15/go.mod h1:tbn/fdOwYHgrhPBzidZfJC2MIVvs9GA7monOmWBbeCI=
|
||||
github.com/go-openapi/validate v0.20.1/go.mod h1:b60iJT+xNNLfaQJUqLI7946tYiFEOuE9E4k54HpKcJ0=
|
||||
github.com/go-openapi/validate v0.20.3 h1:GZPPhhKSZrE8HjB4eEkoYAZmoWA4+tCemSgINH1/vKw=
|
||||
github.com/go-openapi/validate v0.20.3/go.mod h1:goDdqVGiigM3jChcrYJxD2joalke3ZXeftD16byIjA4=
|
||||
github.com/go-openapi/validate v0.21.0 h1:+Wqk39yKOhfpLqNLEC0/eViCkzM5FVXVqrvt526+wcI=
|
||||
github.com/go-openapi/validate v0.21.0/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg=
|
||||
github.com/go-piv/piv-go v1.9.0 h1:P6j2gjfP7zO7T3nCk/jwCgsvFRwB8shEqAJ4q85jgXc=
|
||||
github.com/go-piv/piv-go v1.9.0/go.mod h1:NZ2zmjVkfFaL/CF8cVQ/pXdXtuj110zEKGdJM6fJZZM=
|
||||
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
|
||||
@@ -951,8 +963,9 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw=
|
||||
github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
|
||||
github.com/go-test/deep v1.0.7/go.mod h1:QV8Hv/iy04NyLBxAdO9njL0iVPN1S4d/A3NVv1V36o8=
|
||||
@@ -1338,8 +1351,9 @@ github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpT
|
||||
github.com/hashicorp/vault/api v1.3.0/go.mod h1:EabNQLI0VWbWoGlA+oBLC8PXmR9D60aUVgQGvangFWQ=
|
||||
github.com/hashicorp/vault/api v1.3.1 h1:pkDkcgTh47PRjY1NEFeofqR4W/HkNUi9qIakESO2aRM=
|
||||
github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw=
|
||||
github.com/hashicorp/vault/sdk v0.3.0 h1:kR3dpxNkhh/wr6ycaJYqp6AFT/i2xaftbfnwZduTKEY=
|
||||
github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
|
||||
github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo=
|
||||
github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
|
||||
github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
@@ -1501,6 +1515,7 @@ github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@@ -1910,8 +1925,9 @@ github.com/scylladb/go-set v1.0.2/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQw
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.2.0/go.mod h1:eIjBmIP8LD2MLBL/DkQWayLiz006Q4p+hCu79rvWleY=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.3.0 h1:PH0mUKuUSXVEVDbrKMgGPcrqrnKA8gJii614+EKKi7g=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.3.0/go.mod h1:o8hhjkbNl2gOamKUA/eNW3xUrntHT9L4W89W1nfj43U=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.3.1 h1:LJuyMziazadwmQRRu1M7GMUo5S1oH1+YxU9FjuSFU8k=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.3.1/go.mod h1:o8hhjkbNl2gOamKUA/eNW3xUrntHT9L4W89W1nfj43U=
|
||||
github.com/securego/gosec/v2 v2.9.1/go.mod h1:oDcDLcatOJxkCGaCaq8lua1jTnYf6Sou4wdiJ1n4iHc=
|
||||
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
|
||||
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
|
||||
@@ -1927,8 +1943,8 @@ github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFR
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sigstore/cosign v1.5.2-0.20220222220736-ec79daf63c24 h1:DzEiTgBIVtBY8zGLYmSNJltvaCLIOz7hAUbCsClsvRw=
|
||||
github.com/sigstore/cosign v1.5.2-0.20220222220736-ec79daf63c24/go.mod h1:eedJzrb6Lf1Bq2nA3ipcNHE61Lgkpvt3hmxfZAMi02I=
|
||||
github.com/sigstore/cosign v1.6.0 h1:GBG+asPgsf2iawldL9uO8JDGbFO5yXuvaBTjdejsMzo=
|
||||
github.com/sigstore/cosign v1.6.0/go.mod h1:Ocd28z0Pwtd6+A8s/Vb4SbhwuWOqVdeYAW4yCGF4Ndg=
|
||||
github.com/sigstore/fulcio v0.1.2-0.20220114150912-86a2036f9bc7 h1:XE7A9lJ+wYhmUFBWYTaw3Ph943zHB4iBYd5R0SX0ZOA=
|
||||
github.com/sigstore/fulcio v0.1.2-0.20220114150912-86a2036f9bc7/go.mod h1:ANQivY/lfOp9hN92S813LEthkm/kit96hzeIF3SNoZA=
|
||||
github.com/sigstore/rekor v0.4.1-0.20220114213500-23f583409af3 h1:mbqXrm8YZXN/cJMGeBkgPnswtfrOxDE1f7QZdJ+POQE=
|
||||
@@ -2007,8 +2023,8 @@ github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhU
|
||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
||||
github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk=
|
||||
github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU=
|
||||
github.com/spiffe/go-spiffe/v2 v2.0.0-beta.11 h1:YBSTauKWMRhexLkyz0QD1wJ3xblC8q3c55xfB8DngAM=
|
||||
github.com/spiffe/go-spiffe/v2 v2.0.0-beta.11/go.mod h1:TEfgrEcyFhuSuvqohJt6IxENUNeHfndWCCV1EX7UaVk=
|
||||
github.com/spiffe/go-spiffe/v2 v2.0.0-beta.12 h1:Lh9MRyNNsAbGbMiKbVHDw+qk/DlkjS9wBTlH8v7OnaA=
|
||||
github.com/spiffe/go-spiffe/v2 v2.0.0-beta.12/go.mod h1:TEfgrEcyFhuSuvqohJt6IxENUNeHfndWCCV1EX7UaVk=
|
||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
|
||||
@@ -2132,8 +2148,8 @@ github.com/withfig/autocomplete-tools/packages/cobra v0.0.0-20220122124547-31d38
|
||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
|
||||
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
|
||||
github.com/xanzy/go-gitlab v0.31.0/go.mod h1:sPLojNBn68fMUWSxIJtdVVIP8uSBYqesTfDUseX11Ug=
|
||||
github.com/xanzy/go-gitlab v0.55.1 h1:IgX/DS9buV0AUz8fuJPQkdl0fQGfBiAsAHxpun8sNhg=
|
||||
github.com/xanzy/go-gitlab v0.55.1/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
|
||||
github.com/xanzy/go-gitlab v0.56.0 h1:/QHBvk3IKVNwvXB/UOWVb5J6VCN6r2bg9/WxjUbFY/0=
|
||||
github.com/xanzy/go-gitlab v0.56.0/go.mod h1:F0QEXwmqiBUxCgJm8fE9S+1veX4XC9Z4cfaAbqwk4YM=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
|
||||
@@ -2234,8 +2250,9 @@ go.mongodb.org/mongo-driver v1.4.4/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4S
|
||||
go.mongodb.org/mongo-driver v1.4.6/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
|
||||
go.mongodb.org/mongo-driver v1.5.1/go.mod h1:gRXCHX4Jo7J0IJ1oDQyUxF7jfy19UfxniMS4xxMmUqw=
|
||||
go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg=
|
||||
go.mongodb.org/mongo-driver v1.7.5 h1:ny3p0reEpgsR2cfA5cjgwFZg3Cv/ofFh/8jbhGtz9VI=
|
||||
go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng=
|
||||
go.mongodb.org/mongo-driver v1.8.3 h1:TDKlTkGDKm9kkJVUOAXDK5/fkqKHJVwYQSpoRfB43R4=
|
||||
go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
|
||||
go.mozilla.org/mozlog v0.0.0-20170222151521-4bb13139d403/go.mod h1:jHoPAGnDrCy6kaI2tAze5Prf0Nr0w/oNkROt2lw3n3o=
|
||||
go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
|
||||
go.opencensus.io v0.15.0/go.mod h1:UffZAU+4sDEINUGP/B7UfBBkq4fqLu9zXAX7ke6CHW0=
|
||||
@@ -2302,8 +2319,9 @@ go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
|
||||
go.uber.org/zap v1.20.0 h1:N4oPlghZwYG55MlU6LXk/Zp00FVNE9X9wrYO8CEs4lc=
|
||||
go.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
|
||||
go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
|
||||
gocloud.dev v0.19.0/go.mod h1:SmKwiR8YwIMMJvQBKLsC3fHNyMwXLw3PMDO+VVteJMI=
|
||||
gocloud.dev v0.24.1-0.20211119014450-028788aaaa4c/go.mod h1:EIJSlY7nvfeoWaV2GauF6es27gZfqtTVon47QFueoyE=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -2339,6 +2357,7 @@ golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20200930160638-afb6bcd081ae/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
@@ -2703,6 +2722,7 @@ golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGm
|
||||
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190110163146-51295c7ec13a/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -3136,6 +3156,7 @@ k8s.io/api v0.22.5/go.mod h1:mEhXyLaSD1qTOf40rRiKXkc+2iCem09rWLlFwhCEiAs=
|
||||
k8s.io/api v0.23.3 h1:KNrME8KHGr12Ozjf8ytOewKzZh6hl/hHUZeHddT3a38=
|
||||
k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ=
|
||||
k8s.io/apiextensions-apiserver v0.22.5/go.mod h1:tIXeZ0BrDxUb1PoAz+tgOz43Zi1Bp4BEEqVtUccMJbE=
|
||||
k8s.io/apimachinery v0.19.7/go.mod h1:6sRbGRAVY5DOCuZwB5XkqguBqpqLU6q/kOaOdk29z6Q=
|
||||
k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||
k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
|
||||
k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
|
||||
@@ -3172,6 +3193,7 @@ k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.30.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/klog/v2 v2.40.1 h1:P4RRucWk/lFOlDdkAr3mc7iWFkgKrZY9qZMAgek06S4=
|
||||
k8s.io/klog/v2 v2.40.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o=
|
||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
|
||||
k8s.io/kube-openapi v0.0.0-20211109043538-20434351676c/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk=
|
||||
@@ -3186,6 +3208,7 @@ k8s.io/utils v0.0.0-20211116205334-6203023598ed/go.mod h1:jPW/WVKK9YHAvNhRxK0md/
|
||||
k8s.io/utils v0.0.0-20220127004650-9b3446523e65 h1:ONWS0Wgdg5wRiQIAui7L/023aC9+IxrIrydY7l8llsE=
|
||||
k8s.io/utils v0.0.0-20220127004650-9b3446523e65/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
|
||||
knative.dev/hack v0.0.0-20220118141833-9b2ed8471e30/go.mod h1:PHt8x8yX5Z9pPquBEfIj0X66f8iWkWfR0S/sarACJrI=
|
||||
knative.dev/hack/schema v0.0.0-20220224013837-e1785985d364/go.mod h1:ffjwmdcrH5vN3mPhO8RrF2KfNnbHeCE2C60A+2cv3U0=
|
||||
knative.dev/pkg v0.0.0-20220202132633-df430fa0dd96 h1:JU0DFa06CaUtwSkAY0b3j47ohEJLIYlpPPgNgbPHlAo=
|
||||
knative.dev/pkg v0.0.0-20220202132633-df430fa0dd96/go.mod h1:etVT7Tm8pSDf4RKhGk4r7j/hj3dNBpvT7bO6a6wpahs=
|
||||
mvdan.cc/gofumpt v0.1.1/go.mod h1:yXG1r1WqZVKWbVRtBWKWX9+CxGYfA51nSomhM0woR48=
|
||||
@@ -3206,6 +3229,7 @@ sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 h1:kDi4JBNAsJWfz1aEXhO8Jg87J
|
||||
sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY=
|
||||
sigs.k8s.io/release-utils v0.4.1-0.20220207182343-6dadf2228617 h1:qbKzP8fFAdlnj4WqB9KrAuW4ply+bUQkdRlVrH4Jus0=
|
||||
sigs.k8s.io/release-utils v0.4.1-0.20220207182343-6dadf2228617/go.mod h1:t9pL38kZkTBVDcjL1y7ajrkNQFLiArVAjOVO0sxzFF0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
|
||||
@@ -6,14 +6,15 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/anchore/client-go/pkg/external"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"github.com/anchore/syft/syft/event"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/antihax/optional"
|
||||
"github.com/wagoodman/go-partybus"
|
||||
"github.com/wagoodman/go-progress"
|
||||
|
||||
"github.com/anchore/client-go/pkg/external"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/antihax/optional"
|
||||
)
|
||||
|
||||
type ImportConfig struct {
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -22,7 +24,7 @@ import (
|
||||
"github.com/wagoodman/go-progress"
|
||||
)
|
||||
|
||||
func must(c pkg.CPE, e error) pkg.CPE {
|
||||
func must(c cpe.CPE, e error) cpe.CPE {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
@@ -52,13 +54,13 @@ func (m *mockPackageSBOMImportAPI) ImportImagePackages(ctx context.Context, sess
|
||||
func sbomFixture() sbom.SBOM {
|
||||
return sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
PackageCatalog: pkg.NewCatalog(pkg.Package{
|
||||
Packages: pkg.NewCollection(pkg.Package{
|
||||
Name: "name",
|
||||
Version: "version",
|
||||
FoundBy: "foundBy",
|
||||
Locations: []source.Location{
|
||||
Locations: []file.Location{
|
||||
{
|
||||
Coordinates: source.Coordinates{
|
||||
Coordinates: file.Coordinates{
|
||||
RealPath: "path",
|
||||
FileSystemID: "layerID",
|
||||
},
|
||||
@@ -67,8 +69,8 @@ func sbomFixture() sbom.SBOM {
|
||||
Licenses: []string{"license"},
|
||||
Language: pkg.Python,
|
||||
Type: pkg.PythonPkg,
|
||||
CPEs: []pkg.CPE{
|
||||
must(pkg.NewCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*")),
|
||||
CPEs: []cpe.CPE{
|
||||
must(cpe.New("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*")),
|
||||
},
|
||||
PURL: "purl",
|
||||
MetadataType: pkg.PythonPackageMetadataType,
|
||||
@@ -102,13 +104,13 @@ func sbomFixture() sbom.SBOM {
|
||||
},
|
||||
Relationships: []artifact.Relationship{
|
||||
{
|
||||
From: source.NewLocation("/place1"),
|
||||
To: source.NewLocation("/place2"),
|
||||
From: file.NewLocation("/place1"),
|
||||
To: file.NewLocation("/place2"),
|
||||
Type: artifact.ContainsRelationship,
|
||||
},
|
||||
},
|
||||
Source: source.Metadata{
|
||||
Scheme: source.ImageScheme,
|
||||
Scheme: source.ImageType,
|
||||
ImageMetadata: source.ImageMetadata{
|
||||
UserInput: "user-in",
|
||||
Layers: nil,
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package file
|
||||
package archive
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
const perFileReadLimit = 2 * GB
|
||||
const perFileReadLimit = 2 * file.GB
|
||||
|
||||
// safeCopy limits the copy from the reader. This is useful when extracting files from archives to
|
||||
// protect against decompression bomb attacks.
|
||||
@@ -1,38 +1,39 @@
|
||||
package file
|
||||
package archive
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/bmatcuk/doublestar/v4"
|
||||
"github.com/mholt/archiver/v3"
|
||||
)
|
||||
|
||||
// ExtractGlobsFromTarToUniqueTempFile extracts paths matching the given globs within the given archive to a temporary directory, returning file openers for each file extracted.
|
||||
func ExtractGlobsFromTarToUniqueTempFile(archivePath, dir string, globs ...string) (map[string]Opener, error) {
|
||||
results := make(map[string]Opener)
|
||||
func ExtractGlobsFromTarToUniqueTempFile(archivePath, dir string, globs ...string) (map[string]file.Opener, error) {
|
||||
results := make(map[string]file.Opener)
|
||||
|
||||
// don't allow for full traversal, only select traversal from given paths
|
||||
if len(globs) == 0 {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
visitor := func(file archiver.File) error {
|
||||
defer file.Close()
|
||||
visitor := func(f archiver.File) error {
|
||||
defer f.Close()
|
||||
|
||||
// ignore directories
|
||||
if file.FileInfo.IsDir() {
|
||||
if f.FileInfo.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ignore any filename that doesn't match the given globs...
|
||||
if !matchesAnyGlob(file.Name(), globs...) {
|
||||
if !matchesAnyGlob(f.Name(), globs...) {
|
||||
return nil
|
||||
}
|
||||
|
||||
// we have a file we want to extract....
|
||||
tempfilePrefix := filepath.Base(filepath.Clean(file.Name())) + "-"
|
||||
tempfilePrefix := filepath.Base(filepath.Clean(f.Name())) + "-"
|
||||
tempFile, err := ioutil.TempFile(dir, tempfilePrefix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create temp file: %w", err)
|
||||
@@ -42,11 +43,11 @@ func ExtractGlobsFromTarToUniqueTempFile(archivePath, dir string, globs ...strin
|
||||
// provides a ReadCloser. It is up to the caller to handle closing the file explicitly.
|
||||
defer tempFile.Close()
|
||||
|
||||
if err := safeCopy(tempFile, file.ReadCloser); err != nil {
|
||||
return fmt.Errorf("unable to copy source=%q for tar=%q: %w", file.Name(), archivePath, err)
|
||||
if err := safeCopy(tempFile, f.ReadCloser); err != nil {
|
||||
return fmt.Errorf("unable to copy source=%q for tar=%q: %w", f.Name(), archivePath, err)
|
||||
}
|
||||
|
||||
results[file.Name()] = Opener{path: tempFile.Name()}
|
||||
results[f.Name()] = file.NewOpener(tempFile.Name())
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package file
|
||||
package archive
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -1,4 +1,4 @@
|
||||
package file
|
||||
package archive
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -48,7 +48,7 @@ func (z ZipFileManifest) GlobMatch(patterns ...string) []string {
|
||||
// so that glob logic is consistent inside and outside of ZIP archives
|
||||
normalizedEntry := normalizeZipEntryName(entry)
|
||||
|
||||
if GlobMatch(pattern, normalizedEntry) {
|
||||
if internal.FileNameGlobMatch(pattern, normalizedEntry) {
|
||||
uniqueMatches.Add(entry)
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package file
|
||||
package archive
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
@@ -1,4 +1,4 @@
|
||||
package file
|
||||
package archive
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
@@ -9,15 +9,9 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
"github.com/anchore/syft/syft/file"
|
||||
|
||||
const (
|
||||
// represents the order of bytes
|
||||
_ = iota
|
||||
KB = 1 << (10 * iota)
|
||||
MB
|
||||
GB
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
type errZipSlipDetected struct {
|
||||
@@ -71,16 +65,16 @@ func TraverseFilesInZip(archivePath string, visitor func(*zip.File) error, paths
|
||||
}
|
||||
|
||||
// ExtractFromZipToUniqueTempFile extracts select paths for the given archive to a temporary directory, returning file openers for each file extracted.
|
||||
func ExtractFromZipToUniqueTempFile(archivePath, dir string, paths ...string) (map[string]Opener, error) {
|
||||
results := make(map[string]Opener)
|
||||
func ExtractFromZipToUniqueTempFile(archivePath, dir string, paths ...string) (map[string]file.Opener, error) {
|
||||
results := make(map[string]file.Opener)
|
||||
|
||||
// don't allow for full traversal, only select traversal from given paths
|
||||
if len(paths) == 0 {
|
||||
return results, nil
|
||||
}
|
||||
|
||||
visitor := func(file *zip.File) error {
|
||||
tempfilePrefix := filepath.Base(filepath.Clean(file.Name)) + "-"
|
||||
visitor := func(f *zip.File) error {
|
||||
tempfilePrefix := filepath.Base(filepath.Clean(f.Name)) + "-"
|
||||
|
||||
tempFile, err := ioutil.TempFile(dir, tempfilePrefix)
|
||||
if err != nil {
|
||||
@@ -91,26 +85,26 @@ func ExtractFromZipToUniqueTempFile(archivePath, dir string, paths ...string) (m
|
||||
// provides a ReadCloser. It is up to the caller to handle closing the file explicitly.
|
||||
defer tempFile.Close()
|
||||
|
||||
zippedFile, err := file.Open()
|
||||
zippedFile, err := f.Open()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read file=%q from zip=%q: %w", file.Name, archivePath, err)
|
||||
return fmt.Errorf("unable to read file=%q from zip=%q: %w", f.Name, archivePath, err)
|
||||
}
|
||||
defer func() {
|
||||
err := zippedFile.Close()
|
||||
if err != nil {
|
||||
log.Errorf("unable to close source file=%q from zip=%q: %+v", file.Name, archivePath, err)
|
||||
log.Errorf("unable to close source file=%q from zip=%q: %+v", f.Name, archivePath, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if file.FileInfo().IsDir() {
|
||||
return fmt.Errorf("unable to extract directories, only files: %s", file.Name)
|
||||
if f.FileInfo().IsDir() {
|
||||
return fmt.Errorf("unable to extract directories, only files: %s", f.Name)
|
||||
}
|
||||
|
||||
if err := safeCopy(tempFile, zippedFile); err != nil {
|
||||
return fmt.Errorf("unable to copy source=%q for zip=%q: %w", file.Name, archivePath, err)
|
||||
return fmt.Errorf("unable to copy source=%q for zip=%q: %w", f.Name, archivePath, err)
|
||||
}
|
||||
|
||||
results[file.Name] = Opener{path: tempFile.Name()}
|
||||
results[f.Name] = file.NewOpener(tempFile.Name())
|
||||
|
||||
return nil
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package file
|
||||
package archive
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
@@ -1,4 +1,4 @@
|
||||
package file
|
||||
package archive
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
@@ -1,7 +1,7 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package file
|
||||
package archive
|
||||
|
||||
import (
|
||||
"os"
|
||||
@@ -3,12 +3,16 @@ package config
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"path"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/adrg/xdg"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/version"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
@@ -17,6 +21,7 @@ import (
|
||||
|
||||
var ErrApplicationConfigNotFound = fmt.Errorf("application config not found")
|
||||
|
||||
// TODO: set all catalogers when this is set
|
||||
var catalogerEnabledDefault = false
|
||||
|
||||
type defaultValueLoader interface {
|
||||
@@ -29,23 +34,66 @@ type parser interface {
|
||||
|
||||
// Application is the main syft application configuration.
|
||||
type Application struct {
|
||||
ConfigPath string `yaml:",omitempty" json:"configPath"` // the location where the application config was read from (either from -c or discovered while loading)
|
||||
Output []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output
|
||||
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
|
||||
Quiet bool `yaml:"quiet" json:"quiet" mapstructure:"quiet"` // -q, indicates to not show any status output to stderr (ETUI or logging UI)
|
||||
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
|
||||
Anchore anchore `yaml:"anchore" json:"anchore" mapstructure:"anchore"` // options for interacting with Anchore Engine/Enterprise
|
||||
CliOptions CliOnlyOptions `yaml:"-" json:"-"` // all options only available through the CLI (not via env vars or config)
|
||||
Dev development `yaml:"dev" json:"dev" mapstructure:"dev"`
|
||||
Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options
|
||||
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
|
||||
FileMetadata FileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
|
||||
FileClassification fileClassification `yaml:"file-classification" json:"file-classification" mapstructure:"file-classification"`
|
||||
FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"`
|
||||
Secrets secrets `yaml:"secrets" json:"secrets" mapstructure:"secrets"`
|
||||
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
|
||||
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
|
||||
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
|
||||
ConfigPath string `yaml:",omitempty" json:"configPath"` // the location where the application config was read from (either from -c or discovered while loading)
|
||||
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output
|
||||
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
|
||||
Quiet bool `yaml:"quiet" json:"quiet" mapstructure:"quiet"` // -q, indicates to not show any status output to stderr (ETUI or logging UI)
|
||||
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
|
||||
Anchore anchore `yaml:"anchore" json:"anchore" mapstructure:"anchore"` // options for interacting with Anchore Engine/Enterprise
|
||||
CliOptions CliOnlyOptions `yaml:"-" json:"-"` // all options only available through the CLI (not via env vars or config)
|
||||
Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"`
|
||||
Scope string `yaml:"scope" json:"scope" mapstructure:"scope"`
|
||||
Dev development `yaml:"dev" json:"dev" mapstructure:"dev"`
|
||||
Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options
|
||||
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
|
||||
FileMetadata fileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
|
||||
FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"`
|
||||
Secrets secretsCfg `yaml:"secrets" json:"secrets" mapstructure:"secrets"`
|
||||
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
|
||||
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
|
||||
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
|
||||
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
|
||||
}
|
||||
|
||||
func (cfg Application) ToCatalogingOptions() ([]syft.CatalogingOption, error) {
|
||||
|
||||
digests, err := file.DigestHashesByName(cfg.FileMetadata.Digests...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to parse config item 'file-metadata.digests': %w", err)
|
||||
}
|
||||
|
||||
scopeOption := source.ParseScope(cfg.Scope)
|
||||
if scopeOption == source.UnknownScope {
|
||||
return nil, fmt.Errorf("bad scope value %q", cfg.Scope)
|
||||
}
|
||||
|
||||
secretsConfig, err := cfg.Secrets.ToConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
secretsScopeOption := source.ParseScope(cfg.Secrets.Scope)
|
||||
if secretsScopeOption == source.UnknownScope {
|
||||
return nil, fmt.Errorf("bad scope value %q", cfg.Secrets.Scope)
|
||||
}
|
||||
|
||||
c := syft.DefaultCatalogingConfig()
|
||||
|
||||
c.ToolVersion = version.FromBuild().Version
|
||||
c.ToolConfiguration = &c
|
||||
c.DefaultScope = scopeOption
|
||||
c.ProcessTasksInSerial = false
|
||||
c.DigestHashes = digests
|
||||
c.SecretsSearch = *secretsConfig
|
||||
c.SecretsScope = secretsScopeOption
|
||||
c.ContentsSearch = cfg.FileContents.ToConfig()
|
||||
|
||||
opts := []syft.CatalogingOption{
|
||||
syft.WithConfig(c),
|
||||
syft.WithCatalogers(cfg.Catalogers...),
|
||||
}
|
||||
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
// PowerUserCatalogerEnabledDefault switches all catalogers to be enabled when running power-user command
|
||||
@@ -99,10 +147,20 @@ func (cfg Application) loadDefaultValues(v *viper.Viper) {
|
||||
}
|
||||
|
||||
func (cfg *Application) parseConfigValues() error {
|
||||
// parse fields on application directyy
|
||||
var catalogers []string
|
||||
for _, c := range cfg.Catalogers {
|
||||
for _, f := range strings.Split(c, ",") {
|
||||
catalogers = append(catalogers, strings.TrimSpace(f))
|
||||
}
|
||||
}
|
||||
cfg.Catalogers = catalogers
|
||||
|
||||
// parse application config options
|
||||
for _, optionFn := range []func() error{
|
||||
cfg.parseUploadOptions,
|
||||
cfg.parseLogLevelOption,
|
||||
cfg.parseFile,
|
||||
} {
|
||||
if err := optionFn(); err != nil {
|
||||
return err
|
||||
@@ -125,6 +183,17 @@ func (cfg *Application) parseConfigValues() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Application) parseFile() error {
|
||||
if cfg.File != "" {
|
||||
expandedPath, err := homedir.Expand(cfg.File)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to expand file path=%q: %w", cfg.File, err)
|
||||
}
|
||||
cfg.File = expandedPath
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *Application) parseUploadOptions() error {
|
||||
if cfg.Anchore.Host == "" && cfg.Anchore.Dockerfile != "" {
|
||||
return fmt.Errorf("cannot provide dockerfile option without enabling upload")
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/docker/docker/pkg/homedir"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestApplication_parseFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config Application
|
||||
expected string
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "expand home dir",
|
||||
config: Application{
|
||||
File: "~/place.txt",
|
||||
},
|
||||
expected: filepath.Join(homedir.Get(), "place.txt"),
|
||||
},
|
||||
{
|
||||
name: "passthrough other paths",
|
||||
config: Application{
|
||||
File: "/other/place.txt",
|
||||
},
|
||||
expected: "/other/place.txt",
|
||||
},
|
||||
{
|
||||
name: "no path",
|
||||
config: Application{
|
||||
File: "",
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
cfg := tt.config
|
||||
|
||||
if tt.wantErr == nil {
|
||||
tt.wantErr = require.NoError
|
||||
}
|
||||
|
||||
tt.wantErr(t, cfg.parseFile())
|
||||
assert.Equal(t, tt.expected, cfg.File)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,28 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type attest struct {
|
||||
Key string `yaml:"key" json:"key" mapstructure:"key"`
|
||||
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 string `yaml:"-" json:"-" mapstructure:"password"` // password for the private key
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
func (cfg *attest) parseConfigValues() error {
|
||||
if cfg.Key != "" {
|
||||
expandedPath, err := homedir.Expand(cfg.Key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to expand key path=%q: %w", cfg.Key, err)
|
||||
}
|
||||
cfg.Key = expandedPath
|
||||
}
|
||||
|
||||
if cfg.Password == "" {
|
||||
// we allow for configuration via syft config/env vars and additionally interop with known cosign config env vars
|
||||
if pw, ok := os.LookupEnv("COSIGN_PASSWORD"); ok {
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
type catalogerOptions struct {
|
||||
Enabled bool `yaml:"enabled" json:"enabled" mapstructure:"enabled"`
|
||||
Scope string `yaml:"scope" json:"scope" mapstructure:"scope"`
|
||||
ScopeOpt source.Scope `yaml:"-" json:"-"`
|
||||
}
|
||||
|
||||
func (cfg catalogerOptions) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("package.cataloger.enabled", true)
|
||||
}
|
||||
|
||||
func (cfg *catalogerOptions) parseConfigValues() error {
|
||||
scopeOption := source.ParseScope(cfg.Scope)
|
||||
if scopeOption == source.UnknownScope {
|
||||
return fmt.Errorf("bad scope value %q", cfg.Scope)
|
||||
}
|
||||
cfg.ScopeOpt = scopeOption
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type fileClassification struct {
|
||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||
}
|
||||
|
||||
func (cfg fileClassification) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("file-classification.cataloger.enabled", catalogerEnabledDefault)
|
||||
v.SetDefault("file-classification.cataloger.scope", source.SquashedScope)
|
||||
}
|
||||
|
||||
func (cfg *fileClassification) parseConfigValues() error {
|
||||
return cfg.Cataloger.parseConfigValues()
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/cataloger/files/filecontents"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type fileContents struct {
|
||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||
SkipFilesAboveSize int64 `yaml:"skip-files-above-size" json:"skip-files-above-size" mapstructure:"skip-files-above-size"`
|
||||
Globs []string `yaml:"globs" json:"globs" mapstructure:"globs"`
|
||||
SkipFilesAboveSize int64 `yaml:"skip-files-above-size" json:"skip-files-above-size" mapstructure:"skip-files-above-size"`
|
||||
Globs []string `yaml:"globs" json:"globs" mapstructure:"globs"`
|
||||
}
|
||||
|
||||
func (cfg fileContents) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("file-contents.cataloger.enabled", catalogerEnabledDefault)
|
||||
v.SetDefault("file-contents.cataloger.scope", source.SquashedScope)
|
||||
v.SetDefault("file-contents.skip-files-above-size", 1*file.MB)
|
||||
v.SetDefault("file-contents.globs", []string{})
|
||||
}
|
||||
|
||||
func (cfg *fileContents) parseConfigValues() error {
|
||||
return cfg.Cataloger.parseConfigValues()
|
||||
func (cfg fileContents) ToConfig() filecontents.Config {
|
||||
return filecontents.Config{
|
||||
Globs: cfg.Globs,
|
||||
SkipFilesAboveSizeInBytes: cfg.SkipFilesAboveSize,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type FileMetadata struct {
|
||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||
Digests []string `yaml:"digests" json:"digests" mapstructure:"digests"`
|
||||
type fileMetadata struct {
|
||||
Digests []string `yaml:"digests" json:"digests" mapstructure:"digests"`
|
||||
}
|
||||
|
||||
func (cfg FileMetadata) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("file-metadata.cataloger.enabled", catalogerEnabledDefault)
|
||||
v.SetDefault("file-metadata.cataloger.scope", source.SquashedScope)
|
||||
func (cfg fileMetadata) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("file-metadata.digests", []string{"sha256"})
|
||||
}
|
||||
|
||||
func (cfg *FileMetadata) parseConfigValues() error {
|
||||
return cfg.Cataloger.parseConfigValues()
|
||||
func (cfg *fileMetadata) parseConfigValues() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
@@ -13,6 +16,17 @@ type logging struct {
|
||||
FileLocation string `yaml:"file" json:"file-location" mapstructure:"file"` // the file path to write logs to
|
||||
}
|
||||
|
||||
func (cfg *logging) parseConfigValues() error {
|
||||
if cfg.FileLocation != "" {
|
||||
expandedPath, err := homedir.Expand(cfg.FileLocation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to expand log file path=%q: %w", cfg.FileLocation, err)
|
||||
}
|
||||
cfg.FileLocation = expandedPath
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg logging) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("log.structured", false)
|
||||
v.SetDefault("log.file", "")
|
||||
|
||||
+8
-17
@@ -1,33 +1,24 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger"
|
||||
"github.com/anchore/syft/syft/cataloger/packages"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type pkg struct {
|
||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||
SearchUnindexedArchives bool `yaml:"search-unindexed-archives" json:"search-unindexed-archives" mapstructure:"search-unindexed-archives"`
|
||||
SearchIndexedArchives bool `yaml:"search-indexed-archives" json:"search-indexed-archives" mapstructure:"search-indexed-archives"`
|
||||
SearchUnindexedArchives bool `yaml:"search-unindexed-archives" json:"search-unindexed-archives" mapstructure:"search-unindexed-archives"`
|
||||
SearchIndexedArchives bool `yaml:"search-indexed-archives" json:"search-indexed-archives" mapstructure:"search-indexed-archives"`
|
||||
}
|
||||
|
||||
func (cfg pkg) loadDefaultValues(v *viper.Viper) {
|
||||
cfg.Cataloger.loadDefaultValues(v)
|
||||
c := cataloger.DefaultSearchConfig()
|
||||
c := packages.DefaultSearchConfig()
|
||||
v.SetDefault("package.search-unindexed-archives", c.IncludeUnindexedArchives)
|
||||
v.SetDefault("package.search-indexed-archives", c.IncludeIndexedArchives)
|
||||
}
|
||||
|
||||
func (cfg *pkg) parseConfigValues() error {
|
||||
return cfg.Cataloger.parseConfigValues()
|
||||
}
|
||||
|
||||
func (cfg pkg) ToConfig() cataloger.Config {
|
||||
return cataloger.Config{
|
||||
Search: cataloger.SearchConfig{
|
||||
IncludeIndexedArchives: cfg.SearchIndexedArchives,
|
||||
IncludeUnindexedArchives: cfg.SearchUnindexedArchives,
|
||||
Scope: cfg.Cataloger.ScopeOpt,
|
||||
},
|
||||
func (cfg pkg) ToConfig() packages.SearchConfig {
|
||||
return packages.SearchConfig{
|
||||
IncludeIndexedArchives: cfg.SearchIndexedArchives,
|
||||
IncludeUnindexedArchives: cfg.SearchUnindexedArchives,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,38 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/internal/file"
|
||||
"fmt"
|
||||
"github.com/anchore/syft/syft/cataloger/files/secrets"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type secrets struct {
|
||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||
type secretsCfg struct {
|
||||
AdditionalPatterns map[string]string `yaml:"additional-patterns" json:"additional-patterns" mapstructure:"additional-patterns"`
|
||||
ExcludePatternNames []string `yaml:"exclude-pattern-names" json:"exclude-pattern-names" mapstructure:"exclude-pattern-names"`
|
||||
RevealValues bool `yaml:"reveal-values" json:"reveal-values" mapstructure:"reveal-values"`
|
||||
SkipFilesAboveSize int64 `yaml:"skip-files-above-size" json:"skip-files-above-size" mapstructure:"skip-files-above-size"`
|
||||
Scope string `yaml:"scope" json:"scope" mapstructure:"scope"`
|
||||
}
|
||||
|
||||
func (cfg secrets) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("secrets.cataloger.enabled", catalogerEnabledDefault)
|
||||
v.SetDefault("secrets.cataloger.scope", source.AllLayersScope)
|
||||
func (cfg secretsCfg) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("secrets.scope", source.AllLayersScope)
|
||||
v.SetDefault("secrets.reveal-values", false)
|
||||
v.SetDefault("secrets.skip-files-above-size", 1*file.MB)
|
||||
v.SetDefault("secrets.additional-patterns", map[string]string{})
|
||||
v.SetDefault("secrets.exclude-pattern-names", []string{})
|
||||
}
|
||||
|
||||
func (cfg *secrets) parseConfigValues() error {
|
||||
return cfg.Cataloger.parseConfigValues()
|
||||
func (cfg secretsCfg) ToConfig() (*secrets.Config, error) {
|
||||
patterns, err := file.GenerateSearchPatterns(secrets.DefaultSecretsPatterns, cfg.AdditionalPatterns, cfg.ExcludePatternNames)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to process secrets config patterns: %w", err)
|
||||
}
|
||||
return &secrets.Config{
|
||||
Patterns: patterns,
|
||||
RevealValues: cfg.RevealValues,
|
||||
MaxFileSize: cfg.SkipFilesAboveSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package file
|
||||
package internal
|
||||
|
||||
// GlobMatch evaluates the given glob pattern against the given "name" string, indicating if there is a match or not.
|
||||
// FileNameGlobMatch evaluates the given glob pattern against the given "name" string, indicating if there is a match or not.
|
||||
// Source: https://research.swtch.com/glob.go
|
||||
func GlobMatch(pattern, name string) bool {
|
||||
func FileNameGlobMatch(pattern, name string) bool {
|
||||
px := 0
|
||||
nx := 0
|
||||
nextPx := 0
|
||||
@@ -1,4 +1,4 @@
|
||||
package file
|
||||
package internal
|
||||
|
||||
import (
|
||||
"strings"
|
||||
@@ -32,7 +32,7 @@ func TestGlobMatch(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
if GlobMatch(test.pattern, test.data) != test.ok {
|
||||
if FileNameGlobMatch(test.pattern, test.data) != test.ok {
|
||||
t.Errorf("failed glob='%s' data='%s'", test.pattern, test.data)
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,31 @@
|
||||
package cyclonedxhelpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/formats/common"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func encodeComponent(p pkg.Package) cyclonedx.Component {
|
||||
props := encodeProperties(p, "syft:package")
|
||||
props = append(props, encodeCPEs(p)...)
|
||||
if len(p.Locations) > 0 {
|
||||
props = append(props, encodeProperties(p.Locations, "syft:location")...)
|
||||
}
|
||||
if hasMetadata(p) {
|
||||
props = append(props, encodeProperties(p.Metadata, "syft:metadata")...)
|
||||
}
|
||||
|
||||
var properties *[]cyclonedx.Property
|
||||
if len(props) > 0 {
|
||||
properties = &props
|
||||
}
|
||||
|
||||
return cyclonedx.Component{
|
||||
Type: cyclonedx.ComponentTypeLibrary,
|
||||
Name: p.Name,
|
||||
@@ -20,12 +33,12 @@ func encodeComponent(p pkg.Package) cyclonedx.Component {
|
||||
Version: p.Version,
|
||||
PackageURL: p.PURL,
|
||||
Licenses: encodeLicenses(p),
|
||||
CPE: encodeCPE(p),
|
||||
CPE: encodeSingleCPE(p),
|
||||
Author: encodeAuthor(p),
|
||||
Publisher: encodePublisher(p),
|
||||
Description: encodeDescription(p),
|
||||
ExternalReferences: encodeExternalReferences(p),
|
||||
Properties: encodeProperties(p),
|
||||
Properties: properties,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,131 +47,56 @@ func hasMetadata(p pkg.Package) bool {
|
||||
}
|
||||
|
||||
func decodeComponent(c *cyclonedx.Component) *pkg.Package {
|
||||
typ := pkg.Type(findPropertyValue(c, "type"))
|
||||
purl := c.PackageURL
|
||||
if typ == "" && purl != "" {
|
||||
typ = pkg.TypeFromPURL(purl)
|
||||
values := map[string]string{}
|
||||
for _, p := range *c.Properties {
|
||||
values[p.Name] = p.Value
|
||||
}
|
||||
|
||||
metaType, meta := decodePackageMetadata(c)
|
||||
|
||||
p := &pkg.Package{
|
||||
Name: c.Name,
|
||||
Version: c.Version,
|
||||
FoundBy: findPropertyValue(c, "foundBy"),
|
||||
Locations: decodeLocations(c),
|
||||
Licenses: decodeLicenses(c),
|
||||
Language: pkg.Language(findPropertyValue(c, "language")),
|
||||
Type: typ,
|
||||
CPEs: decodeCPEs(c),
|
||||
PURL: purl,
|
||||
MetadataType: metaType,
|
||||
Metadata: meta,
|
||||
Name: c.Name,
|
||||
Version: c.Version,
|
||||
Locations: decodeLocations(values),
|
||||
Licenses: decodeLicenses(c),
|
||||
CPEs: decodeCPEs(c),
|
||||
PURL: c.PackageURL,
|
||||
}
|
||||
|
||||
common.DecodeInto(p, values, "syft:package", CycloneDXFields)
|
||||
|
||||
p.Metadata = decodePackageMetadata(values, c, p.MetadataType)
|
||||
|
||||
if p.Type == "" {
|
||||
p.Type = pkg.TypeFromPURL(p.PURL)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func decodeLocations(c *cyclonedx.Component) (out []source.Location) {
|
||||
if c.Properties != nil {
|
||||
props := *c.Properties
|
||||
for i := 0; i < len(props)-1; i++ {
|
||||
if props[i].Name == "path" && props[i+1].Name == "layerID" {
|
||||
out = append(out, source.Location{
|
||||
Coordinates: source.Coordinates{
|
||||
RealPath: props[i].Value,
|
||||
FileSystemID: props[i+1].Value,
|
||||
},
|
||||
})
|
||||
i++
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
func decodeLocations(vals map[string]string) []file.Location {
|
||||
v := common.Decode(reflect.TypeOf([]file.Location{}), vals, "syft:location", CycloneDXFields)
|
||||
out, _ := v.([]file.Location)
|
||||
return out
|
||||
}
|
||||
|
||||
func mapAllProps(c *cyclonedx.Component, obj reflect.Value) {
|
||||
value := obj
|
||||
if value.Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
func decodePackageMetadata(vals map[string]string, c *cyclonedx.Component, typ pkg.MetadataType) interface{} {
|
||||
if typ != "" && c.Properties != nil {
|
||||
metaTyp, ok := pkg.MetadataTypeByName[typ]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
metaPtrTyp := reflect.PtrTo(metaTyp)
|
||||
metaPtr := common.Decode(metaPtrTyp, vals, "syft:metadata", CycloneDXFields)
|
||||
|
||||
// Map all explicit metadata properties
|
||||
decodeAuthor(c.Author, metaPtr)
|
||||
decodeGroup(c.Group, metaPtr)
|
||||
decodePublisher(c.Publisher, metaPtr)
|
||||
decodeDescription(c.Description, metaPtr)
|
||||
decodeExternalReferences(c, metaPtr)
|
||||
|
||||
// return the actual interface{} -> struct ... not interface{} -> *struct
|
||||
return common.PtrToStruct(metaPtr)
|
||||
}
|
||||
|
||||
structType := value.Type()
|
||||
if structType.Kind() != reflect.Struct {
|
||||
return
|
||||
}
|
||||
for i := 0; i < value.NumField(); i++ {
|
||||
field := structType.Field(i)
|
||||
fieldType := field.Type
|
||||
fieldValue := value.Field(i)
|
||||
|
||||
name, mapped := field.Tag.Lookup("cyclonedx")
|
||||
if !mapped {
|
||||
continue
|
||||
}
|
||||
|
||||
if fieldType.Kind() == reflect.Ptr {
|
||||
fieldType = fieldType.Elem()
|
||||
if fieldValue.IsNil() {
|
||||
newValue := reflect.New(fieldType)
|
||||
fieldValue.Set(newValue)
|
||||
}
|
||||
fieldValue = fieldValue.Elem()
|
||||
}
|
||||
|
||||
propertyValue := findPropertyValue(c, name)
|
||||
switch fieldType.Kind() {
|
||||
case reflect.String:
|
||||
if fieldValue.CanSet() {
|
||||
fieldValue.SetString(propertyValue)
|
||||
} else {
|
||||
msg := fmt.Sprintf("unable to set field: %s.%s", structType.Name(), field.Name)
|
||||
log.Info(msg)
|
||||
}
|
||||
case reflect.Bool:
|
||||
if b, err := strconv.ParseBool(propertyValue); err == nil {
|
||||
fieldValue.SetBool(b)
|
||||
}
|
||||
case reflect.Int:
|
||||
if i, err := strconv.Atoi(propertyValue); err == nil {
|
||||
fieldValue.SetInt(int64(i))
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if i, err := strconv.ParseFloat(propertyValue, 64); err == nil {
|
||||
fieldValue.SetFloat(i)
|
||||
}
|
||||
case reflect.Struct:
|
||||
mapAllProps(c, fieldValue)
|
||||
case reflect.Complex128, reflect.Complex64:
|
||||
fallthrough
|
||||
case reflect.Ptr:
|
||||
msg := fmt.Sprintf("decoding CycloneDX properties to a pointer is not supported: %s.%s", field.Type.Name(), field.Name)
|
||||
log.Warnf(msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func decodePackageMetadata(c *cyclonedx.Component) (pkg.MetadataType, interface{}) {
|
||||
if c.Properties != nil {
|
||||
typ := pkg.MetadataType(findPropertyValue(c, "metadataType"))
|
||||
if typ != "" {
|
||||
meta := reflect.New(pkg.MetadataTypeByName[typ])
|
||||
metaPtr := meta.Interface()
|
||||
|
||||
// Map all dynamic properties
|
||||
mapAllProps(c, meta.Elem())
|
||||
|
||||
// Map all explicit metadata properties
|
||||
decodeAuthor(c.Author, metaPtr)
|
||||
decodeGroup(c.Group, metaPtr)
|
||||
decodePublisher(c.Publisher, metaPtr)
|
||||
decodeDescription(c.Description, metaPtr)
|
||||
decodeExternalReferences(c, metaPtr)
|
||||
|
||||
// return the actual interface{} | struct ( not interface{} | *struct )
|
||||
return typ, meta.Elem().Interface()
|
||||
}
|
||||
}
|
||||
|
||||
return pkg.UnknownMetadataType, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
+32
-29
@@ -1,15 +1,16 @@
|
||||
package cyclonedxhelpers
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"testing"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Test_encodeProperties(t *testing.T) {
|
||||
func Test_encodeComponentProperties(t *testing.T) {
|
||||
epoch := 2
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -25,8 +26,8 @@ func Test_encodeProperties(t *testing.T) {
|
||||
name: "from apk",
|
||||
input: pkg.Package{
|
||||
FoundBy: "cataloger",
|
||||
Locations: []source.Location{
|
||||
{Coordinates: source.Coordinates{RealPath: "test"}},
|
||||
Locations: []file.Location{
|
||||
{Coordinates: file.Coordinates{RealPath: "test"}},
|
||||
},
|
||||
Metadata: pkg.ApkMetadata{
|
||||
Package: "libc-utils",
|
||||
@@ -46,13 +47,14 @@ func Test_encodeProperties(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: &[]cyclonedx.Property{
|
||||
{Name: "foundBy", Value: "cataloger"},
|
||||
{Name: "path", Value: "test"},
|
||||
{Name: "originPackage", Value: "libc-dev"},
|
||||
{Name: "installedSize", Value: "4096"},
|
||||
{Name: "pullDependencies", Value: "musl-utils"},
|
||||
{Name: "pullChecksum", Value: "Q1p78yvTLG094tHE1+dToJGbmYzQE="},
|
||||
{Name: "gitCommitOfApkPort", Value: "97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479"},
|
||||
{Name: "syft:package:foundBy", Value: "cataloger"},
|
||||
{Name: "syft:location:0:path", Value: "test"},
|
||||
{Name: "syft:metadata:gitCommitOfApkPort", Value: "97b1c2842faa3bfa30f5811ffbf16d5ff9f1a479"},
|
||||
{Name: "syft:metadata:installedSize", Value: "4096"},
|
||||
{Name: "syft:metadata:originPackage", Value: "libc-dev"},
|
||||
{Name: "syft:metadata:pullChecksum", Value: "Q1p78yvTLG094tHE1+dToJGbmYzQE="},
|
||||
{Name: "syft:metadata:pullDependencies", Value: "musl-utils"},
|
||||
{Name: "syft:metadata:size", Value: "0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -71,10 +73,10 @@ func Test_encodeProperties(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: &[]cyclonedx.Property{
|
||||
{Name: "metadataType", Value: "DpkgMetadata"},
|
||||
{Name: "source", Value: "tzdata-dev"},
|
||||
{Name: "sourceVersion", Value: "1.0"},
|
||||
{Name: "installedSize", Value: "3036"},
|
||||
{Name: "syft:package:metadataType", Value: "DpkgMetadata"},
|
||||
{Name: "syft:metadata:installedSize", Value: "3036"},
|
||||
{Name: "syft:metadata:source", Value: "tzdata-dev"},
|
||||
{Name: "syft:metadata:sourceVersion", Value: "1.0"},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -92,12 +94,12 @@ func Test_encodeProperties(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: &[]cyclonedx.Property{
|
||||
{Name: "language", Value: pkg.Go.String()},
|
||||
{Name: "type", Value: "go-module"},
|
||||
{Name: "metadataType", Value: "GolangBinMetadata"},
|
||||
{Name: "goCompiledVersion", Value: "1.17"},
|
||||
{Name: "architecture", Value: "amd64"},
|
||||
{Name: "h1Digest", Value: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k="},
|
||||
{Name: "syft:package:language", Value: pkg.Go.String()},
|
||||
{Name: "syft:package:metadataType", Value: "GolangBinMetadata"},
|
||||
{Name: "syft:package:type", Value: "go-module"},
|
||||
{Name: "syft:metadata:architecture", Value: "amd64"},
|
||||
{Name: "syft:metadata:goCompiledVersion", Value: "1.17"},
|
||||
{Name: "syft:metadata:h1Digest", Value: "h1:KlOXYy8wQWTUJYFgkUI40Lzr06ofg5IRXUK5C7qZt1k="},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -121,18 +123,19 @@ func Test_encodeProperties(t *testing.T) {
|
||||
},
|
||||
},
|
||||
expected: &[]cyclonedx.Property{
|
||||
{Name: "type", Value: "rpm"},
|
||||
{Name: "metadataType", Value: "RpmdbMetadata"},
|
||||
{Name: "epoch", Value: "2"},
|
||||
{Name: "release", Value: "1"},
|
||||
{Name: "sourceRpm", Value: "dive-0.9.2-1.src.rpm"},
|
||||
{Name: "size", Value: "12406784"},
|
||||
{Name: "syft:package:metadataType", Value: "RpmdbMetadata"},
|
||||
{Name: "syft:package:type", Value: "rpm"},
|
||||
{Name: "syft:metadata:epoch", Value: "2"},
|
||||
{Name: "syft:metadata:release", Value: "1"},
|
||||
{Name: "syft:metadata:size", Value: "12406784"},
|
||||
{Name: "syft:metadata:sourceRpm", Value: "dive-0.9.2-1.src.rpm"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, encodeProperties(test.input))
|
||||
c := encodeComponent(test.input)
|
||||
assert.Equal(t, test.expected, c.Properties)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -2,29 +2,57 @@ package cyclonedxhelpers
|
||||
|
||||
import (
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func encodeCPE(p pkg.Package) string {
|
||||
func encodeSingleCPE(p pkg.Package) string {
|
||||
// Since the CPEs in a package are sorted by specificity
|
||||
// we can extract the first CPE as the one to output in cyclonedx
|
||||
if len(p.CPEs) > 0 {
|
||||
return pkg.CPEString(p.CPEs[0])
|
||||
return cpe.String(p.CPEs[0])
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func decodeCPEs(c *cyclonedx.Component) []pkg.CPE {
|
||||
// FIXME we not encoding all the CPEs (see above), so here we just use the single provided one
|
||||
func encodeCPEs(p pkg.Package) (out []cyclonedx.Property) {
|
||||
for i, c := range p.CPEs {
|
||||
// first CPE is "most specific" and already encoded as the component CPE
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
out = append(out, cyclonedx.Property{
|
||||
Name: "syft:cpe23",
|
||||
Value: cpe.String(c),
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func decodeCPEs(c *cyclonedx.Component) (out []cpe.CPE) {
|
||||
if c.CPE != "" {
|
||||
cp, err := pkg.NewCPE(c.CPE)
|
||||
cp, err := cpe.New(c.CPE)
|
||||
if err != nil {
|
||||
log.Warnf("invalid CPE: %s", c.CPE)
|
||||
} else {
|
||||
return []pkg.CPE{cp}
|
||||
out = append(out, cp)
|
||||
}
|
||||
}
|
||||
|
||||
return []pkg.CPE{}
|
||||
if c.Properties != nil {
|
||||
for _, p := range *c.Properties {
|
||||
if p.Name == "syft:cpe23" {
|
||||
cp, err := cpe.New(p.Value)
|
||||
if err != nil {
|
||||
log.Warnf("invalid CPE: %s", p.Value)
|
||||
} else {
|
||||
out = append(out, cp)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package cyclonedxhelpers
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func Test_encodeCPE(t *testing.T) {
|
||||
testCPE := pkg.MustCPE("cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*")
|
||||
testCPE2 := pkg.MustCPE("cpe:2.3:a:name:name2:3.2:*:*:*:*:*:*:*")
|
||||
testCPE := cpe.Must("cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*")
|
||||
testCPE2 := cpe.Must("cpe:2.3:a:name:name2:3.2:*:*:*:*:*:*:*")
|
||||
tests := []struct {
|
||||
name string
|
||||
input pkg.Package
|
||||
@@ -19,14 +21,14 @@ func Test_encodeCPE(t *testing.T) {
|
||||
// note: since this is an optional field, no value is preferred over NONE or NOASSERTION
|
||||
name: "no metadata",
|
||||
input: pkg.Package{
|
||||
CPEs: []pkg.CPE{},
|
||||
CPEs: []cpe.CPE{},
|
||||
},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "single CPE",
|
||||
input: pkg.Package{
|
||||
CPEs: []pkg.CPE{
|
||||
CPEs: []cpe.CPE{
|
||||
testCPE,
|
||||
},
|
||||
},
|
||||
@@ -35,7 +37,7 @@ func Test_encodeCPE(t *testing.T) {
|
||||
{
|
||||
name: "multiple CPEs",
|
||||
input: pkg.Package{
|
||||
CPEs: []pkg.CPE{
|
||||
CPEs: []cpe.CPE{
|
||||
testCPE2,
|
||||
testCPE,
|
||||
},
|
||||
@@ -51,7 +53,7 @@ func Test_encodeCPE(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, encodeCPE(test.input))
|
||||
assert.Equal(t, test.expected, encodeSingleCPE(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,13 @@ import (
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func GetValidator(format cyclonedx.BOMFileFormat) format.Validator {
|
||||
func GetValidator(format cyclonedx.BOMFileFormat) sbom.Validator {
|
||||
return func(reader io.Reader) error {
|
||||
bom := &cyclonedx.BOM{}
|
||||
err := cyclonedx.NewBOMDecoder(reader, format).Decode(bom)
|
||||
@@ -28,7 +27,7 @@ func GetValidator(format cyclonedx.BOMFileFormat) format.Validator {
|
||||
}
|
||||
}
|
||||
|
||||
func GetDecoder(format cyclonedx.BOMFileFormat) format.Decoder {
|
||||
func GetDecoder(format cyclonedx.BOMFileFormat) sbom.Decoder {
|
||||
return func(reader io.Reader) (*sbom.SBOM, error) {
|
||||
bom := &cyclonedx.BOM{}
|
||||
err := cyclonedx.NewBOMDecoder(reader, format).Decode(bom)
|
||||
@@ -50,7 +49,7 @@ func toSyftModel(bom *cyclonedx.BOM) (*sbom.SBOM, error) {
|
||||
}
|
||||
s := &sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
PackageCatalog: pkg.NewCatalog(),
|
||||
Packages: pkg.NewCollection(),
|
||||
LinuxDistribution: linuxReleaseFromComponents(*bom.Components),
|
||||
},
|
||||
Source: meta,
|
||||
@@ -87,7 +86,7 @@ func collectPackages(component *cyclonedx.Component, s *sbom.SBOM, idMap map[str
|
||||
idMap[component.BOMRef] = p
|
||||
// TODO there must be a better way than needing to call this manually:
|
||||
p.SetID()
|
||||
s.Artifacts.PackageCatalog.Add(*p)
|
||||
s.Artifacts.Packages.Add(*p)
|
||||
}
|
||||
|
||||
if component.Components != nil {
|
||||
@@ -200,7 +199,7 @@ func decodeMetadata(component *cyclonedx.Component) source.Metadata {
|
||||
switch component.Type {
|
||||
case cyclonedx.ComponentTypeContainer:
|
||||
return source.Metadata{
|
||||
Scheme: source.ImageScheme,
|
||||
Scheme: source.ImageType,
|
||||
ImageMetadata: source.ImageMetadata{
|
||||
UserInput: component.Name,
|
||||
ID: component.BOMRef,
|
||||
@@ -209,7 +208,7 @@ func decodeMetadata(component *cyclonedx.Component) source.Metadata {
|
||||
}
|
||||
case cyclonedx.ComponentTypeFile:
|
||||
return source.Metadata{
|
||||
Scheme: source.FileScheme, // or source.DirectoryScheme
|
||||
Scheme: source.FileType, // or source.DirectoryType
|
||||
Path: component.Name,
|
||||
ImageMetadata: source.ImageMetadata{
|
||||
UserInput: component.Name,
|
||||
|
||||
@@ -212,7 +212,7 @@ func Test_decode(t *testing.T) {
|
||||
assert.Equal(t, e.ver, sbom.Artifacts.LinuxDistribution.VersionID)
|
||||
}
|
||||
if e.pkg != "" {
|
||||
for p := range sbom.Artifacts.PackageCatalog.Enumerate() {
|
||||
for p := range sbom.Artifacts.Packages.Enumerate() {
|
||||
if e.pkg != p.Name {
|
||||
continue
|
||||
}
|
||||
@@ -240,7 +240,7 @@ func Test_decode(t *testing.T) {
|
||||
if e.relation != "" {
|
||||
foundRelation := false
|
||||
for _, r := range sbom.Relationships {
|
||||
p := sbom.Artifacts.PackageCatalog.Package(r.To.ID())
|
||||
p := sbom.Artifacts.Packages.Package(r.To.ID())
|
||||
if e.relation == p.Name {
|
||||
foundRelation = true
|
||||
break
|
||||
|
||||
@@ -4,6 +4,8 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/version"
|
||||
@@ -11,7 +13,6 @@ import (
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
|
||||
@@ -24,7 +25,7 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
|
||||
cdxBOM.SerialNumber = uuid.New().URN()
|
||||
cdxBOM.Metadata = toBomDescriptor(internal.ApplicationName, versionInfo.Version, s.Source)
|
||||
|
||||
packages := s.Artifacts.PackageCatalog.Sorted()
|
||||
packages := s.Artifacts.Packages.Sorted()
|
||||
components := make([]cyclonedx.Component, len(packages))
|
||||
for i, p := range packages {
|
||||
components[i] = encodeComponent(p)
|
||||
@@ -74,9 +75,10 @@ func toOSComponent(distro *linux.Release) []cyclonedx.Component {
|
||||
if len(*eRefs) == 0 {
|
||||
eRefs = nil
|
||||
}
|
||||
props := getCycloneDXProperties(*distro)
|
||||
if len(*props) == 0 {
|
||||
props = nil
|
||||
props := encodeProperties(distro, "syft:distro")
|
||||
var properties *[]cyclonedx.Property
|
||||
if len(props) > 0 {
|
||||
properties = &props
|
||||
}
|
||||
return []cyclonedx.Component{
|
||||
{
|
||||
@@ -93,7 +95,7 @@ func toOSComponent(distro *linux.Release) []cyclonedx.Component {
|
||||
// TODO should we add a PURL?
|
||||
CPE: distro.CPEName,
|
||||
ExternalReferences: eRefs,
|
||||
Properties: props,
|
||||
Properties: properties,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -153,7 +155,7 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc
|
||||
|
||||
func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component {
|
||||
switch srcMetadata.Scheme {
|
||||
case source.ImageScheme:
|
||||
case source.ImageType:
|
||||
bomRef, err := artifact.IDByHash(srcMetadata.ImageMetadata.ID)
|
||||
if err != nil {
|
||||
log.Warnf("unable to get fingerprint of image metadata=%s: %+v", srcMetadata.ImageMetadata.ID, err)
|
||||
@@ -164,7 +166,7 @@ func toBomDescriptorComponent(srcMetadata source.Metadata) *cyclonedx.Component
|
||||
Name: srcMetadata.ImageMetadata.UserInput,
|
||||
Version: srcMetadata.ImageMetadata.ManifestDigest,
|
||||
}
|
||||
case source.DirectoryScheme, source.FileScheme:
|
||||
case source.DirectoryType, source.FileType:
|
||||
bomRef, err := artifact.IDByHash(srcMetadata.Path)
|
||||
if err != nil {
|
||||
log.Warnf("unable to get fingerprint of source metadata path=%s: %+v", srcMetadata.Path, err)
|
||||
|
||||
@@ -1,87 +1,21 @@
|
||||
package cyclonedxhelpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
|
||||
"github.com/anchore/syft/internal/formats/common"
|
||||
)
|
||||
|
||||
func encodeProperties(p pkg.Package) *[]cyclonedx.Property {
|
||||
props := []cyclonedx.Property{}
|
||||
props = append(props, *getCycloneDXProperties(p)...)
|
||||
if len(p.Locations) > 0 {
|
||||
for _, l := range p.Locations {
|
||||
props = append(props, *getCycloneDXProperties(l.Coordinates)...)
|
||||
}
|
||||
}
|
||||
if hasMetadata(p) {
|
||||
props = append(props, *getCycloneDXProperties(p.Metadata)...)
|
||||
}
|
||||
if len(props) > 0 {
|
||||
return &props
|
||||
}
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
CycloneDXFields = common.RequiredTag("cyclonedx")
|
||||
)
|
||||
|
||||
func getCycloneDXProperties(m interface{}) *[]cyclonedx.Property {
|
||||
props := []cyclonedx.Property{}
|
||||
structValue := reflect.ValueOf(m)
|
||||
if structValue.Kind() != reflect.Struct {
|
||||
return &props
|
||||
func encodeProperties(obj interface{}, prefix string) (out []cyclonedx.Property) {
|
||||
for _, p := range common.Sorted(common.Encode(obj, prefix, CycloneDXFields)) {
|
||||
out = append(out, cyclonedx.Property{
|
||||
Name: p.Name,
|
||||
Value: p.Value,
|
||||
})
|
||||
}
|
||||
structType := structValue.Type()
|
||||
for i := 0; i < structValue.NumField(); i++ {
|
||||
if name, value := getCycloneDXPropertyName(structType.Field(i)), getCycloneDXPropertyValue(structValue.Field(i)); name != "" && value != "" {
|
||||
// In the case of the value is a struct and has cyclonedx tag with name "-"
|
||||
// call the getCycloneDXProperties recursively.
|
||||
if name == "-" && reflect.ValueOf(value).Kind() == reflect.Struct {
|
||||
props = append(props, *getCycloneDXProperties(value)...)
|
||||
} else if reflect.ValueOf(value).Kind() == reflect.String {
|
||||
props = append(props, cyclonedx.Property{
|
||||
Name: name,
|
||||
Value: fmt.Sprint(value),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return &props
|
||||
}
|
||||
|
||||
func getCycloneDXPropertyName(field reflect.StructField) string {
|
||||
if value, exists := field.Tag.Lookup("cyclonedx"); exists {
|
||||
return value
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getCycloneDXPropertyValue(field reflect.Value) interface{} {
|
||||
if field.IsZero() {
|
||||
return ""
|
||||
}
|
||||
switch field.Kind() {
|
||||
case reflect.String, reflect.Bool, reflect.Int, reflect.Float32, reflect.Float64, reflect.Complex128, reflect.Complex64:
|
||||
if field.CanInterface() {
|
||||
return fmt.Sprint(field.Interface())
|
||||
}
|
||||
return ""
|
||||
case reflect.Struct:
|
||||
if field.CanInterface() {
|
||||
return field.Interface()
|
||||
}
|
||||
return ""
|
||||
case reflect.Ptr:
|
||||
return getCycloneDXPropertyValue(reflect.Indirect(field))
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func findPropertyValue(c *cyclonedx.Component, name string) string {
|
||||
for _, p := range *c.Properties {
|
||||
if p.Name == name {
|
||||
return p.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,322 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
// FieldName return a flag to indicate this is a valid field and a name to use
|
||||
type FieldName func(field reflect.StructField) (string, bool)
|
||||
|
||||
// OptionalTag given a tag name, will return the defined tag or fall back to lower camel case field name
|
||||
func OptionalTag(tag string) FieldName {
|
||||
return func(f reflect.StructField) (string, bool) {
|
||||
if n, ok := f.Tag.Lookup(tag); ok {
|
||||
return n, true
|
||||
}
|
||||
return lowerFirst(f.Name), true
|
||||
}
|
||||
}
|
||||
|
||||
// TrimOmitempty trims `,omitempty` from the name
|
||||
func TrimOmitempty(fn FieldName) FieldName {
|
||||
return func(f reflect.StructField) (string, bool) {
|
||||
if v, ok := fn(f); ok {
|
||||
return strings.TrimSuffix(v, ",omitempty"), true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// RequiredTag based on the given tag, only use a field if present
|
||||
func RequiredTag(tag string) FieldName {
|
||||
return func(f reflect.StructField) (string, bool) {
|
||||
if n, ok := f.Tag.Lookup(tag); ok {
|
||||
return n, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
// OptionalJSONTag uses field names defined in json tags, if available
|
||||
OptionalJSONTag = TrimOmitempty(OptionalTag("json"))
|
||||
)
|
||||
|
||||
// lowerFirst converts the first character of the string to lower case
|
||||
func lowerFirst(s string) string {
|
||||
return strings.ToLower(s[0:1]) + s[1:]
|
||||
}
|
||||
|
||||
// Encode recursively encodes the object's properties as an ordered set of NameValue pairs
|
||||
func Encode(obj interface{}, prefix string, fn FieldName) map[string]string {
|
||||
if obj == nil {
|
||||
return nil
|
||||
}
|
||||
props := map[string]string{}
|
||||
encode(props, reflect.ValueOf(obj), prefix, fn)
|
||||
return props
|
||||
}
|
||||
|
||||
// NameValue a simple type to store stringified name/value pairs
|
||||
type NameValue struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
// Sorted returns a sorted set of NameValue pairs
|
||||
func Sorted(values map[string]string) (out []NameValue) {
|
||||
var keys []string
|
||||
for k := range values {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
out = append(out, NameValue{
|
||||
Name: k,
|
||||
Value: values[k],
|
||||
})
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func encode(out map[string]string, value reflect.Value, prefix string, fn FieldName) {
|
||||
if !value.IsValid() || value.Type() == nil {
|
||||
return
|
||||
}
|
||||
|
||||
typ := value.Type()
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
if value.IsNil() {
|
||||
return
|
||||
}
|
||||
value = value.Elem()
|
||||
encode(out, value, prefix, fn)
|
||||
case reflect.String:
|
||||
v := value.String()
|
||||
if v != "" {
|
||||
out[prefix] = v
|
||||
}
|
||||
case reflect.Bool:
|
||||
v := value.Bool()
|
||||
out[prefix] = strconv.FormatBool(v)
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
v := value.Int()
|
||||
out[prefix] = strconv.FormatInt(v, 10)
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
v := value.Uint()
|
||||
out[prefix] = strconv.FormatUint(v, 10)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
v := value.Float()
|
||||
out[prefix] = fmt.Sprintf("%f", v)
|
||||
case reflect.Array, reflect.Slice:
|
||||
for idx := 0; idx < value.Len(); idx++ {
|
||||
encode(out, value.Index(idx), fmt.Sprintf("%s:%d", prefix, idx), fn)
|
||||
}
|
||||
case reflect.Struct:
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
pv := value.Field(i)
|
||||
f := typ.Field(i)
|
||||
name, ok := fieldName(f, prefix, fn)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
encode(out, pv, name, fn)
|
||||
}
|
||||
default:
|
||||
log.Warnf("skipping encoding of unsupported property: %s", prefix)
|
||||
}
|
||||
}
|
||||
|
||||
// fieldName gets the name of the field using the provided FieldName function
|
||||
func fieldName(f reflect.StructField, prefix string, fn FieldName) (string, bool) {
|
||||
name, ok := fn(f)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
if name == "" {
|
||||
return prefix, true
|
||||
}
|
||||
if prefix != "" {
|
||||
name = fmt.Sprintf("%s:%s", prefix, name)
|
||||
}
|
||||
return name, true
|
||||
}
|
||||
|
||||
// Decode based on the given type, applies all values to hydrate a new instance
|
||||
func Decode(typ reflect.Type, values map[string]string, prefix string, fn FieldName) interface{} {
|
||||
isPtr := false
|
||||
for typ.Kind() == reflect.Ptr {
|
||||
typ = typ.Elem()
|
||||
isPtr = true
|
||||
}
|
||||
|
||||
isSlice := false
|
||||
if typ.Kind() == reflect.Slice {
|
||||
typ = reflect.PtrTo(typ)
|
||||
isSlice = true
|
||||
}
|
||||
|
||||
v := reflect.New(typ)
|
||||
|
||||
decode(values, v, prefix, fn)
|
||||
|
||||
switch {
|
||||
case isSlice && isPtr:
|
||||
return v.Elem().Interface()
|
||||
case isSlice:
|
||||
return PtrToStruct(v.Elem().Interface())
|
||||
case isPtr:
|
||||
return v.Interface()
|
||||
}
|
||||
return v.Elem().Interface()
|
||||
}
|
||||
|
||||
// DecodeInto decodes all values to hydrate the given object instance
|
||||
func DecodeInto(obj interface{}, values map[string]string, prefix string, fn FieldName) {
|
||||
value := reflect.ValueOf(obj)
|
||||
|
||||
for value.Type().Kind() == reflect.Ptr {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
decode(values, value, prefix, fn)
|
||||
}
|
||||
|
||||
// nolint: funlen, gocognit, gocyclo
|
||||
func decode(vals map[string]string, value reflect.Value, prefix string, fn FieldName) bool {
|
||||
if !value.IsValid() || value.Type() == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
typ := value.Type()
|
||||
|
||||
incoming, valid := vals[prefix]
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
t := typ.Elem()
|
||||
v := value
|
||||
if v.IsNil() {
|
||||
v = reflect.New(t)
|
||||
}
|
||||
if decode(vals, v.Elem(), prefix, fn) && value.CanSet() {
|
||||
o := v.Interface()
|
||||
log.Infof("%v", o)
|
||||
value.Set(v)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case reflect.String:
|
||||
if valid {
|
||||
value.SetString(incoming)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case reflect.Bool:
|
||||
if !valid {
|
||||
return false
|
||||
}
|
||||
if b, err := strconv.ParseBool(incoming); err == nil {
|
||||
value.SetBool(b)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if !valid {
|
||||
return false
|
||||
}
|
||||
if i, err := strconv.ParseInt(incoming, 10, 64); err == nil {
|
||||
value.SetInt(i)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
if !valid {
|
||||
return false
|
||||
}
|
||||
if i, err := strconv.ParseUint(incoming, 10, 64); err == nil {
|
||||
value.SetUint(i)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case reflect.Float32, reflect.Float64:
|
||||
if !valid {
|
||||
return false
|
||||
}
|
||||
if i, err := strconv.ParseFloat(incoming, 64); err == nil {
|
||||
value.SetFloat(i)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
values := false
|
||||
t := typ.Elem()
|
||||
slice := reflect.MakeSlice(typ, 0, 0)
|
||||
for idx := 0; ; idx++ {
|
||||
// test for index
|
||||
str := fmt.Sprintf("%s:%d", prefix, idx)
|
||||
// create new placeholder and decode values
|
||||
newType := t
|
||||
if t.Kind() == reflect.Ptr {
|
||||
newType = t.Elem()
|
||||
}
|
||||
v := reflect.New(newType)
|
||||
if decode(vals, v.Elem(), str, fn) {
|
||||
// append to slice
|
||||
if t.Kind() != reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
slice = reflect.Append(slice, v)
|
||||
values = true
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
if values {
|
||||
value.Set(slice)
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case reflect.Struct:
|
||||
values := false
|
||||
for i := 0; i < typ.NumField(); i++ {
|
||||
f := typ.Field(i)
|
||||
v := value.Field(i)
|
||||
|
||||
name, ok := fieldName(f, prefix, fn)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if decode(vals, v, name, fn) {
|
||||
values = true
|
||||
}
|
||||
}
|
||||
return values
|
||||
default:
|
||||
log.Warnf("unable to set field: %s", prefix)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func PtrToStruct(ptr interface{}) interface{} {
|
||||
v := reflect.ValueOf(ptr)
|
||||
if v.IsZero() {
|
||||
return nil
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Ptr:
|
||||
return PtrToStruct(v.Elem().Interface())
|
||||
case reflect.Interface:
|
||||
return PtrToStruct(v.Elem().Interface())
|
||||
}
|
||||
return v.Interface()
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type T1 struct {
|
||||
Name string
|
||||
Val int
|
||||
ValU uint
|
||||
Flag bool `json:"bool_flag"`
|
||||
Float float64
|
||||
T2 T2
|
||||
T2Ptr *T2
|
||||
T2Arr []T2
|
||||
T2PtrArr []*T2
|
||||
StrArr []string
|
||||
IntArr []int
|
||||
FloatArr []float64
|
||||
BoolArr []bool
|
||||
T3Arr []T3
|
||||
}
|
||||
|
||||
type T2 struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
type T3 struct {
|
||||
T4Arr []T4
|
||||
}
|
||||
|
||||
type T4 struct {
|
||||
Typ string
|
||||
IntPtr *int
|
||||
}
|
||||
|
||||
func Test_EncodeDecodeCycle(t *testing.T) {
|
||||
val := 99
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value interface{}
|
||||
}{
|
||||
{
|
||||
name: "all values",
|
||||
value: T1{
|
||||
Name: "name",
|
||||
Val: 10,
|
||||
ValU: 16,
|
||||
Flag: true,
|
||||
Float: 1.2,
|
||||
T2: T2{
|
||||
Name: "embedded t2",
|
||||
},
|
||||
T2Ptr: &T2{
|
||||
"t2 ptr",
|
||||
},
|
||||
T2Arr: []T2{
|
||||
{"t2 elem 0"},
|
||||
{"t2 elem 1"},
|
||||
},
|
||||
T2PtrArr: []*T2{
|
||||
{"t2 ptr v1"},
|
||||
{"t2 ptr v2"},
|
||||
},
|
||||
StrArr: []string{"s 1", "s 2", "s 3"},
|
||||
IntArr: []int{9, 12, -1},
|
||||
FloatArr: []float64{-23.99, 15.234321, 39912342314},
|
||||
BoolArr: []bool{false, true, true, true, false},
|
||||
T3Arr: []T3{
|
||||
{
|
||||
T4Arr: []T4{
|
||||
{
|
||||
Typ: "t4 nested typ 1",
|
||||
},
|
||||
{
|
||||
Typ: "t4 nested typ 2",
|
||||
IntPtr: &val,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nil values",
|
||||
value: T1{
|
||||
Name: "t1 test",
|
||||
Val: 0,
|
||||
ValU: 0,
|
||||
Flag: false,
|
||||
Float: 0,
|
||||
T2: T2{},
|
||||
T2Ptr: nil,
|
||||
T2Arr: nil,
|
||||
T2PtrArr: nil,
|
||||
StrArr: nil,
|
||||
IntArr: nil,
|
||||
FloatArr: nil,
|
||||
BoolArr: nil,
|
||||
T3Arr: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "array values",
|
||||
value: []T2{
|
||||
{"t2 elem 0"},
|
||||
{"t2 elem 1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "array ptr",
|
||||
value: &[]T2{
|
||||
{"t2 elem 0"},
|
||||
{"t2 elem 1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
vals := Encode(test.value, "props", OptionalJSONTag)
|
||||
|
||||
typ := reflect.TypeOf(test.value)
|
||||
|
||||
if typ.Kind() != reflect.Slice && typ.Kind() != reflect.Ptr {
|
||||
assert.NotEmpty(t, vals["props:bool_flag"])
|
||||
|
||||
t2 := T1{}
|
||||
DecodeInto(&t2, vals, "props", OptionalJSONTag)
|
||||
|
||||
assert.EqualValues(t, test.value, t2)
|
||||
}
|
||||
|
||||
t3 := Decode(typ, vals, "props", OptionalJSONTag)
|
||||
|
||||
assert.EqualValues(t, test.value, t3)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
|
||||
func DocumentName(srcMetadata source.Metadata) (string, error) {
|
||||
switch srcMetadata.Scheme {
|
||||
case source.ImageScheme:
|
||||
case source.ImageType:
|
||||
return cleanName(srcMetadata.ImageMetadata.UserInput), nil
|
||||
case source.DirectoryScheme, source.FileScheme:
|
||||
case source.DirectoryType, source.FileType:
|
||||
return cleanName(srcMetadata.Path), nil
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
|
||||
func Test_DocumentName(t *testing.T) {
|
||||
allSchemes := strset.New()
|
||||
for _, s := range source.AllSchemes {
|
||||
for _, s := range source.AllTypes {
|
||||
allSchemes.Add(string(s))
|
||||
}
|
||||
testedSchemes := strset.New()
|
||||
@@ -29,7 +29,7 @@ func Test_DocumentName(t *testing.T) {
|
||||
name: "image",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.ImageScheme,
|
||||
Scheme: source.ImageType,
|
||||
ImageMetadata: source.ImageMetadata{
|
||||
UserInput: "image-repo/name:tag",
|
||||
ID: "id",
|
||||
@@ -42,7 +42,7 @@ func Test_DocumentName(t *testing.T) {
|
||||
name: "directory",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.DirectoryScheme,
|
||||
Scheme: source.DirectoryType,
|
||||
Path: "some/path/to/place",
|
||||
},
|
||||
expected: "some/path/to/place",
|
||||
@@ -51,7 +51,7 @@ func Test_DocumentName(t *testing.T) {
|
||||
name: "file",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.FileScheme,
|
||||
Scheme: source.FileType,
|
||||
Path: "some/path/to/place",
|
||||
},
|
||||
expected: "some/path/to/place",
|
||||
|
||||
@@ -21,11 +21,11 @@ func DocumentNameAndNamespace(srcMetadata source.Metadata) (string, string, erro
|
||||
func DocumentNamespace(name string, srcMetadata source.Metadata) string {
|
||||
input := "unknown-source-type"
|
||||
switch srcMetadata.Scheme {
|
||||
case source.ImageScheme:
|
||||
case source.ImageType:
|
||||
input = "image"
|
||||
case source.DirectoryScheme:
|
||||
case source.DirectoryType:
|
||||
input = "dir"
|
||||
case source.FileScheme:
|
||||
case source.FileType:
|
||||
input = "file"
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
|
||||
func Test_documentNamespace(t *testing.T) {
|
||||
allSchemes := strset.New()
|
||||
for _, s := range source.AllSchemes {
|
||||
for _, s := range source.AllTypes {
|
||||
allSchemes.Add(string(s))
|
||||
}
|
||||
testedSchemes := strset.New()
|
||||
@@ -27,7 +27,7 @@ func Test_documentNamespace(t *testing.T) {
|
||||
name: "image",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.ImageScheme,
|
||||
Scheme: source.ImageType,
|
||||
ImageMetadata: source.ImageMetadata{
|
||||
UserInput: "image-repo/name:tag",
|
||||
ID: "id",
|
||||
@@ -40,7 +40,7 @@ func Test_documentNamespace(t *testing.T) {
|
||||
name: "directory",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.DirectoryScheme,
|
||||
Scheme: source.DirectoryType,
|
||||
Path: "some/path/to/place",
|
||||
},
|
||||
expected: "https://anchore.com/syft/dir/my-name-",
|
||||
@@ -49,7 +49,7 @@ func Test_documentNamespace(t *testing.T) {
|
||||
name: "file",
|
||||
inputName: "my-name",
|
||||
srcMetadata: source.Metadata{
|
||||
Scheme: source.FileScheme,
|
||||
Scheme: source.FileType,
|
||||
Path: "some/path/to/place",
|
||||
},
|
||||
expected: "https://anchore.com/syft/file/my-name-",
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
@@ -10,7 +11,7 @@ func ExternalRefs(p pkg.Package) (externalRefs []ExternalRef) {
|
||||
for _, c := range p.CPEs {
|
||||
externalRefs = append(externalRefs, ExternalRef{
|
||||
ReferenceCategory: SecurityReferenceCategory,
|
||||
ReferenceLocator: pkg.CPEString(c),
|
||||
ReferenceLocator: cpe.String(c),
|
||||
ReferenceType: Cpe23ExternalRefType,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -8,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func Test_ExternalRefs(t *testing.T) {
|
||||
testCPE := pkg.MustCPE("cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*")
|
||||
testCPE := cpe.Must("cpe:2.3:a:name:name:3.2:*:*:*:*:*:*:*")
|
||||
tests := []struct {
|
||||
name string
|
||||
input pkg.Package
|
||||
@@ -17,7 +18,7 @@ func Test_ExternalRefs(t *testing.T) {
|
||||
{
|
||||
name: "cpe + purl",
|
||||
input: pkg.Package{
|
||||
CPEs: []pkg.CPE{
|
||||
CPEs: []cpe.CPE{
|
||||
testCPE,
|
||||
},
|
||||
PURL: "a-purl",
|
||||
@@ -25,7 +26,7 @@ func Test_ExternalRefs(t *testing.T) {
|
||||
expected: []ExternalRef{
|
||||
{
|
||||
ReferenceCategory: SecurityReferenceCategory,
|
||||
ReferenceLocator: pkg.CPEString(testCPE),
|
||||
ReferenceLocator: cpe.String(testCPE),
|
||||
ReferenceType: Cpe23ExternalRefType,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -18,9 +18,9 @@ func Test_SourceInfo(t *testing.T) {
|
||||
name: "locations are captured",
|
||||
input: pkg.Package{
|
||||
// note: no type given
|
||||
Locations: []source.Location{
|
||||
source.NewVirtualLocation("/a-place", "/b-place"),
|
||||
source.NewVirtualLocation("/c-place", "/d-place"),
|
||||
Locations: []file.Location{
|
||||
file.NewVirtualLocation("/a-place", "/b-place"),
|
||||
file.NewVirtualLocation("/c-place", "/d-place"),
|
||||
},
|
||||
},
|
||||
expected: []string{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -13,7 +14,6 @@ import (
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func ToSyftModel(doc *spdx.Document2_2) (*sbom.SBOM, error) {
|
||||
@@ -21,9 +21,9 @@ func ToSyftModel(doc *spdx.Document2_2) (*sbom.SBOM, error) {
|
||||
|
||||
s := &sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
PackageCatalog: pkg.NewCatalog(),
|
||||
FileMetadata: map[source.Coordinates]source.FileMetadata{},
|
||||
FileDigests: map[source.Coordinates][]file.Digest{},
|
||||
Packages: pkg.NewCollection(),
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{},
|
||||
FileDigests: map[file.Coordinates][]file.Digest{},
|
||||
LinuxDistribution: findLinuxReleaseByPURL(doc),
|
||||
},
|
||||
}
|
||||
@@ -74,7 +74,7 @@ func collectSyftPackages(s *sbom.SBOM, spdxIDMap map[string]interface{}, doc *sp
|
||||
for _, p := range doc.Packages {
|
||||
syftPkg := toSyftPackage(p)
|
||||
spdxIDMap[string(p.PackageSPDXIdentifier)] = syftPkg
|
||||
s.Artifacts.PackageCatalog.Add(*syftPkg)
|
||||
s.Artifacts.Packages.Add(*syftPkg)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ func toFileDigests(f *spdx.File2_2) (digests []file.Digest) {
|
||||
return digests
|
||||
}
|
||||
|
||||
func toFileMetadata(f *spdx.File2_2) (meta source.FileMetadata) {
|
||||
func toFileMetadata(f *spdx.File2_2) (meta file.Metadata) {
|
||||
// FIXME Syft is currently lossy due to the SPDX 2.2.1 spec not supporting arbitrary mimetypes
|
||||
for _, typ := range f.FileType {
|
||||
switch FileType(typ) {
|
||||
@@ -132,7 +132,7 @@ func toSyftRelationships(spdxIDMap map[string]interface{}, doc *spdx.Document2_2
|
||||
b := spdxIDMap[string(r.RefB.ElementRefID)]
|
||||
from, fromOk := a.(*pkg.Package)
|
||||
toPackage, toPackageOk := b.(*pkg.Package)
|
||||
toLocation, toLocationOk := b.(*source.Location)
|
||||
toLocation, toLocationOk := b.(*file.Location)
|
||||
if !fromOk || !(toPackageOk || toLocationOk) {
|
||||
log.Debugf("unable to find valid relationship mapping from SPDX 2.2 JSON, ignoring: (from: %+v) (to: %+v)", a, b)
|
||||
continue
|
||||
@@ -174,7 +174,7 @@ func toSyftRelationships(spdxIDMap map[string]interface{}, doc *spdx.Document2_2
|
||||
return out
|
||||
}
|
||||
|
||||
func toSyftCoordinates(f *spdx.File2_2) source.Coordinates {
|
||||
func toSyftCoordinates(f *spdx.File2_2) file.Coordinates {
|
||||
const layerIDPrefix = "layerID: "
|
||||
var fileSystemID string
|
||||
if strings.Index(f.FileComment, layerIDPrefix) == 0 {
|
||||
@@ -183,16 +183,16 @@ func toSyftCoordinates(f *spdx.File2_2) source.Coordinates {
|
||||
if strings.Index(string(f.FileSPDXIdentifier), layerIDPrefix) == 0 {
|
||||
fileSystemID = strings.TrimPrefix(string(f.FileSPDXIdentifier), layerIDPrefix)
|
||||
}
|
||||
return source.Coordinates{
|
||||
return file.Coordinates{
|
||||
RealPath: f.FileName,
|
||||
FileSystemID: fileSystemID,
|
||||
}
|
||||
}
|
||||
|
||||
func toSyftLocation(f *spdx.File2_2) *source.Location {
|
||||
return &source.Location{
|
||||
func toSyftLocation(f *spdx.File2_2) *file.Location {
|
||||
return &file.Location{
|
||||
Coordinates: toSyftCoordinates(f),
|
||||
VirtualPath: f.FileName,
|
||||
AccessPath: f.FileName,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,10 +317,10 @@ func findPURLValue(p *spdx.Package2_2) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func extractCPEs(p *spdx.Package2_2) (cpes []pkg.CPE) {
|
||||
func extractCPEs(p *spdx.Package2_2) (cpes []cpe.CPE) {
|
||||
for _, r := range p.PackageExternalReferences {
|
||||
if r.RefType == string(Cpe23ExternalRefType) {
|
||||
cpe, err := pkg.NewCPE(r.Locator)
|
||||
cpe, err := cpe.New(r.Locator)
|
||||
if err != nil {
|
||||
log.Warnf("unable to extract SPDX CPE=%q: %+v", r.Locator, err)
|
||||
continue
|
||||
|
||||
@@ -94,7 +94,7 @@ func TestToSyftModel(t *testing.T) {
|
||||
|
||||
assert.NotNil(t, sbom)
|
||||
|
||||
pkgs := sbom.Artifacts.PackageCatalog.Sorted()
|
||||
pkgs := sbom.Artifacts.Packages.Sorted()
|
||||
|
||||
assert.Len(t, pkgs, 2)
|
||||
|
||||
|
||||
@@ -2,14 +2,16 @@ package testutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
|
||||
"github.com/anchore/go-testutils"
|
||||
"github.com/anchore/stereoscope/pkg/filetree"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
@@ -32,7 +34,7 @@ func FromSnapshot() ImageOption {
|
||||
}
|
||||
}
|
||||
|
||||
func AssertEncoderAgainstGoldenImageSnapshot(t *testing.T, format format.Format, sbom sbom.SBOM, testImage string, updateSnapshot bool, redactors ...redactor) {
|
||||
func AssertEncoderAgainstGoldenImageSnapshot(t *testing.T, format sbom.Format, sbom sbom.SBOM, testImage string, updateSnapshot bool, redactors ...redactor) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
// grab the latest image contents and persist
|
||||
@@ -66,7 +68,7 @@ func AssertEncoderAgainstGoldenImageSnapshot(t *testing.T, format format.Format,
|
||||
}
|
||||
}
|
||||
|
||||
func AssertEncoderAgainstGoldenSnapshot(t *testing.T, format format.Format, sbom sbom.SBOM, updateSnapshot bool, redactors ...redactor) {
|
||||
func AssertEncoderAgainstGoldenSnapshot(t *testing.T, format sbom.Format, sbom sbom.SBOM, updateSnapshot bool, redactors ...redactor) {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
err := format.Encode(&buffer, sbom)
|
||||
@@ -98,7 +100,7 @@ func AssertEncoderAgainstGoldenSnapshot(t *testing.T, format format.Format, sbom
|
||||
|
||||
func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBOM {
|
||||
t.Helper()
|
||||
catalog := pkg.NewCatalog()
|
||||
catalog := pkg.NewCollection()
|
||||
var cfg imageCfg
|
||||
var img *image.Image
|
||||
for _, opt := range options {
|
||||
@@ -122,7 +124,7 @@ func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBO
|
||||
|
||||
return sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
PackageCatalog: catalog,
|
||||
Packages: catalog,
|
||||
LinuxDistribution: &linux.Release{
|
||||
PrettyName: "debian",
|
||||
Name: "debian",
|
||||
@@ -150,7 +152,7 @@ func carriageRedactor(s []byte) []byte {
|
||||
return []byte(msg)
|
||||
}
|
||||
|
||||
func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
|
||||
func populateImageCatalog(catalog pkg.Collection, img *image.Image) {
|
||||
_, ref1, _ := img.SquashedTree().File("/somefile-1.txt", filetree.FollowBasenameLinks)
|
||||
_, ref2, _ := img.SquashedTree().File("/somefile-2.txt", filetree.FollowBasenameLinks)
|
||||
|
||||
@@ -158,8 +160,8 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
Locations: []source.Location{
|
||||
source.NewLocationFromImage(string(ref1.RealPath), *ref1, img),
|
||||
Locations: []file.Location{
|
||||
file.NewLocationFromImage(string(ref1.RealPath), *ref1, img),
|
||||
},
|
||||
Type: pkg.PythonPkg,
|
||||
FoundBy: "the-cataloger-1",
|
||||
@@ -171,15 +173,15 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
|
||||
Version: "1.0.1",
|
||||
},
|
||||
PURL: "a-purl-1",
|
||||
CPEs: []pkg.CPE{
|
||||
pkg.MustCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"),
|
||||
},
|
||||
})
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "package-2",
|
||||
Version: "2.0.1",
|
||||
Locations: []source.Location{
|
||||
source.NewLocationFromImage(string(ref2.RealPath), *ref2, img),
|
||||
Locations: []file.Location{
|
||||
file.NewLocationFromImage(string(ref2.RealPath), *ref2, img),
|
||||
},
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "the-cataloger-2",
|
||||
@@ -189,8 +191,8 @@ func populateImageCatalog(catalog *pkg.Catalog, img *image.Image) {
|
||||
Version: "2.0.1",
|
||||
},
|
||||
PURL: "a-purl-2",
|
||||
CPEs: []pkg.CPE{
|
||||
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -203,7 +205,7 @@ func DirectoryInput(t testing.TB) sbom.SBOM {
|
||||
|
||||
return sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
PackageCatalog: catalog,
|
||||
Packages: catalog,
|
||||
LinuxDistribution: &linux.Release{
|
||||
PrettyName: "debian",
|
||||
Name: "debian",
|
||||
@@ -226,8 +228,8 @@ func DirectoryInput(t testing.TB) sbom.SBOM {
|
||||
}
|
||||
}
|
||||
|
||||
func newDirectoryCatalog() *pkg.Catalog {
|
||||
catalog := pkg.NewCatalog()
|
||||
func newDirectoryCatalog() pkg.Collection {
|
||||
catalog := pkg.NewCollection()
|
||||
|
||||
// populate catalog with test data
|
||||
catalog.Add(pkg.Package{
|
||||
@@ -235,8 +237,8 @@ func newDirectoryCatalog() *pkg.Catalog {
|
||||
Version: "1.0.1",
|
||||
Type: pkg.PythonPkg,
|
||||
FoundBy: "the-cataloger-1",
|
||||
Locations: []source.Location{
|
||||
source.NewLocation("/some/path/pkg1"),
|
||||
Locations: []file.Location{
|
||||
file.NewLocation("/some/path/pkg1"),
|
||||
},
|
||||
Language: pkg.Python,
|
||||
MetadataType: pkg.PythonPackageMetadataType,
|
||||
@@ -251,8 +253,8 @@ func newDirectoryCatalog() *pkg.Catalog {
|
||||
},
|
||||
},
|
||||
PURL: "a-purl-2",
|
||||
CPEs: []pkg.CPE{
|
||||
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
||||
},
|
||||
})
|
||||
catalog.Add(pkg.Package{
|
||||
@@ -260,8 +262,8 @@ func newDirectoryCatalog() *pkg.Catalog {
|
||||
Version: "2.0.1",
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "the-cataloger-2",
|
||||
Locations: []source.Location{
|
||||
source.NewLocation("/some/path/pkg1"),
|
||||
Locations: []file.Location{
|
||||
file.NewLocation("/some/path/pkg1"),
|
||||
},
|
||||
MetadataType: pkg.DpkgMetadataType,
|
||||
Metadata: pkg.DpkgMetadata{
|
||||
@@ -269,8 +271,8 @@ func newDirectoryCatalog() *pkg.Catalog {
|
||||
Version: "2.0.1",
|
||||
},
|
||||
PURL: "a-purl-2",
|
||||
CPEs: []pkg.CPE{
|
||||
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
-62
@@ -1,62 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:e702d78f-9372-4458-ac38-be8e80fc3005" version="1">
|
||||
<metadata>
|
||||
<timestamp>2022-02-10T17:18:31-05:00</timestamp>
|
||||
<tools>
|
||||
<tool>
|
||||
<vendor>anchore</vendor>
|
||||
<name>syft</name>
|
||||
<version>[not provided]</version>
|
||||
</tool>
|
||||
</tools>
|
||||
<component bom-ref="4f9453fd20e0cf80" type="container">
|
||||
<name>user-image-input</name>
|
||||
<version>sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368</version>
|
||||
</component>
|
||||
</metadata>
|
||||
<components>
|
||||
<component type="library">
|
||||
<name>package-1</name>
|
||||
<version>1.0.1</version>
|
||||
<licenses>
|
||||
<license>
|
||||
<id>MIT</id>
|
||||
</license>
|
||||
</licenses>
|
||||
<cpe>cpe:2.3:*:some:package:1:*:*:*:*:*:*:*</cpe>
|
||||
<purl>a-purl-1</purl>
|
||||
<properties>
|
||||
<property name="foundBy">the-cataloger-1</property>
|
||||
<property name="language">python</property>
|
||||
<property name="type">python</property>
|
||||
<property name="metadataType">PythonPackageMetadata</property>
|
||||
<property name="path">/somefile-1.txt</property>
|
||||
<property name="layerID">sha256:41e7295da66c405eb3a4df29188dcf80f622f9304d487033a86d4a22e3f01abe</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component type="library">
|
||||
<name>package-2</name>
|
||||
<version>2.0.1</version>
|
||||
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
||||
<purl>a-purl-2</purl>
|
||||
<properties>
|
||||
<property name="foundBy">the-cataloger-2</property>
|
||||
<property name="type">deb</property>
|
||||
<property name="metadataType">DpkgMetadata</property>
|
||||
<property name="path">/somefile-2.txt</property>
|
||||
<property name="layerID">sha256:68a2c166dcb3acf6b7303e995ca1fe7d794bd3b5852a0b4048f9c96b796086aa</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component type="operating-system">
|
||||
<name>debian</name>
|
||||
<version>1.2.3</version>
|
||||
<description>debian</description>
|
||||
<swid tagId="debian" name="debian" version="1.2.3"></swid>
|
||||
<properties>
|
||||
<property name="prettyName">debian</property>
|
||||
<property name="id">debian</property>
|
||||
<property name="versionID">1.2.3</property>
|
||||
</properties>
|
||||
</component>
|
||||
</components>
|
||||
</bom>
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package cyclonedx13json
|
||||
package cyclonedxjson
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -57,7 +57,7 @@ func Test_decodeJSON(t *testing.T) {
|
||||
split = strings.SplitN(pkg, ":", 2)
|
||||
name = split[0]
|
||||
version = split[1]
|
||||
for p := range bom.Artifacts.PackageCatalog.Enumerate() {
|
||||
for p := range bom.Artifacts.Packages.Enumerate() {
|
||||
if p.Name == name {
|
||||
assert.Equal(t, version, p.Version)
|
||||
continue pkgs
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package cyclonedx13json
|
||||
package cyclonedxjson
|
||||
|
||||
import (
|
||||
"io"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package cyclonedx13json
|
||||
package cyclonedxjson
|
||||
|
||||
import (
|
||||
"flag"
|
||||
+7
-5
@@ -1,14 +1,16 @@
|
||||
package cyclonedx13json
|
||||
package cyclonedxjson
|
||||
|
||||
import (
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/anchore/syft/internal/formats/common/cyclonedxhelpers"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func Format() format.Format {
|
||||
return format.NewFormat(
|
||||
format.CycloneDxJSONOption,
|
||||
const ID sbom.FormatID = "cyclonedx-1-json"
|
||||
|
||||
func Format() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
ID,
|
||||
encoder,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON),
|
||||
+30
-23
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.3",
|
||||
"serialNumber": "urn:uuid:326afa86-5620-4a80-8f2b-7f283b954b9b",
|
||||
"specVersion": "1.4",
|
||||
"serialNumber": "urn:uuid:498e659b-0758-4a7f-816e-91bee18df634",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"timestamp": "2022-02-10T17:19:38-05:00",
|
||||
"timestamp": "2022-03-08T12:30:39Z",
|
||||
"tools": [
|
||||
{
|
||||
"vendor": "anchore",
|
||||
@@ -15,8 +15,7 @@
|
||||
"component": {
|
||||
"bom-ref": "163686ac6e30c752",
|
||||
"type": "file",
|
||||
"name": "/some/path",
|
||||
"version": ""
|
||||
"name": "/some/path"
|
||||
}
|
||||
},
|
||||
"components": [
|
||||
@@ -35,23 +34,23 @@
|
||||
"purl": "a-purl-2",
|
||||
"properties": [
|
||||
{
|
||||
"name": "foundBy",
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "the-cataloger-1"
|
||||
},
|
||||
{
|
||||
"name": "language",
|
||||
"name": "syft:package:language",
|
||||
"value": "python"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "python"
|
||||
},
|
||||
{
|
||||
"name": "metadataType",
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "PythonPackageMetadata"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"name": "syft:package:type",
|
||||
"value": "python"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/some/path/pkg1"
|
||||
}
|
||||
]
|
||||
@@ -64,20 +63,24 @@
|
||||
"purl": "a-purl-2",
|
||||
"properties": [
|
||||
{
|
||||
"name": "foundBy",
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "the-cataloger-2"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "deb"
|
||||
},
|
||||
{
|
||||
"name": "metadataType",
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "DpkgMetadata"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"name": "syft:package:type",
|
||||
"value": "deb"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/some/path/pkg1"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -93,15 +96,19 @@
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"name": "prettyName",
|
||||
"name": "syft:distro:id",
|
||||
"value": "debian"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"name": "syft:distro:idLike:0",
|
||||
"value": "like!"
|
||||
},
|
||||
{
|
||||
"name": "syft:distro:prettyName",
|
||||
"value": "debian"
|
||||
},
|
||||
{
|
||||
"name": "versionID",
|
||||
"name": "syft:distro:versionID",
|
||||
"value": "1.2.3"
|
||||
}
|
||||
]
|
||||
+35
-27
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.3",
|
||||
"serialNumber": "urn:uuid:761a2036-0f25-4787-bf28-f5e9a7d9a0bf",
|
||||
"specVersion": "1.4",
|
||||
"serialNumber": "urn:uuid:342c3d2c-d26e-47b6-94d6-92fbf41da945",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"timestamp": "2022-02-10T17:19:38-05:00",
|
||||
"timestamp": "2022-03-08T12:30:39Z",
|
||||
"tools": [
|
||||
{
|
||||
"vendor": "anchore",
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
],
|
||||
"component": {
|
||||
"bom-ref": "4f9453fd20e0cf80",
|
||||
"bom-ref": "711095b1cdf90cce",
|
||||
"type": "container",
|
||||
"name": "user-image-input",
|
||||
"version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
|
||||
@@ -35,28 +35,28 @@
|
||||
"purl": "a-purl-1",
|
||||
"properties": [
|
||||
{
|
||||
"name": "foundBy",
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "the-cataloger-1"
|
||||
},
|
||||
{
|
||||
"name": "language",
|
||||
"name": "syft:package:language",
|
||||
"value": "python"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "python"
|
||||
},
|
||||
{
|
||||
"name": "metadataType",
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "PythonPackageMetadata"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"value": "/somefile-1.txt"
|
||||
"name": "syft:package:type",
|
||||
"value": "python"
|
||||
},
|
||||
{
|
||||
"name": "layerID",
|
||||
"value": "sha256:41e7295da66c405eb3a4df29188dcf80f622f9304d487033a86d4a22e3f01abe"
|
||||
"name": "syft:location:0:layerID",
|
||||
"value": "sha256:16e64541f2ddf59a90391ce7bb8af90313f7d373f2105d88f3d3267b72e0ebab"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/somefile-1.txt"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -68,24 +68,28 @@
|
||||
"purl": "a-purl-2",
|
||||
"properties": [
|
||||
{
|
||||
"name": "foundBy",
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "the-cataloger-2"
|
||||
},
|
||||
{
|
||||
"name": "type",
|
||||
"value": "deb"
|
||||
},
|
||||
{
|
||||
"name": "metadataType",
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "DpkgMetadata"
|
||||
},
|
||||
{
|
||||
"name": "path",
|
||||
"name": "syft:package:type",
|
||||
"value": "deb"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:layerID",
|
||||
"value": "sha256:de6c235f76ea24c8503ec08891445b5d6a8bdf8249117ed8d8b0b6fb3ebe4f67"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/somefile-2.txt"
|
||||
},
|
||||
{
|
||||
"name": "layerID",
|
||||
"value": "sha256:68a2c166dcb3acf6b7303e995ca1fe7d794bd3b5852a0b4048f9c96b796086aa"
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -101,15 +105,19 @@
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"name": "prettyName",
|
||||
"name": "syft:distro:id",
|
||||
"value": "debian"
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"name": "syft:distro:idLike:0",
|
||||
"value": "like!"
|
||||
},
|
||||
{
|
||||
"name": "syft:distro:prettyName",
|
||||
"value": "debian"
|
||||
},
|
||||
{
|
||||
"name": "versionID",
|
||||
"name": "syft:distro:versionID",
|
||||
"value": "1.2.3"
|
||||
}
|
||||
]
|
||||
BIN
Binary file not shown.
+2
-2
@@ -1,4 +1,4 @@
|
||||
package cyclonedx13xml
|
||||
package cyclonedxxml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -57,7 +57,7 @@ func Test_decodeXML(t *testing.T) {
|
||||
split = strings.SplitN(pkg, ":", 2)
|
||||
name = split[0]
|
||||
version = split[1]
|
||||
for p := range bom.Artifacts.PackageCatalog.Enumerate() {
|
||||
for p := range bom.Artifacts.Packages.Enumerate() {
|
||||
if p.Name == name {
|
||||
assert.Equal(t, version, p.Version)
|
||||
continue pkgs
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package cyclonedx13xml
|
||||
package cyclonedxxml
|
||||
|
||||
import (
|
||||
"io"
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package cyclonedx13xml
|
||||
package cyclonedxxml
|
||||
|
||||
import (
|
||||
"flag"
|
||||
@@ -1,14 +1,16 @@
|
||||
package cyclonedx13xml
|
||||
package cyclonedxxml
|
||||
|
||||
import (
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/anchore/syft/internal/formats/common/cyclonedxhelpers"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func Format() format.Format {
|
||||
return format.NewFormat(
|
||||
format.CycloneDxXMLOption,
|
||||
const ID sbom.FormatID = "cyclonedx-1-xml"
|
||||
|
||||
func Format() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
ID,
|
||||
encoder,
|
||||
cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML),
|
||||
cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML),
|
||||
+16
-15
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.3" serialNumber="urn:uuid:25133adc-01af-46ad-a198-002034e54097" version="1">
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:892f8304-0142-45b1-b411-cade3c53057f" version="1">
|
||||
<metadata>
|
||||
<timestamp>2022-02-10T17:18:31-05:00</timestamp>
|
||||
<timestamp>2022-03-08T12:30:33Z</timestamp>
|
||||
<tools>
|
||||
<tool>
|
||||
<vendor>anchore</vendor>
|
||||
@@ -11,7 +11,6 @@
|
||||
</tools>
|
||||
<component bom-ref="163686ac6e30c752" type="file">
|
||||
<name>/some/path</name>
|
||||
<version></version>
|
||||
</component>
|
||||
</metadata>
|
||||
<components>
|
||||
@@ -26,11 +25,11 @@
|
||||
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
||||
<purl>a-purl-2</purl>
|
||||
<properties>
|
||||
<property name="foundBy">the-cataloger-1</property>
|
||||
<property name="language">python</property>
|
||||
<property name="type">python</property>
|
||||
<property name="metadataType">PythonPackageMetadata</property>
|
||||
<property name="path">/some/path/pkg1</property>
|
||||
<property name="syft:package:foundBy">the-cataloger-1</property>
|
||||
<property name="syft:package:language">python</property>
|
||||
<property name="syft:package:metadataType">PythonPackageMetadata</property>
|
||||
<property name="syft:package:type">python</property>
|
||||
<property name="syft:location:0:path">/some/path/pkg1</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component type="library">
|
||||
@@ -39,10 +38,11 @@
|
||||
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
||||
<purl>a-purl-2</purl>
|
||||
<properties>
|
||||
<property name="foundBy">the-cataloger-2</property>
|
||||
<property name="type">deb</property>
|
||||
<property name="metadataType">DpkgMetadata</property>
|
||||
<property name="path">/some/path/pkg1</property>
|
||||
<property name="syft:package:foundBy">the-cataloger-2</property>
|
||||
<property name="syft:package:metadataType">DpkgMetadata</property>
|
||||
<property name="syft:package:type">deb</property>
|
||||
<property name="syft:location:0:path">/some/path/pkg1</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component type="operating-system">
|
||||
@@ -51,9 +51,10 @@
|
||||
<description>debian</description>
|
||||
<swid tagId="debian" name="debian" version="1.2.3"></swid>
|
||||
<properties>
|
||||
<property name="prettyName">debian</property>
|
||||
<property name="id">debian</property>
|
||||
<property name="versionID">1.2.3</property>
|
||||
<property name="syft:distro:id">debian</property>
|
||||
<property name="syft:distro:idLike:0">like!</property>
|
||||
<property name="syft:distro:prettyName">debian</property>
|
||||
<property name="syft:distro:versionID">1.2.3</property>
|
||||
</properties>
|
||||
</component>
|
||||
</components>
|
||||
@@ -0,0 +1,64 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:5fa94827-eb85-4f32-a62d-76fb6e89a2dd" version="1">
|
||||
<metadata>
|
||||
<timestamp>2022-03-08T12:30:33Z</timestamp>
|
||||
<tools>
|
||||
<tool>
|
||||
<vendor>anchore</vendor>
|
||||
<name>syft</name>
|
||||
<version>[not provided]</version>
|
||||
</tool>
|
||||
</tools>
|
||||
<component bom-ref="711095b1cdf90cce" type="container">
|
||||
<name>user-image-input</name>
|
||||
<version>sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368</version>
|
||||
</component>
|
||||
</metadata>
|
||||
<components>
|
||||
<component type="library">
|
||||
<name>package-1</name>
|
||||
<version>1.0.1</version>
|
||||
<licenses>
|
||||
<license>
|
||||
<id>MIT</id>
|
||||
</license>
|
||||
</licenses>
|
||||
<cpe>cpe:2.3:*:some:package:1:*:*:*:*:*:*:*</cpe>
|
||||
<purl>a-purl-1</purl>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">the-cataloger-1</property>
|
||||
<property name="syft:package:language">python</property>
|
||||
<property name="syft:package:metadataType">PythonPackageMetadata</property>
|
||||
<property name="syft:package:type">python</property>
|
||||
<property name="syft:location:0:layerID">sha256:16e64541f2ddf59a90391ce7bb8af90313f7d373f2105d88f3d3267b72e0ebab</property>
|
||||
<property name="syft:location:0:path">/somefile-1.txt</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component type="library">
|
||||
<name>package-2</name>
|
||||
<version>2.0.1</version>
|
||||
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
|
||||
<purl>a-purl-2</purl>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">the-cataloger-2</property>
|
||||
<property name="syft:package:metadataType">DpkgMetadata</property>
|
||||
<property name="syft:package:type">deb</property>
|
||||
<property name="syft:location:0:layerID">sha256:de6c235f76ea24c8503ec08891445b5d6a8bdf8249117ed8d8b0b6fb3ebe4f67</property>
|
||||
<property name="syft:location:0:path">/somefile-2.txt</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component type="operating-system">
|
||||
<name>debian</name>
|
||||
<version>1.2.3</version>
|
||||
<description>debian</description>
|
||||
<swid tagId="debian" name="debian" version="1.2.3"></swid>
|
||||
<properties>
|
||||
<property name="syft:distro:id">debian</property>
|
||||
<property name="syft:distro:idLike:0">like!</property>
|
||||
<property name="syft:distro:prettyName">debian</property>
|
||||
<property name="syft:distro:versionID">1.2.3</property>
|
||||
</properties>
|
||||
</component>
|
||||
</components>
|
||||
</bom>
|
||||
BIN
Binary file not shown.
@@ -1,46 +0,0 @@
|
||||
package formats
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/anchore/syft/internal/formats/cyclonedx13json"
|
||||
"github.com/anchore/syft/internal/formats/cyclonedx13xml"
|
||||
"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/internal/formats/text"
|
||||
"github.com/anchore/syft/syft/format"
|
||||
)
|
||||
|
||||
// TODO: eventually this is the source of truth for all formatters
|
||||
func All() []format.Format {
|
||||
return []format.Format{
|
||||
syftjson.Format(),
|
||||
table.Format(),
|
||||
cyclonedx13xml.Format(),
|
||||
cyclonedx13json.Format(),
|
||||
spdx22json.Format(),
|
||||
spdx22tagvalue.Format(),
|
||||
text.Format(),
|
||||
}
|
||||
}
|
||||
|
||||
func Identify(by []byte) (*format.Format, error) {
|
||||
for _, f := range All() {
|
||||
if err := f.Validate(bytes.NewReader(by)); err != nil {
|
||||
continue
|
||||
}
|
||||
return &f, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func ByOption(option format.Option) *format.Format {
|
||||
for _, f := range All() {
|
||||
if f.Option == option {
|
||||
return &f
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package formats
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/format"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIdentify(t *testing.T) {
|
||||
tests := []struct {
|
||||
fixture string
|
||||
expected format.Option
|
||||
}{
|
||||
{
|
||||
fixture: "test-fixtures/alpine-syft.json",
|
||||
expected: format.JSONOption,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixture, func(t *testing.T) {
|
||||
f, err := os.Open(test.fixture)
|
||||
assert.NoError(t, err)
|
||||
by, err := io.ReadAll(f)
|
||||
assert.NoError(t, err)
|
||||
frmt, err := Identify(by)
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, frmt)
|
||||
assert.Equal(t, test.expected, frmt.Option)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -71,11 +71,11 @@ func TestSPDXJSONDecoder(t *testing.T) {
|
||||
}
|
||||
|
||||
if test.packages != nil {
|
||||
assert.Equal(t, sbom.Artifacts.PackageCatalog.PackageCount(), len(test.packages))
|
||||
assert.Equal(t, sbom.Artifacts.Packages.Size(), len(test.packages))
|
||||
|
||||
packages:
|
||||
for _, pkgName := range test.packages {
|
||||
for _, p := range sbom.Artifacts.PackageCatalog.Sorted() {
|
||||
for _, p := range sbom.Artifacts.Packages.Sorted() {
|
||||
if p.Name == pkgName {
|
||||
continue packages
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package spdx22json
|
||||
|
||||
import "github.com/anchore/syft/syft/format"
|
||||
import (
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
const ID sbom.FormatID = "spdx-2-json"
|
||||
|
||||
// note: this format is LOSSY relative to the syftjson format
|
||||
func Format() format.Format {
|
||||
return format.NewFormat(
|
||||
format.SPDXJSONOption,
|
||||
func Format() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
ID,
|
||||
encoder,
|
||||
decoder,
|
||||
validator,
|
||||
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// toFormatModel creates and populates a new JSON document struct that follows the SPDX 2.2 spec from the given cataloging results.
|
||||
@@ -43,13 +42,13 @@ func toFormatModel(s sbom.SBOM) (*model.Document, error) {
|
||||
},
|
||||
DataLicense: "CC0-1.0",
|
||||
DocumentNamespace: namespace,
|
||||
Packages: toPackages(s.Artifacts.PackageCatalog, s.Relationships),
|
||||
Packages: toPackages(s.Artifacts.Packages, s.Relationships),
|
||||
Files: toFiles(s),
|
||||
Relationships: toRelationships(s.Relationships),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toPackages(catalog *pkg.Catalog, relationships []artifact.Relationship) []model.Package {
|
||||
func toPackages(catalog pkg.Collection, relationships []artifact.Relationship) []model.Package {
|
||||
packages := make([]model.Package, 0)
|
||||
|
||||
for _, p := range catalog.Sorted() {
|
||||
@@ -94,7 +93,7 @@ func fileIDsForPackage(packageSpdxID string, relationships []artifact.Relationsh
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := relationship.To.(source.Coordinates); !ok {
|
||||
if _, ok := relationship.To.(file.Coordinates); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -110,7 +109,7 @@ func toFiles(s sbom.SBOM) []model.File {
|
||||
artifacts := s.Artifacts
|
||||
|
||||
for _, coordinates := range sbom.AllCoordinates(s) {
|
||||
var metadata *source.FileMetadata
|
||||
var metadata *file.Metadata
|
||||
if metadataForLocation, exists := artifacts.FileMetadata[coordinates]; exists {
|
||||
metadata = &metadataForLocation
|
||||
}
|
||||
@@ -152,14 +151,20 @@ func toFiles(s sbom.SBOM) []model.File {
|
||||
func toFileChecksums(digests []file.Digest) (checksums []model.Checksum) {
|
||||
for _, digest := range digests {
|
||||
checksums = append(checksums, model.Checksum{
|
||||
Algorithm: digest.Algorithm,
|
||||
Algorithm: toChecksumAlgorithm(digest.Algorithm),
|
||||
ChecksumValue: digest.Value,
|
||||
})
|
||||
}
|
||||
return checksums
|
||||
}
|
||||
|
||||
func toFileTypes(metadata *source.FileMetadata) (ty []string) {
|
||||
func toChecksumAlgorithm(algorithm string) string {
|
||||
// basically, we need an uppercase version of our algorithm:
|
||||
// https://github.com/spdx/spdx-spec/blob/development/v2.2.2/schemas/spdx-schema.json#L165
|
||||
return strings.ToUpper(algorithm)
|
||||
}
|
||||
|
||||
func toFileTypes(metadata *file.Metadata) (ty []string) {
|
||||
if metadata == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
|
||||
"github.com/anchore/syft/internal/formats/common/spdxhelpers"
|
||||
"github.com/anchore/syft/internal/formats/spdx22json/model"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
@@ -19,12 +18,12 @@ func Test_toFileTypes(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
metadata source.FileMetadata
|
||||
metadata file.Metadata
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "application",
|
||||
metadata: source.FileMetadata{
|
||||
metadata: file.Metadata{
|
||||
MIMEType: "application/vnd.unknown",
|
||||
},
|
||||
expected: []string{
|
||||
@@ -33,7 +32,7 @@ func Test_toFileTypes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "archive",
|
||||
metadata: source.FileMetadata{
|
||||
metadata: file.Metadata{
|
||||
MIMEType: "application/zip",
|
||||
},
|
||||
expected: []string{
|
||||
@@ -43,7 +42,7 @@ func Test_toFileTypes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "audio",
|
||||
metadata: source.FileMetadata{
|
||||
metadata: file.Metadata{
|
||||
MIMEType: "audio/ogg",
|
||||
},
|
||||
expected: []string{
|
||||
@@ -52,7 +51,7 @@ func Test_toFileTypes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "video",
|
||||
metadata: source.FileMetadata{
|
||||
metadata: file.Metadata{
|
||||
MIMEType: "video/3gpp",
|
||||
},
|
||||
expected: []string{
|
||||
@@ -61,7 +60,7 @@ func Test_toFileTypes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "text",
|
||||
metadata: source.FileMetadata{
|
||||
metadata: file.Metadata{
|
||||
MIMEType: "text/html",
|
||||
},
|
||||
expected: []string{
|
||||
@@ -70,7 +69,7 @@ func Test_toFileTypes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "image",
|
||||
metadata: source.FileMetadata{
|
||||
metadata: file.Metadata{
|
||||
MIMEType: "image/png",
|
||||
},
|
||||
expected: []string{
|
||||
@@ -79,7 +78,7 @@ func Test_toFileTypes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "binary",
|
||||
metadata: source.FileMetadata{
|
||||
metadata: file.Metadata{
|
||||
MIMEType: "application/x-sharedlib",
|
||||
},
|
||||
expected: []string{
|
||||
@@ -142,7 +141,7 @@ func Test_toFileChecksums(t *testing.T) {
|
||||
name: "has digests",
|
||||
digests: []file.Digest{
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Algorithm: "SHA256",
|
||||
Value: "deadbeefcafe",
|
||||
},
|
||||
{
|
||||
@@ -152,11 +151,11 @@ func Test_toFileChecksums(t *testing.T) {
|
||||
},
|
||||
expected: []model.Checksum{
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Algorithm: "SHA256",
|
||||
ChecksumValue: "deadbeefcafe",
|
||||
},
|
||||
{
|
||||
Algorithm: "md5",
|
||||
Algorithm: "MD5",
|
||||
ChecksumValue: "meh",
|
||||
},
|
||||
},
|
||||
@@ -175,7 +174,7 @@ func Test_fileIDsForPackage(t *testing.T) {
|
||||
Name: "bogus",
|
||||
}
|
||||
|
||||
c := source.Coordinates{
|
||||
c := file.Coordinates{
|
||||
RealPath: "/path",
|
||||
FileSystemID: "nowhere",
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
package spdx22tagvalue
|
||||
|
||||
import "github.com/anchore/syft/syft/format"
|
||||
import (
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
const ID sbom.FormatID = "spdx-2-tag-value"
|
||||
|
||||
// note: this format is LOSSY relative to the syftjson formation, which means that decoding and validation is not supported at this time
|
||||
func Format() format.Format {
|
||||
return format.NewFormat(
|
||||
format.SPDXTagValueOption,
|
||||
func Format() sbom.Format {
|
||||
return sbom.NewFormat(
|
||||
ID,
|
||||
encoder,
|
||||
decoder,
|
||||
validator,
|
||||
|
||||
@@ -85,13 +85,13 @@ func toFormatModel(s sbom.SBOM) (*spdx.Document2_2, error) {
|
||||
// Cardinality: optional, one
|
||||
DocumentComment: "",
|
||||
},
|
||||
Packages: toFormatPackages(s.Artifacts.PackageCatalog),
|
||||
Packages: toFormatPackages(s.Artifacts.Packages),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// packages populates all Package Information from the package Catalog (see https://spdx.github.io/spdx-spec/3-package-information/)
|
||||
// packages populates all Package Information from the package Collection (see https://spdx.github.io/spdx-spec/3-package-information/)
|
||||
// nolint: funlen
|
||||
func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2 {
|
||||
func toFormatPackages(catalog pkg.Collection) map[spdx.ElementID]*spdx.Package2_2 {
|
||||
results := make(map[spdx.ElementID]*spdx.Package2_2)
|
||||
|
||||
for _, p := range catalog.Sorted() {
|
||||
@@ -221,7 +221,7 @@ func toFormatPackages(catalog *pkg.Catalog) map[spdx.ElementID]*spdx.Package2_2
|
||||
|
||||
// 3.17: Copyright Text: copyright notice(s) text, "NONE" or "NOASSERTION"
|
||||
// Cardinality: mandatory, one
|
||||
// Purpose: Identify the copyright holders of the package, as well as any dates present. This will be a free form text field extracted from package information files. The options to populate this field are limited to:
|
||||
// Purpose: IdentifyFormat the copyright holders of the package, as well as any dates present. This will be a free form text field extracted from package information files. The options to populate this field are limited to:
|
||||
//
|
||||
// Any text related to a copyright notice, even if not complete;
|
||||
// NONE if the package contains no copyright information whatsoever; or
|
||||
|
||||
@@ -28,8 +28,8 @@ func TestEncodeDecodeCycle(t *testing.T) {
|
||||
t.Errorf("metadata difference: %+v", d)
|
||||
}
|
||||
|
||||
actualPackages := actualSBOM.Artifacts.PackageCatalog.Sorted()
|
||||
for idx, p := range originalSBOM.Artifacts.PackageCatalog.Sorted() {
|
||||
actualPackages := actualSBOM.Artifacts.Packages.Sorted()
|
||||
for idx, p := range originalSBOM.Artifacts.Packages.Sorted() {
|
||||
if !assert.Equal(t, p.Name, actualPackages[idx].Name) {
|
||||
t.Errorf("different package at idx=%d: %s vs %s", idx, p.Name, actualPackages[idx].Name)
|
||||
continue
|
||||
|
||||
@@ -2,6 +2,7 @@ package syftjson
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@@ -37,14 +38,14 @@ func TestImageEncoder(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
catalog := pkg.NewCatalog()
|
||||
catalog := pkg.NewCollection()
|
||||
|
||||
p1 := pkg.Package{
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
Locations: []source.Location{
|
||||
Locations: []file.Location{
|
||||
{
|
||||
Coordinates: source.Coordinates{
|
||||
Coordinates: file.Coordinates{
|
||||
RealPath: "/a/place/a",
|
||||
},
|
||||
},
|
||||
@@ -60,17 +61,17 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
Files: []pkg.PythonFileRecord{},
|
||||
},
|
||||
PURL: "a-purl-1",
|
||||
CPEs: []pkg.CPE{
|
||||
pkg.MustCPE("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"),
|
||||
},
|
||||
}
|
||||
|
||||
p2 := pkg.Package{
|
||||
Name: "package-2",
|
||||
Version: "2.0.1",
|
||||
Locations: []source.Location{
|
||||
Locations: []file.Location{
|
||||
{
|
||||
Coordinates: source.Coordinates{
|
||||
Coordinates: file.Coordinates{
|
||||
RealPath: "/b/place/b",
|
||||
},
|
||||
},
|
||||
@@ -84,8 +85,8 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
Files: []pkg.DpkgFileRecord{},
|
||||
},
|
||||
PURL: "a-purl-2",
|
||||
CPEs: []pkg.CPE{
|
||||
pkg.MustCPE("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -94,50 +95,50 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
|
||||
s := sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
PackageCatalog: catalog,
|
||||
FileMetadata: map[source.Coordinates]source.FileMetadata{
|
||||
source.NewLocation("/a/place").Coordinates: {
|
||||
Packages: catalog,
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{
|
||||
file.NewLocation("/a/place").Coordinates: {
|
||||
Mode: 0775,
|
||||
Type: "directory",
|
||||
UserID: 0,
|
||||
GroupID: 0,
|
||||
},
|
||||
source.NewLocation("/a/place/a").Coordinates: {
|
||||
file.NewLocation("/a/place/a").Coordinates: {
|
||||
Mode: 0775,
|
||||
Type: "regularFile",
|
||||
UserID: 0,
|
||||
GroupID: 0,
|
||||
},
|
||||
source.NewLocation("/b").Coordinates: {
|
||||
file.NewLocation("/b").Coordinates: {
|
||||
Mode: 0775,
|
||||
Type: "symbolicLink",
|
||||
LinkDestination: "/c",
|
||||
UserID: 0,
|
||||
GroupID: 0,
|
||||
},
|
||||
source.NewLocation("/b/place/b").Coordinates: {
|
||||
file.NewLocation("/b/place/b").Coordinates: {
|
||||
Mode: 0644,
|
||||
Type: "regularFile",
|
||||
UserID: 1,
|
||||
GroupID: 2,
|
||||
},
|
||||
},
|
||||
FileDigests: map[source.Coordinates][]file.Digest{
|
||||
source.NewLocation("/a/place/a").Coordinates: {
|
||||
FileDigests: map[file.Coordinates][]file.Digest{
|
||||
file.NewLocation("/a/place/a").Coordinates: {
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "366a3f5653e34673b875891b021647440d0127c2ef041e3b1a22da2a7d4f3703",
|
||||
},
|
||||
},
|
||||
source.NewLocation("/b/place/b").Coordinates: {
|
||||
file.NewLocation("/b/place/b").Coordinates: {
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "1b3722da2a7d90d033b87581a2a3f12021647445653e34666ef041e3b4f3707c",
|
||||
},
|
||||
},
|
||||
},
|
||||
FileContents: map[source.Coordinates]string{
|
||||
source.NewLocation("/a/place/a").Coordinates: "the-contents",
|
||||
FileContents: map[file.Coordinates]string{
|
||||
file.NewLocation("/a/place/a").Coordinates: "the-contents",
|
||||
},
|
||||
LinuxDistribution: &linux.Release{
|
||||
ID: "redhat",
|
||||
@@ -159,7 +160,7 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
},
|
||||
},
|
||||
Source: source.Metadata{
|
||||
Scheme: source.ImageScheme,
|
||||
Scheme: source.ImageType,
|
||||
ImageMetadata: source.ImageMetadata{
|
||||
UserInput: "user-image-input",
|
||||
ID: "sha256:c2b46b4eb06296933b7cf0722683964e9ecbd93265b9ef6ae9642e3952afbba0",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user