From 28b06dae25a7f6e507658f9e85f668c4903f51dd Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 11:35:57 -0400 Subject: [PATCH 001/106] chore(deps): update CPE dictionary index (#2025) Signed-off-by: GitHub Co-authored-by: wagoodman --- syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json b/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json index a47e39ca6..5e357e2b8 100644 --- a/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json +++ b/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json @@ -807,6 +807,7 @@ "promise-probe": "cpe:2.3:a:promise-probe_project:promise-probe:*:*:*:*:*:node.js:*:*", "promisehelpers": "cpe:2.3:a:yola:promisehelpers:*:*:*:*:*:node.js:*:*", "property-expr": "cpe:2.3:a:property-expr_project:property-expr:*:*:*:*:*:node.js:*:*", + "proxy": "cpe:2.3:a:proxy_project:proxy:*:*:*:*:*:node.js:*:*", "proxy.js": "cpe:2.3:a:proxy.js_project:proxy.js:*:*:*:*:*:node.js:*:*", "ps-kill": "cpe:2.3:a:ps-kill_project:ps-kill:*:*:*:*:*:node.js:*:*", "ps-visitor": "cpe:2.3:a:ps-visitor_project:ps-visitor:*:*:*:*:*:node.js:*:*", @@ -1144,6 +1145,7 @@ "papercrop": "cpe:2.3:a:papercrop_project:papercrop:*:*:*:*:*:ruby:*:*", "paranoid2": "cpe:2.3:a:anjlab:paranoid2:*:*:*:*:*:ruby:*:*", "paratrooper-pingdom": "cpe:2.3:a:tobias_maier:paratrooper-pingdom:*:*:-:*:-:ruby:*:*", + "pdf_info": "cpe:2.3:a:newspaperclub:pdf_info:*:*:*:*:*:ruby:*:*", "pdfkit": "cpe:2.3:a:pdfkit_project:pdfkit:*:*:*:*:*:ruby:*:*", "point-cli": "cpe:2.3:a:point-cli_project:point-cli:*:*:*:*:*:ruby:*:*", "private_address_check": "cpe:2.3:a:private_address_check_project:private_address_check:*:*:*:*:*:ruby:*:*", From c7fe58683da9c84da28db28eb2bf5c4f11697087 Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Mon, 14 Aug 2023 11:36:15 -0400 Subject: [PATCH 002/106] chore(deps): update bootstrap tools to latest versions (#2022) Signed-off-by: GitHub Co-authored-by: spiffcs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index fd7debaa0..17a3c27d4 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ CHRONICLE_CMD = $(TEMP_DIR)/chronicle GLOW_CMD = $(TEMP_DIR)/glow # Tool versions ################################# -GOLANGCILINT_VERSION := v1.54.0 +GOLANGCILINT_VERSION := v1.54.1 GOSIMPORTS_VERSION := v0.3.8 BOUNCER_VERSION := v0.4.0 CHRONICLE_VERSION := v0.7.0 From b3d7ba569b64376cf33df717e3bc40d8375e033b Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 14 Aug 2023 11:37:24 -0400 Subject: [PATCH 003/106] fix: read direct package files when decoding SPDX tag-value (#2014) * fix: read direct package files when decoding SPDX tag-value --------- Signed-off-by: Keith Zantow --- .../common/spdxhelpers/to_syft_model.go | 43 ++++++++++- .../common/spdxhelpers/to_syft_model_test.go | 74 +++++++++++++++++++ syft/formats/spdxtagvalue/decoder_test.go | 60 +++++++++++++++ 3 files changed, 176 insertions(+), 1 deletion(-) diff --git a/syft/formats/common/spdxhelpers/to_syft_model.go b/syft/formats/common/spdxhelpers/to_syft_model.go index c17b6367e..f61f723c8 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model.go +++ b/syft/formats/common/spdxhelpers/to_syft_model.go @@ -286,6 +286,16 @@ func collectSyftPackages(s *sbom.SBOM, spdxIDMap map[string]any, packages []*spd } func collectSyftFiles(s *sbom.SBOM, spdxIDMap map[string]any, doc *spdx.Document) { + for _, p := range doc.Packages { + for _, f := range p.Files { + l := toSyftLocation(f) + spdxIDMap[string(f.FileSPDXIdentifier)] = l + + s.Artifacts.FileMetadata[l.Coordinates] = toFileMetadata(f) + s.Artifacts.FileDigests[l.Coordinates] = toFileDigests(f) + } + } + for _, f := range doc.Files { l := toSyftLocation(f) spdxIDMap[string(f.FileSPDXIdentifier)] = l @@ -332,7 +342,14 @@ func toFileMetadata(f *spdx.File) (meta file.Metadata) { } func toSyftRelationships(spdxIDMap map[string]any, doc *spdx.Document) []artifact.Relationship { - var out []artifact.Relationship + out := collectDocRelationships(spdxIDMap, doc) + + out = append(out, collectPackageFileRelationships(spdxIDMap, doc)...) + + return out +} + +func collectDocRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out []artifact.Relationship) { for _, r := range doc.Relationships { // FIXME what to do with r.RefA.DocumentRefID and r.RefA.SpecialID if r.RefA.DocumentRefID != "" && requireAndTrimPrefix(r.RefA.DocumentRefID, "DocumentRef-") != string(doc.SPDXIdentifier) { @@ -386,6 +403,30 @@ func toSyftRelationships(spdxIDMap map[string]any, doc *spdx.Document) []artifac return out } +// collectPackageFileRelationships add relationships for direct files +func collectPackageFileRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out []artifact.Relationship) { + for _, p := range doc.Packages { + a := spdxIDMap[string(p.PackageSPDXIdentifier)] + from, fromOk := a.(pkg.Package) + if !fromOk { + continue + } + for _, f := range p.Files { + b := spdxIDMap[string(f.FileSPDXIdentifier)] + to, toLocationOk := b.(file.Location) + if !toLocationOk { + continue + } + out = append(out, artifact.Relationship{ + From: from, + To: to, + Type: artifact.ContainsRelationship, + }) + } + } + return out +} + func toSyftCoordinates(f *spdx.File) file.Coordinates { const layerIDPrefix = "layerID: " var fileSystemID string diff --git a/syft/formats/common/spdxhelpers/to_syft_model_test.go b/syft/formats/common/spdxhelpers/to_syft_model_test.go index f6023f662..eb997eb35 100644 --- a/syft/formats/common/spdxhelpers/to_syft_model_test.go +++ b/syft/formats/common/spdxhelpers/to_syft_model_test.go @@ -608,3 +608,77 @@ func Test_purlValue(t *testing.T) { }) } } + +func Test_directPackageFiles(t *testing.T) { + doc := &spdx.Document{ + SPDXVersion: "SPDX-2.3", + Packages: []*spdx.Package{ + { + PackageName: "some-package", + PackageSPDXIdentifier: "1", + PackageVersion: "1.0.5", + Files: []*spdx.File{ + { + FileName: "some-file", + FileSPDXIdentifier: "2", + Checksums: []spdx.Checksum{ + { + Algorithm: "SHA1", + Value: "a8d733c64f9123", + }, + }, + }, + }, + }, + }, + } + + got, err := ToSyftModel(doc) + require.NoError(t, err) + + p := pkg.Package{ + Name: "some-package", + Version: "1.0.5", + MetadataType: pkg.UnknownMetadataType, + } + p.SetID() + f := file.Location{ + LocationData: file.LocationData{ + Coordinates: file.Coordinates{ + RealPath: "some-file", + FileSystemID: "", + }, + VirtualPath: "some-file", + }, + LocationMetadata: file.LocationMetadata{ + Annotations: map[string]string{}, + }, + } + s := &sbom.SBOM{ + Artifacts: sbom.Artifacts{ + Packages: pkg.NewCollection(p), + FileMetadata: map[file.Coordinates]file.Metadata{ + f.Coordinates: {}, + }, + FileDigests: map[file.Coordinates][]file.Digest{ + f.Coordinates: { + { + Algorithm: "sha1", + Value: "a8d733c64f9123", + }, + }, + }, + }, + Relationships: []artifact.Relationship{ + { + From: p, + To: f, + Type: artifact.ContainsRelationship, + }, + }, + Source: source.Description{}, + Descriptor: sbom.Descriptor{}, + } + + require.Equal(t, s, got) +} diff --git a/syft/formats/spdxtagvalue/decoder_test.go b/syft/formats/spdxtagvalue/decoder_test.go index 85f0b01c3..6802e1b5c 100644 --- a/syft/formats/spdxtagvalue/decoder_test.go +++ b/syft/formats/spdxtagvalue/decoder_test.go @@ -2,9 +2,13 @@ package spdxtagvalue import ( "os" + "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/file" ) // TODO: this is a temporary coverage see below @@ -34,3 +38,59 @@ func TestSPDXTagValueDecoder(t *testing.T) { }) } } + +func Test_packageDirectFiles(t *testing.T) { + contents := ` +SPDXVersion: SPDX-2.2 +DataLicense: CC0-1.0 +SPDXID: SPDXRef-DOCUMENT +DocumentName: Some-SBOM +DocumentNamespace: https://example.org/some/namespace +Creator: Organization: Some-organization +Creator: Tool: Some-tool Version: 1.0 +Created: 2021-12-29T17:02:21Z +PackageName: Some-package +PackageVersion: 5.1.2 +SPDXID: SPDXRef-Package-43c51b08-cc7e-406d-8ad9-34aa292d1157 +PackageSupplier: Organization: Some-organization +PackageDownloadLocation: https://example.org/download/location +FilesAnalyzed: true +PackageLicenseInfoFromFiles: NOASSERTION +PackageVerificationCode: 23460C5559C8D4DE3F6504E0E84E844CAC8B1D95 +PackageLicenseConcluded: NOASSERTION +PackageLicenseDeclared: NOASSERTION +PackageCopyrightText: NOASSERTION +PackageChecksum: SHA1: 23460C5559C8D4DE3F6504E0E84E844CAC8B1D95 +FileName: Some-file-name +SPDXID: SPDXRef-99545d55-933d-4e08-9eb5-9d826111cb79 +FileContributor: Some-file-contributor +FileType: BINARY +FileChecksum: SHA1: 23460C5559C8D4DE3F6504E0E84E844CAC8B1D95 +LicenseConcluded: NOASSERTION +LicenseInfoInFile: NOASSERTION +FileCopyrightText: NOASSERTION +` + + s, err := decoder(strings.NewReader(contents)) + require.NoError(t, err) + + pkgs := s.Artifacts.Packages.Sorted() + assert.Len(t, pkgs, 1) + assert.Len(t, s.Artifacts.FileMetadata, 1) + assert.Len(t, s.Relationships, 1) + p := pkgs[0] + r := s.Relationships[0] + f := file.Location{} + for c := range s.Artifacts.FileMetadata { + f = file.Location{ + LocationData: file.LocationData{ + Coordinates: c, + VirtualPath: "", + }, + LocationMetadata: file.LocationMetadata{}, + } + break // there should only be 1 + } + assert.Equal(t, p.ID(), r.From.ID()) + assert.Equal(t, f.ID(), r.To.ID()) +} From 59107324ce7fcf78a139f1219e1751a8b851674a Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 14 Aug 2023 16:28:07 -0400 Subject: [PATCH 004/106] chore: restore cataloger.DefaultConfig (#2028) Signed-off-by: Keith Zantow --- syft/pkg/cataloger/config.go | 10 ++++++++++ test/integration/catalog_packages_test.go | 4 ++-- test/integration/utils_test.go | 16 ++-------------- 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/syft/pkg/cataloger/config.go b/syft/pkg/cataloger/config.go index d0a64d83d..df3b63978 100644 --- a/syft/pkg/cataloger/config.go +++ b/syft/pkg/cataloger/config.go @@ -18,6 +18,16 @@ type Config struct { ExcludeBinaryOverlapByOwnership bool } +func DefaultConfig() Config { + return Config{ + Search: DefaultSearchConfig(), + Parallelism: 1, + LinuxKernel: kernel.DefaultLinuxCatalogerConfig(), + Python: python.DefaultCatalogerConfig(), + ExcludeBinaryOverlapByOwnership: true, + } +} + func (c Config) Java() java.Config { return java.Config{ SearchUnindexedArchives: c.Search.IncludeUnindexedArchives, diff --git a/test/integration/catalog_packages_test.go b/test/integration/catalog_packages_test.go index 4596a75aa..0b6ca54f0 100644 --- a/test/integration/catalog_packages_test.go +++ b/test/integration/catalog_packages_test.go @@ -23,7 +23,7 @@ func BenchmarkImagePackageCatalogers(b *testing.B) { tarPath := imagetest.GetFixtureImageTarPath(b, fixtureImageName) var pc *pkg.Collection - for _, c := range cataloger.ImageCatalogers(defaultConfig()) { + for _, c := range cataloger.ImageCatalogers(cataloger.DefaultConfig()) { // in case of future alteration where state is persisted, assume no dependency is safe to reuse userInput := "docker-archive:" + tarPath detection, err := source.Detect(userInput, source.DefaultDetectConfig()) @@ -260,7 +260,7 @@ func TestPkgCoverageCatalogerConfiguration(t *testing.T) { assert.Equal(t, definedLanguages, observedLanguages) // Verify that rust isn't actually an image cataloger - c := defaultConfig() + c := cataloger.DefaultConfig() c.Catalogers = []string{"rust"} assert.Len(t, cataloger.ImageCatalogers(c), 0) } diff --git a/test/integration/utils_test.go b/test/integration/utils_test.go index 207fe675c..eeb583f65 100644 --- a/test/integration/utils_test.go +++ b/test/integration/utils_test.go @@ -8,8 +8,6 @@ import ( "github.com/anchore/stereoscope/pkg/imagetest" "github.com/anchore/syft/syft" "github.com/anchore/syft/syft/pkg/cataloger" - "github.com/anchore/syft/syft/pkg/cataloger/kernel" - "github.com/anchore/syft/syft/pkg/cataloger/python" "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" ) @@ -26,7 +24,7 @@ func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Sco theSource.Close() }) - c := defaultConfig() + c := cataloger.DefaultConfig() c.Catalogers = catalogerCfg c.Search.Scope = scope @@ -54,16 +52,6 @@ func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Sco }, theSource } -func defaultConfig() cataloger.Config { - return cataloger.Config{ - Search: cataloger.DefaultSearchConfig(), - Parallelism: 1, - LinuxKernel: kernel.DefaultLinuxCatalogerConfig(), - Python: python.DefaultCatalogerConfig(), - ExcludeBinaryOverlapByOwnership: true, - } -} - func catalogDirectory(t *testing.T, dir string) (sbom.SBOM, source.Source) { userInput := "dir:" + dir detection, err := source.Detect(userInput, source.DefaultDetectConfig()) @@ -75,7 +63,7 @@ func catalogDirectory(t *testing.T, dir string) (sbom.SBOM, source.Source) { }) // TODO: this would be better with functional options (after/during API refactor) - c := defaultConfig() + c := cataloger.DefaultConfig() c.Search.Scope = source.AllLayersScope pkgCatalog, relationships, actualDistro, err := syft.CatalogPackages(theSource, c) if err != nil { From 9467bd66c265c016b980faadd33fe1791e277954 Mon Sep 17 00:00:00 2001 From: Mark Galpin Date: Thu, 17 Aug 2023 08:02:12 -0700 Subject: [PATCH 005/106] fix: CycloneDX relationships not output or decoded properly (#1974) Signed-off-by: Mark Galpin Signed-off-by: Keith Zantow Co-authored-by: Mark Galpin Co-authored-by: Keith Zantow --- .../common/cyclonedxhelpers/decoder.go | 25 +++-- .../common/cyclonedxhelpers/decoder_test.go | 100 +++++++++++++++++ .../formats/common/cyclonedxhelpers/format.go | 12 ++- .../common/cyclonedxhelpers/format_test.go | 101 ++++++++++++++++++ test/integration/encode_decode_cycle_test.go | 6 +- 5 files changed, 228 insertions(+), 16 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/decoder.go b/syft/formats/common/cyclonedxhelpers/decoder.go index ecfb9baf9..741e51ea5 100644 --- a/syft/formats/common/cyclonedxhelpers/decoder.go +++ b/syft/formats/common/cyclonedxhelpers/decoder.go @@ -210,24 +210,33 @@ func collectRelationships(bom *cyclonedx.BOM, s *sbom.SBOM, idMap map[string]int return } for _, d := range *bom.Dependencies { - to, fromExists := idMap[d.Ref].(artifact.Identifiable) - if !fromExists { - continue - } - if d.Dependencies == nil { continue } + toPtr, toExists := idMap[d.Ref] + if !toExists { + continue + } + to, ok := common.PtrToStruct(toPtr).(artifact.Identifiable) + if !ok { + continue + } + for _, t := range *d.Dependencies { - from, toExists := idMap[t].(artifact.Identifiable) - if !toExists { + fromPtr, fromExists := idMap[t] + if !fromExists { + continue + } + from, ok := common.PtrToStruct(fromPtr).(artifact.Identifiable) + if !ok { continue } s.Relationships = append(s.Relationships, artifact.Relationship{ From: from, To: to, - Type: artifact.DependencyOfRelationship, // FIXME this information is lost + // match assumptions in encoding, that this is the only type of relationship captured: + Type: artifact.DependencyOfRelationship, }) } } diff --git a/syft/formats/common/cyclonedxhelpers/decoder_test.go b/syft/formats/common/cyclonedxhelpers/decoder_test.go index 7559dc02d..5d335d700 100644 --- a/syft/formats/common/cyclonedxhelpers/decoder_test.go +++ b/syft/formats/common/cyclonedxhelpers/decoder_test.go @@ -8,8 +8,10 @@ import ( "github.com/CycloneDX/cyclonedx-go" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" ) @@ -336,3 +338,101 @@ func Test_missingComponentsDecode(t *testing.T) { assert.NoError(t, err) } + +func Test_decodeDependencies(t *testing.T) { + c1 := cyclonedx.Component{ + Name: "c1", + } + + c2 := cyclonedx.Component{ + Name: "c2", + } + + c3 := cyclonedx.Component{ + Name: "c3", + } + + for _, c := range []*cyclonedx.Component{&c1, &c2, &c3} { + c.BOMRef = c.Name + } + + setTypes := func(typ cyclonedx.ComponentType, components ...cyclonedx.Component) *[]cyclonedx.Component { + var out []cyclonedx.Component + for _, c := range components { + c.Type = typ + out = append(out, c) + } + return &out + } + + tests := []struct { + name string + sbom cyclonedx.BOM + expected []string + }{ + { + name: "dependencies decoded as dependencyOf relationships", + sbom: cyclonedx.BOM{ + Components: setTypes(cyclonedx.ComponentTypeLibrary, + c1, + c2, + c3, + ), + Dependencies: &[]cyclonedx.Dependency{ + { + Ref: c1.BOMRef, + Dependencies: &[]string{ + c2.BOMRef, + c3.BOMRef, + }, + }, + }, + }, + expected: []string{c2.Name, c3.Name}, + }, + { + name: "dependencies skipped with unhandled components", + sbom: cyclonedx.BOM{ + Components: setTypes("", + c1, + c2, + c3, + ), + Dependencies: &[]cyclonedx.Dependency{ + { + Ref: c1.BOMRef, + Dependencies: &[]string{ + c2.BOMRef, + c3.BOMRef, + }, + }, + }, + }, + expected: nil, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + s, err := ToSyftModel(&test.sbom) + require.NoError(t, err) + require.NotNil(t, s) + + var deps []string + if s != nil { + for _, r := range s.Relationships { + if r.Type != artifact.DependencyOfRelationship { + continue + } + if p, ok := r.To.(pkg.Package); !ok || p.Name != c1.Name { + continue + } + if p, ok := r.From.(pkg.Package); ok { + deps = append(deps, p.Name) + } + } + } + require.Equal(t, test.expected, deps) + }) + } +} diff --git a/syft/formats/common/cyclonedxhelpers/format.go b/syft/formats/common/cyclonedxhelpers/format.go index 34ca35094..9582ba85d 100644 --- a/syft/formats/common/cyclonedxhelpers/format.go +++ b/syft/formats/common/cyclonedxhelpers/format.go @@ -143,27 +143,29 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc for _, r := range relationships { exists := isExpressiblePackageRelationship(r.Type) if !exists { - log.Debugf("unable to convert relationship from CycloneDX 1.4 JSON, dropping: %+v", r) + log.Debugf("unable to convert relationship type to CycloneDX JSON, dropping: %#v", r) continue } // we only capture package-to-package relationships for now - fromPkg, ok := r.From.(*pkg.Package) + fromPkg, ok := r.From.(pkg.Package) if !ok { + log.Tracef("unable to convert relationship fromPkg to CycloneDX JSON, dropping: %#v", r) continue } - toPkg, ok := r.To.(*pkg.Package) + toPkg, ok := r.To.(pkg.Package) if !ok { + log.Tracef("unable to convert relationship toPkg to CycloneDX JSON, dropping: %#v", r) continue } // ind dep innerDeps := []string{} - innerDeps = append(innerDeps, deriveBomRef(*fromPkg)) + innerDeps = append(innerDeps, deriveBomRef(fromPkg)) result = append(result, cyclonedx.Dependency{ - Ref: deriveBomRef(*toPkg), + Ref: deriveBomRef(toPkg), Dependencies: &innerDeps, }) } diff --git a/syft/formats/common/cyclonedxhelpers/format_test.go b/syft/formats/common/cyclonedxhelpers/format_test.go index 290e7152a..d16a390a6 100644 --- a/syft/formats/common/cyclonedxhelpers/format_test.go +++ b/syft/formats/common/cyclonedxhelpers/format_test.go @@ -3,7 +3,13 @@ package cyclonedxhelpers import ( "testing" + "github.com/CycloneDX/cyclonedx-go" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/sbom" ) func Test_formatCPE(t *testing.T) { @@ -32,3 +38,98 @@ func Test_formatCPE(t *testing.T) { }) } } + +func Test_relationships(t *testing.T) { + p1 := pkg.Package{ + Name: "p1", + } + p1.SetID() + + p2 := pkg.Package{ + Name: "p2", + } + p2.SetID() + + p3 := pkg.Package{ + Name: "p3", + } + p3.SetID() + + tests := []struct { + name string + sbom sbom.SBOM + expected []string + }{ + { + name: "package dependencyOf relationships output as dependencies", + sbom: sbom.SBOM{ + Artifacts: sbom.Artifacts{ + Packages: pkg.NewCollection(p1, p2, p3), + }, + Relationships: []artifact.Relationship{ + { + From: p2, + To: p1, + Type: artifact.DependencyOfRelationship, + }, + { + From: p3, + To: p1, + Type: artifact.DependencyOfRelationship, + }, + }, + }, + expected: []string{p2.Name, p3.Name}, + }, + { + name: "package contains relationships not output", + sbom: sbom.SBOM{ + Artifacts: sbom.Artifacts{ + Packages: pkg.NewCollection(p1, p2, p3), + }, + Relationships: []artifact.Relationship{ + { + From: p2, + To: p1, + Type: artifact.ContainsRelationship, + }, + { + From: p3, + To: p1, + Type: artifact.ContainsRelationship, + }, + }, + }, + expected: nil, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + cdx := ToFormatModel(test.sbom) + got := cdx.Dependencies + + var deps []string + if got != nil { + for _, r := range *got { + for _, d := range *r.Dependencies { + c := findComponent(cdx, d) + require.NotNil(t, c) + deps = append(deps, c.Name) + } + + } + } + require.Equal(t, test.expected, deps) + }) + } +} + +func findComponent(cdx *cyclonedx.BOM, bomRef string) *cyclonedx.Component { + for _, c := range *cdx.Components { + if c.BOMRef == bomRef { + return &c + } + } + return nil +} diff --git a/test/integration/encode_decode_cycle_test.go b/test/integration/encode_decode_cycle_test.go index 844959bfe..ce839eca6 100644 --- a/test/integration/encode_decode_cycle_test.go +++ b/test/integration/encode_decode_cycle_test.go @@ -47,8 +47,8 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) { formatOption: cyclonedxjson.ID, redactor: func(in []byte) []byte { // unstable values - in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref)": "[^"]+",`).ReplaceAll(in, []byte{}) - + in = regexp.MustCompile(`"(timestamp|serialNumber|bom-ref|ref)":\s*"(\n|[^"])+"`).ReplaceAll(in, []byte(`"$1": "redacted"`)) + in = regexp.MustCompile(`"(dependsOn)":\s*\[(?:\s|[^]])+]`).ReplaceAll(in, []byte(`"$1": []`)) return in }, json: true, @@ -57,7 +57,7 @@ func TestEncodeDecodeEncodeCycleComparison(t *testing.T) { formatOption: cyclonedxxml.ID, redactor: func(in []byte) []byte { // unstable values - in = regexp.MustCompile(`(serialNumber|bom-ref)="[^"]+"`).ReplaceAll(in, []byte{}) + in = regexp.MustCompile(`(serialNumber|bom-ref|ref)="[^"]+"`).ReplaceAll(in, []byte{}) in = regexp.MustCompile(`[^<]+`).ReplaceAll(in, []byte{}) return in From d1635971a1cd9348f6cd13c81062282257bc9e06 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Thu, 17 Aug 2023 11:27:31 -0400 Subject: [PATCH 006/106] fix: inconsistent removal of binaries by overlap (#2036) Signed-off-by: Keith Zantow --- syft/lib.go | 3 +-- syft/lib_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 syft/lib_test.go diff --git a/syft/lib.go b/syft/lib.go index 32f010d02..d8ef7617d 100644 --- a/syft/lib.go +++ b/syft/lib.go @@ -92,8 +92,7 @@ func CatalogPackages(src source.Source, cfg cataloger.Config) (*pkg.Collection, } func removeRelationshipsByID(relationships []artifact.Relationship, id artifact.ID) []artifact.Relationship { - // https://github.com/golang/go/wiki/SliceTricks#filtering-without-allocating - filtered := relationships[:0] + var filtered []artifact.Relationship for _, r := range relationships { if r.To.ID() != id && r.From.ID() != id { filtered = append(filtered, r) diff --git a/syft/lib_test.go b/syft/lib_test.go new file mode 100644 index 000000000..0e2ca8a8e --- /dev/null +++ b/syft/lib_test.go @@ -0,0 +1,42 @@ +package syft + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/pkg" +) + +func Test_removeRelationshipsByID(t *testing.T) { + p1 := pkg.Package{} + p1.OverrideID("1") + + p2 := pkg.Package{} + p2.OverrideID("2") + + p3 := pkg.Package{} + p3.OverrideID("3") + + rel := func(pkgs ...pkg.Package) (out []artifact.Relationship) { + for _, p := range pkgs { + out = append(out, artifact.Relationship{ + From: p, + To: p, + Type: artifact.OwnershipByFileOverlapRelationship, + }) + } + return + } + + relationships := rel(p1, p2, p3) + + for _, r := range relationships { + if r.From.ID() == "1" || r.From.ID() == "2" { + relationships = removeRelationshipsByID(relationships, r.From.ID()) + } + } + + require.Equal(t, rel(p3), relationships) +} From 4762ba0943785fe778276893388e839e01787b45 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Thu, 17 Aug 2023 12:55:25 -0400 Subject: [PATCH 007/106] feat: use java package names to determine known groupids (#2032) Signed-off-by: Keith Zantow --- syft/pkg/cataloger/common/cpe/java.go | 12 ++-- .../cataloger/common/cpe/java_groupid_map.go | 69 +++++++++++++++++++ syft/pkg/cataloger/common/cpe/java_test.go | 36 ++++++++++ syft/pkg/cataloger/java/package_url.go | 2 +- 4 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 syft/pkg/cataloger/common/cpe/java_groupid_map.go diff --git a/syft/pkg/cataloger/common/cpe/java.go b/syft/pkg/cataloger/common/cpe/java.go index 6de454c06..2e838de30 100644 --- a/syft/pkg/cataloger/common/cpe/java.go +++ b/syft/pkg/cataloger/common/cpe/java.go @@ -181,13 +181,13 @@ func GroupIDsFromJavaPackage(p pkg.Package) (groupIDs []string) { return nil } - return GroupIDsFromJavaMetadata(metadata) + return GroupIDsFromJavaMetadata(p.Name, metadata) } -func GroupIDsFromJavaMetadata(metadata pkg.JavaMetadata) (groupIDs []string) { +func GroupIDsFromJavaMetadata(pkgName string, metadata pkg.JavaMetadata) (groupIDs []string) { groupIDs = append(groupIDs, groupIDsFromPomProperties(metadata.PomProperties)...) groupIDs = append(groupIDs, groupIDsFromPomProject(metadata.PomProject)...) - groupIDs = append(groupIDs, groupIDsFromJavaManifest(metadata.Manifest)...) + groupIDs = append(groupIDs, groupIDsFromJavaManifest(pkgName, metadata.Manifest)...) return groupIDs } @@ -241,7 +241,11 @@ func addGroupIDsFromGroupIDsAndArtifactID(groupID, artifactID string) (groupIDs return groupIDs } -func groupIDsFromJavaManifest(manifest *pkg.JavaManifest) []string { +func groupIDsFromJavaManifest(pkgName string, manifest *pkg.JavaManifest) []string { + if groupID, ok := defaultArtifactIDToGroupID[pkgName]; ok { + return []string{groupID} + } + if manifest == nil { return nil } diff --git a/syft/pkg/cataloger/common/cpe/java_groupid_map.go b/syft/pkg/cataloger/common/cpe/java_groupid_map.go new file mode 100644 index 000000000..9e36db185 --- /dev/null +++ b/syft/pkg/cataloger/common/cpe/java_groupid_map.go @@ -0,0 +1,69 @@ +package cpe + +var defaultArtifactIDToGroupID = map[string]string{ + "ant": "org.apache.ant", + "ant-antlr": "org.apache.ant", + "ant-antunit": "org.apache.ant", + "ant-apache-bcel": "org.apache.ant", + "ant-apache-bsf": "org.apache.ant", + "ant-apache-log4j": "org.apache.ant", + "ant-apache-oro": "org.apache.ant", + "ant-apache-regexp": "org.apache.ant", + "ant-apache-resolver": "org.apache.ant", + "ant-apache-xalan2": "org.apache.ant", + "ant-commons-logging": "org.apache.ant", + "ant-commons-net": "org.apache.ant", + "ant-compress": "org.apache.ant", + "ant-dotnet": "org.apache.ant", + "ant-imageio": "org.apache.ant", + "ant-jai": "org.apache.ant", + "ant-jakartamail": "org.apache.ant", + "ant-javamail": "org.apache.ant", + "ant-jdepend": "org.apache.ant", + "ant-jmf": "org.apache.ant", + "ant-jsch": "org.apache.ant", + "ant-junit": "org.apache.ant", + "ant-junit4": "org.apache.ant", + "ant-junitlauncher": "org.apache.ant", + "ant-launcher": "org.apache.ant", + "ant-netrexx": "org.apache.ant", + "ant-nodeps": "org.apache.ant", + "ant-parent": "org.apache.ant", + "ant-starteam": "org.apache.ant", + "ant-stylebook": "org.apache.ant", + "ant-swing": "org.apache.ant", + "ant-testutil": "org.apache.ant", + "ant-trax": "org.apache.ant", + "ant-weblogic": "org.apache.ant", + "ant-xz": "org.apache.ant", + "spring": "org.springframework", + "spring-amqp": "org.springframework.amqp", + "spring-batch-core": "org.springframework.batch", + "spring-beans": "org.springframework", + "spring-boot": "org.springframework.boot", + "spring-boot-starter-web": "org.springframework.boot", + "spring-boot-starter-webflux": "org.springframework.boot", + "spring-cloud-function-context": "org.springframework.cloud", + "spring-cloud-function-parent": "org.springframework.cloud", + "spring-cloud-gateway": "org.springframework.cloud", + "spring-cloud-openfeign-core": "org.springframework.cloud", + "spring-cloud-task-dependencies": "org.springframework.cloud", + "spring-core": "org.springframework", + "spring-data-jpa": "org.springframework.data", + "spring-data-mongodb": "org.springframework.data", + "spring-data-rest-core": "org.springframework.data", + "spring-expression": "org.springframework", + "spring-integration-zip": "org.springframework.integration", + "spring-oxm": "org.springframework", + "spring-security-core": "org.springframework.security", + "spring-security-config": "org.springframework.security", + "spring-security-oauth": "org.springframework.security.oauth", + "spring-security-oauth-parent": "org.springframework.security.oauth", + "spring-security-oauth2-client": "org.springframework.security", + "spring-session-core": "org.springframework.session", + "spring-vault-core": "org.springframework.vault", + "spring-web": "org.springframework", + "spring-webflow": "org.springframework.webflow", + "spring-webflux": "org.springframework", + "spring-webmvc": "org.springframework", +} diff --git a/syft/pkg/cataloger/common/cpe/java_test.go b/syft/pkg/cataloger/common/cpe/java_test.go index c27e2ac5b..9d75014ac 100644 --- a/syft/pkg/cataloger/common/cpe/java_test.go +++ b/syft/pkg/cataloger/common/cpe/java_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/anchore/syft/syft/pkg" ) @@ -427,3 +428,38 @@ func Test_vendorsFromJavaManifestNames(t *testing.T) { }) } } + +func Test_groupIDsFromJavaManifest(t *testing.T) { + tests := []struct { + name string + manifest pkg.JavaManifest + expected []string + }{ + { + name: "spring-security-core", + manifest: pkg.JavaManifest{}, + expected: []string{"org.springframework.security"}, + }, + { + name: "spring-web", + manifest: pkg.JavaManifest{}, + expected: []string{"org.springframework"}, + }, + { + name: "spring-foo", + manifest: pkg.JavaManifest{ + Main: map[string]string{ + "Implementation-Vendor": "org.foo", + }, + }, + expected: []string{"org.foo"}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := groupIDsFromJavaManifest(test.name, &test.manifest) + require.Equal(t, test.expected, got) + }) + } +} diff --git a/syft/pkg/cataloger/java/package_url.go b/syft/pkg/cataloger/java/package_url.go index b091ac383..adf05ff74 100644 --- a/syft/pkg/cataloger/java/package_url.go +++ b/syft/pkg/cataloger/java/package_url.go @@ -9,7 +9,7 @@ import ( // PackageURL returns the PURL for the specific java package (see https://github.com/package-url/purl-spec) func packageURL(name, version string, metadata pkg.JavaMetadata) string { var groupID = name - groupIDs := cpe.GroupIDsFromJavaMetadata(metadata) + groupIDs := cpe.GroupIDsFromJavaMetadata(name, metadata) if len(groupIDs) > 0 { groupID = groupIDs[0] } From cb0214ec1d74f056be9e40a55dc0c405cbbc5f2f Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 18 Aug 2023 16:03:11 -0400 Subject: [PATCH 008/106] fill out new version notice (#2042) Signed-off-by: Alex Goodman --- .../ui/__snapshots__/post_ui_event_writer_test.snap | 7 +------ cmd/syft/internal/ui/post_ui_event_writer.go | 5 ++++- cmd/syft/internal/ui/post_ui_event_writer_test.go | 2 +- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/cmd/syft/internal/ui/__snapshots__/post_ui_event_writer_test.snap b/cmd/syft/internal/ui/__snapshots__/post_ui_event_writer_test.snap index bf473e331..64cafcab3 100755 --- a/cmd/syft/internal/ui/__snapshots__/post_ui_event_writer_test.snap +++ b/cmd/syft/internal/ui/__snapshots__/post_ui_event_writer_test.snap @@ -27,12 +27,7 @@ report 1!!> - - - - - +A newer version of syft is available for download: v0.33.0 (installed version is [not provided]) --- diff --git a/cmd/syft/internal/ui/post_ui_event_writer.go b/cmd/syft/internal/ui/post_ui_event_writer.go index e3772981b..22287d8ff 100644 --- a/cmd/syft/internal/ui/post_ui_event_writer.go +++ b/cmd/syft/internal/ui/post_ui_event_writer.go @@ -10,6 +10,7 @@ import ( "github.com/wagoodman/go-partybus" "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/internal/version" "github.com/anchore/syft/syft/event" "github.com/anchore/syft/syft/event/parsers" ) @@ -118,12 +119,14 @@ func writeAppUpdate(writer io.Writer, events ...partybus.Event) error { style := lipgloss.NewStyle().Foreground(lipgloss.Color("13")).Italic(true) for _, e := range events { - notice, err := parsers.ParseCLIAppUpdateAvailable(e) + newVersion, err := parsers.ParseCLIAppUpdateAvailable(e) if err != nil { log.WithFields("error", err).Warn("failed to parse app update notification") continue } + notice := fmt.Sprintf("A newer version of syft is available for download: %s (installed version is %s)", newVersion, version.FromBuild().Version) + if _, err := fmt.Fprintln(writer, style.Render(notice)); err != nil { // don't let this be fatal log.WithFields("error", err).Warn("failed to write app update notification") diff --git a/cmd/syft/internal/ui/post_ui_event_writer_test.go b/cmd/syft/internal/ui/post_ui_event_writer_test.go index a5bdd5792..83af4d2c5 100644 --- a/cmd/syft/internal/ui/post_ui_event_writer_test.go +++ b/cmd/syft/internal/ui/post_ui_event_writer_test.go @@ -35,7 +35,7 @@ func Test_postUIEventWriter_write(t *testing.T) { }, { Type: event.CLIAppUpdateAvailable, - Value: "\n\n\n\n", + Value: "v0.33.0", }, { Type: event.CLINotification, From 01c7709e0da25c385c8f501f87bcf58ff83ca994 Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 09:33:41 -0400 Subject: [PATCH 009/106] chore(deps): update CPE dictionary index (#2043) Signed-off-by: GitHub Co-authored-by: wagoodman --- syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json b/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json index 5e357e2b8..7ffc387cc 100644 --- a/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json +++ b/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json @@ -283,6 +283,7 @@ "360class.jansenhm": "cpe:2.3:a:360class.jansenhm_project:360class.jansenhm:*:*:*:*:*:node.js:*:*", "626": "cpe:2.3:a:626_project:626:*:*:*:*:*:node.js:*:*", "@actions/core": "cpe:2.3:a:toolkit_project:toolkit:*:*:*:*:*:node.js:*:*", + "@antfu/utils": "cpe:2.3:a:antfu:utils:*:*:*:*:*:node.js:*:*", "@awsui/components-react": "cpe:2.3:a:amazon:awsui\\/components-react:*:*:*:*:*:node.js:*:*", "@azure/ms-rest-nodeauth": "cpe:2.3:a:microsoft:ms-rest-nodeauth:*:*:*:*:*:node.js:*:*", "@backstage/plugin-auth-backend": "cpe:2.3:a:linuxfoundation:auth_backend:*:*:*:*:*:node.js:*:*", @@ -1081,6 +1082,7 @@ "spacy": "cpe:2.3:a:explosion:spacy:*:*:*:*:*:python:*:*", "sqlparse": "cpe:2.3:a:sqlparse_project:sqlparse:*:*:*:*:*:python:*:*", "tkvideoplayer": "cpe:2.3:a:python:tkvideoplayer:*:*:*:*:*:*:*:*", + "togglee": "cpe:2.3:a:togglee:togglee:*:*:*:*:*:pypi:*:*", "urllib3": "cpe:2.3:a:python:urllib3:*:*:*:*:*:*:*:*", "validators": "cpe:2.3:a:validators_project:validators:*:*:*:*:*:python:*:*", "vault-cli": "cpe:2.3:a:vault-cli_project:vault-cli:*:*:*:*:*:python:*:*", From f58425a305e7c8ff9d79540b6b014aea912e0615 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 21 Aug 2023 10:37:11 -0400 Subject: [PATCH 010/106] chore(deps): bump github.com/jinzhu/copier from 0.3.5 to 0.4.0 (#2045) Bumps [github.com/jinzhu/copier](https://github.com/jinzhu/copier) from 0.3.5 to 0.4.0. - [Commits](https://github.com/jinzhu/copier/compare/v0.3.5...v0.4.0) --- updated-dependencies: - dependency-name: github.com/jinzhu/copier dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c67d868d2..0df1f2927 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/google/uuid v1.3.0 github.com/gookit/color v1.5.4 github.com/hashicorp/go-multierror v1.1.1 - github.com/jinzhu/copier v0.3.5 + github.com/jinzhu/copier v0.4.0 github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mholt/archiver/v3 v3.5.1 github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5 diff --git a/go.sum b/go.sum index 7ae75b12f..0a347c487 100644 --- a/go.sum +++ b/go.sum @@ -419,8 +419,8 @@ github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy77 github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= -github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg= -github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= +github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= +github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= From ee656fe08898f7958712b3299462d555f514a4f7 Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 08:48:42 -0400 Subject: [PATCH 011/106] chore(deps): update bootstrap tools to latest versions (#2048) Signed-off-by: GitHub Co-authored-by: spiffcs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 17a3c27d4..7647cee79 100644 --- a/Makefile +++ b/Makefile @@ -10,7 +10,7 @@ CHRONICLE_CMD = $(TEMP_DIR)/chronicle GLOW_CMD = $(TEMP_DIR)/glow # Tool versions ################################# -GOLANGCILINT_VERSION := v1.54.1 +GOLANGCILINT_VERSION := v1.54.2 GOSIMPORTS_VERSION := v0.3.8 BOUNCER_VERSION := v0.4.0 CHRONICLE_VERSION := v0.7.0 From cf37b1786985443c9864a0e4dd9fd51d26e85ed2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 22 Aug 2023 10:42:19 -0400 Subject: [PATCH 012/106] chore(deps): bump github.com/google/uuid from 1.3.0 to 1.3.1 (#2049) Bumps [github.com/google/uuid](https://github.com/google/uuid) from 1.3.0 to 1.3.1. - [Release notes](https://github.com/google/uuid/releases) - [Changelog](https://github.com/google/uuid/blob/master/CHANGELOG.md) - [Commits](https://github.com/google/uuid/compare/v1.3.0...v1.3.1) --- updated-dependencies: - dependency-name: github.com/google/uuid dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0df1f2927..d0ca06368 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/facebookincubator/nvdtools v0.1.5 github.com/go-test/deep v1.1.0 github.com/google/go-cmp v0.5.9 - github.com/google/uuid v1.3.0 + github.com/google/uuid v1.3.1 github.com/gookit/color v1.5.4 github.com/hashicorp/go-multierror v1.1.1 github.com/jinzhu/copier v0.4.0 diff --git a/go.sum b/go.sum index 0a347c487..b097b954f 100644 --- a/go.sum +++ b/go.sum @@ -355,8 +355,8 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S3 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= From ee121cff215a223bf4c6186e101f40f75914a5a8 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Tue, 22 Aug 2023 10:47:07 -0400 Subject: [PATCH 013/106] feat: 1944 - update purl generation to use a consistent groupID (#2033) Separate the logic for CPE and PURL generation. PURL generation needs a single answer for groupID based on a priority of discovering the field. CPE generation still uses multiple potential groupID to populate the candidate cpe. Improve GroupID detection. Currently syft does not use any hierarchy for GroupID detection and treats all sources as equal. It treats fields from the manifest file with priority. This change adds a hierarchy to the fields and returns a single answer based on that hierarchy. --------- Signed-off-by: Christopher Phillips Signed-off-by: Keith Zantow Co-authored-by: Keith Zantow --- syft/pkg/cataloger/common/cpe/java.go | 23 ++-- .../cataloger/common/cpe/java_groupid_map.go | 2 +- syft/pkg/cataloger/java/archive_parser.go | 63 ++++++--- .../pkg/cataloger/java/archive_parser_test.go | 50 +------ syft/pkg/cataloger/java/package_url.go | 126 +++++++++++++++++- syft/pkg/cataloger/java/package_url_test.go | 56 ++++++++ syft/pkg/cataloger/java/parse_pom_xml.go | 4 +- 7 files changed, 247 insertions(+), 77 deletions(-) diff --git a/syft/pkg/cataloger/common/cpe/java.go b/syft/pkg/cataloger/common/cpe/java.go index 2e838de30..7a1a8f7b8 100644 --- a/syft/pkg/cataloger/common/cpe/java.go +++ b/syft/pkg/cataloger/common/cpe/java.go @@ -21,16 +21,16 @@ var ( "be", } - primaryJavaManifestGroupIDFields = []string{ + PrimaryJavaManifestGroupIDFields = []string{ + "Bundle-SymbolicName", "Extension-Name", "Specification-Vendor", "Implementation-Vendor", - "Bundle-SymbolicName", "Implementation-Vendor-Id", "Implementation-Title", "Bundle-Activator", } - secondaryJavaManifestGroupIDFields = []string{ + SecondaryJavaManifestGroupIDFields = []string{ "Automatic-Module-Name", "Main-Class", "Package", @@ -168,7 +168,7 @@ func artifactIDFromJavaPackage(p pkg.Package) string { } artifactID := strings.TrimSpace(metadata.PomProperties.ArtifactID) - if startsWithTopLevelDomain(artifactID) && len(strings.Split(artifactID, ".")) > 1 { + if looksLikeGroupID(artifactID) && len(strings.Split(artifactID, ".")) > 1 { // there is a strong indication that the artifact ID is really a group ID, don't use it return "" } @@ -184,6 +184,9 @@ func GroupIDsFromJavaPackage(p pkg.Package) (groupIDs []string) { return GroupIDsFromJavaMetadata(p.Name, metadata) } +// GroupIDsFromJavaMetadata returns the possible group IDs for a Java package +// This function is similar to GroupIDFromJavaPackage, but returns all possible group IDs and is less strict +// It is used as a way to generate possible candidates for CPE matching. func GroupIDsFromJavaMetadata(pkgName string, metadata pkg.JavaMetadata) (groupIDs []string) { groupIDs = append(groupIDs, groupIDsFromPomProperties(metadata.PomProperties)...) groupIDs = append(groupIDs, groupIDsFromPomProject(metadata.PomProject)...) @@ -242,7 +245,7 @@ func addGroupIDsFromGroupIDsAndArtifactID(groupID, artifactID string) (groupIDs } func groupIDsFromJavaManifest(pkgName string, manifest *pkg.JavaManifest) []string { - if groupID, ok := defaultArtifactIDToGroupID[pkgName]; ok { + if groupID, ok := DefaultArtifactIDToGroupID[pkgName]; ok { return []string{groupID} } @@ -251,7 +254,7 @@ func groupIDsFromJavaManifest(pkgName string, manifest *pkg.JavaManifest) []stri } // try the common manifest fields first for a set of candidates - groupIDs := getManifestFieldGroupIDs(manifest, primaryJavaManifestGroupIDFields) + groupIDs := GetManifestFieldGroupIDs(manifest, PrimaryJavaManifestGroupIDFields) if len(groupIDs) != 0 { return groupIDs @@ -262,10 +265,10 @@ func groupIDsFromJavaManifest(pkgName string, manifest *pkg.JavaManifest) []stri // for more info see pkg:maven/commons-io/commons-io@2.8.0 within cloudbees/cloudbees-core-mm:2.263.4.2 // at /usr/share/jenkins/jenkins.war:WEB-INF/plugins/analysis-model-api.hpi:WEB-INF/lib/commons-io-2.8.0.jar // as well as the ant package from cloudbees/cloudbees-core-mm:2.277.2.4-ra. - return getManifestFieldGroupIDs(manifest, secondaryJavaManifestGroupIDFields) + return GetManifestFieldGroupIDs(manifest, SecondaryJavaManifestGroupIDFields) } -func getManifestFieldGroupIDs(manifest *pkg.JavaManifest, fields []string) (groupIDs []string) { +func GetManifestFieldGroupIDs(manifest *pkg.JavaManifest, fields []string) (groupIDs []string) { if manifest == nil { return nil } @@ -302,3 +305,7 @@ func removeOSCIDirectives(groupID string) string { func startsWithTopLevelDomain(value string) bool { return internal.HasAnyOfPrefixes(value, domains...) } + +func looksLikeGroupID(value string) bool { + return strings.Contains(value, ".") +} diff --git a/syft/pkg/cataloger/common/cpe/java_groupid_map.go b/syft/pkg/cataloger/common/cpe/java_groupid_map.go index 9e36db185..5205c81e9 100644 --- a/syft/pkg/cataloger/common/cpe/java_groupid_map.go +++ b/syft/pkg/cataloger/common/cpe/java_groupid_map.go @@ -1,6 +1,6 @@ package cpe -var defaultArtifactIDToGroupID = map[string]string{ +var DefaultArtifactIDToGroupID = map[string]string{ "ant": "org.apache.ant", "ant-antlr": "org.apache.ant", "ant-antunit": "org.apache.ant", diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index ea216e906..a9662be65 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -62,11 +62,11 @@ func parseJavaArchive(_ file.Resolver, _ *generic.Environment, reader file.Locat } // uniquePkgKey creates a unique string to identify the given package. -func uniquePkgKey(p *pkg.Package) string { +func uniquePkgKey(groupID string, p *pkg.Package) string { if p == nil { return "" } - return fmt.Sprintf("%s|%s", p.Name, p.Version) + return fmt.Sprintf("%s|%s|%s", groupID, p.Name, p.Version) } // newJavaArchiveParser returns a new java archive parser object for the given archive. Can be configured to discover @@ -371,13 +371,27 @@ func pomProjectByParentPath(archivePath string, location file.Location, extractP return projectByParentPath, nil } -// packagesFromPomProperties processes a single Maven POM properties for a given parent package, returning all listed Java packages found and +// newPackageFromMavenData processes a single Maven POM properties for a given parent package, returning all listed Java packages found and // associating each discovered package to the given parent package. Note the pom.xml is optional, the pom.properties is not. func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.PomProject, parentPkg *pkg.Package, location file.Location) *pkg.Package { // keep the artifact name within the virtual path if this package does not match the parent package vPathSuffix := "" - if !strings.HasPrefix(pomProperties.ArtifactID, parentPkg.Name) { - vPathSuffix += ":" + pomProperties.ArtifactID + groupID := "" + if parentMetadata, ok := parentPkg.Metadata.(pkg.JavaMetadata); ok { + groupID = groupIDFromJavaMetadata(parentPkg.Name, parentMetadata) + } + + parentKey := fmt.Sprintf("%s:%s:%s", groupID, parentPkg.Name, parentPkg.Version) + // Since we don't have a package yet, it's important to use the same `field: value` association that we used when creating the parent package + // See below where Name => pomProperties.ArtifactID and Version => pomProperties.Version. We want to check for potentially nested identical + // packages and create equal virtual paths so they are de duped in the future + pomProjectKey := fmt.Sprintf("%s:%s:%s", pomProperties.GroupID, pomProperties.ArtifactID, pomProperties.Version) + if parentKey != pomProjectKey { + // build a new virtual path suffix for the package that is different from the parent package + // we want to use the GroupID and ArtifactID here to preserve uniqueness + // Some packages have the same name but different group IDs (e.g. "org.glassfish.jaxb/jaxb-core", "com.sun.xml.bind/jaxb-core") + // https://github.com/anchore/syft/issues/1944 + vPathSuffix += ":" + pomProperties.GroupID + ":" + pomProperties.ArtifactID } virtualPath := location.AccessPath() + vPathSuffix @@ -408,21 +422,26 @@ func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.Po } func packageIdentitiesMatch(p pkg.Package, parentPkg *pkg.Package) bool { - // the name/version pair matches... - if uniquePkgKey(&p) == uniquePkgKey(parentPkg) { - return true - } - metadata, ok := p.Metadata.(pkg.JavaMetadata) - if !ok { - log.WithFields("package", p.String()).Warn("unable to extract java metadata to check for matching package identity") - return false + parentMetadata, parentOk := parentPkg.Metadata.(pkg.JavaMetadata) + if !ok || !parentOk { + switch { + case !ok: + log.WithFields("package", p.String()).Trace("unable to extract java metadata to check for matching package identity for package: %s", p.Name) + case !parentOk: + log.WithFields("package", parentPkg.String()).Trace("unable to extract java metadata to check for matching package identity for package: %s", parentPkg.Name) + } + // if we can't extract metadata, we can check for matching identities via the package name + // this is not ideal, but it's better than nothing - this should not be used if we have Metadata + + return uniquePkgKey("", &p) == uniquePkgKey("", parentPkg) } - parentMetadata, ok := parentPkg.Metadata.(pkg.JavaMetadata) - if !ok { - log.WithFields("package", p.String()).Warn("unable to extract java metadata from parent for verifying virtual path") - return false + // try to determine identity with the metadata + groupID := groupIDFromJavaMetadata(p.Name, metadata) + parentGroupID := groupIDFromJavaMetadata(parentPkg.Name, parentMetadata) + if uniquePkgKey(groupID, &p) == uniquePkgKey(parentGroupID, parentPkg) { + return true } // the virtual path matches... @@ -434,10 +453,14 @@ func packageIdentitiesMatch(p pkg.Package, parentPkg *pkg.Package) bool { // note: you CANNOT use name-is-subset-of-artifact-id or vice versa --this is too generic. Shaded jars are a good // example of this: where the package name is "cloudbees-analytics-segment-driver" and a child is "analytics", but // they do not indicate the same package. - if metadata.PomProperties.ArtifactID != "" && parentPkg.Name == metadata.PomProperties.ArtifactID { - return true + // NOTE: artifactId might not be a good indicator of uniqueness since archives can contain forks with the same name + // from different groups (e.g. "org.glassfish.jaxb.jaxb-core" and "com.sun.xml.bind.jaxb-core") + // we will use this check as a last resort + if metadata.PomProperties != nil { + if metadata.PomProperties.ArtifactID != "" && parentPkg.Name == metadata.PomProperties.ArtifactID { + return true + } } - return false } diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index 422de7d48..4635a9c98 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -174,8 +174,8 @@ func TestParseJar(t *testing.T) { MetadataType: pkg.JavaMetadataType, Metadata: pkg.JavaMetadata{ // ensure that nested packages with different names than that of the parent are appended as - // a suffix on the virtual path - VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar:joda-time", + // a suffix on the virtual path with a colon separator between group name and artifact name + VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar:joda-time:joda-time", PomProperties: &pkg.PomProperties{ Path: "META-INF/maven/joda-time/joda-time/pom.properties", GroupID: "joda-time", @@ -240,7 +240,7 @@ func TestParseJar(t *testing.T) { Metadata: pkg.JavaMetadata{ // ensure that nested packages with different names than that of the parent are appended as // a suffix on the virtual path - VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar:joda-time", + VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar:joda-time:joda-time", PomProperties: &pkg.PomProperties{ Path: "META-INF/maven/joda-time/joda-time/pom.properties", GroupID: "joda-time", @@ -659,7 +659,7 @@ func Test_newPackageFromMavenData(t *testing.T) { Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, Metadata: pkg.JavaMetadata{ - VirtualPath: virtualPath + ":" + "some-artifact-id", + VirtualPath: virtualPath + ":" + "some-group-id" + ":" + "some-artifact-id", PomProperties: &pkg.PomProperties{ Name: "some-name", GroupID: "some-group-id", @@ -728,7 +728,7 @@ func Test_newPackageFromMavenData(t *testing.T) { Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, Metadata: pkg.JavaMetadata{ - VirtualPath: virtualPath + ":" + "some-artifact-id", + VirtualPath: virtualPath + ":" + "some-group-id" + ":" + "some-artifact-id", PomProperties: &pkg.PomProperties{ Name: "some-name", GroupID: "some-group-id", @@ -797,7 +797,7 @@ func Test_newPackageFromMavenData(t *testing.T) { Type: pkg.JenkinsPluginPkg, MetadataType: pkg.JavaMetadataType, Metadata: pkg.JavaMetadata{ - VirtualPath: virtualPath + ":" + "some-artifact-id", + VirtualPath: virtualPath + ":" + "com.cloudbees.jenkins.plugins" + ":" + "some-artifact-id", PomProperties: &pkg.PomProperties{ Name: "some-name", GroupID: "com.cloudbees.jenkins.plugins", @@ -894,44 +894,6 @@ func Test_newPackageFromMavenData(t *testing.T) { }, expectedPackage: nil, }, - { - name: "child matches parent by virtual path -- override name and version", - props: pkg.PomProperties{ - Name: "some-name", - GroupID: "some-group-id", - ArtifactID: "some-parent-name", // note: DOES NOT match parent package - Version: "3.0", // note: DOES NOT match parent package - }, - parent: &pkg.Package{ - Name: "", // note: empty, so should not be matched on - Version: "", // note: empty, so should not be matched on - Type: pkg.JavaPkg, - Metadata: pkg.JavaMetadata{ - VirtualPath: virtualPath, // note: matching virtual path - Manifest: nil, - PomProperties: nil, - Parent: nil, - }, - }, - expectedParent: pkg.Package{ - Name: "some-parent-name", - Version: "3.0", - Type: pkg.JavaPkg, - Metadata: pkg.JavaMetadata{ - VirtualPath: virtualPath, - Manifest: nil, - // note: we attach the discovered pom properties data - PomProperties: &pkg.PomProperties{ - Name: "some-name", - GroupID: "some-group-id", - ArtifactID: "some-parent-name", - Version: "3.0", - }, - Parent: nil, - }, - }, - expectedPackage: nil, - }, { name: "child matches parent by artifact id", props: pkg.PomProperties{ diff --git a/syft/pkg/cataloger/java/package_url.go b/syft/pkg/cataloger/java/package_url.go index adf05ff74..d29f1ae3c 100644 --- a/syft/pkg/cataloger/java/package_url.go +++ b/syft/pkg/cataloger/java/package_url.go @@ -1,6 +1,8 @@ package java import ( + "strings" + "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/common/cpe" @@ -9,9 +11,9 @@ import ( // PackageURL returns the PURL for the specific java package (see https://github.com/package-url/purl-spec) func packageURL(name, version string, metadata pkg.JavaMetadata) string { var groupID = name - groupIDs := cpe.GroupIDsFromJavaMetadata(name, metadata) - if len(groupIDs) > 0 { - groupID = groupIDs[0] + + if gID := groupIDFromJavaMetadata(name, metadata); gID != "" { + groupID = gID } pURL := packageurl.NewPackageURL( @@ -23,3 +25,121 @@ func packageURL(name, version string, metadata pkg.JavaMetadata) string { "") return pURL.ToString() } + +// GroupIDFromJavaPackage returns the authoritative group ID for a Java package. +// The order of precedence is: +// 1. The group ID from the POM properties +// 2. The group ID from the POM project +// 3. The group ID from a select map of known group IDs +// 4. The group ID from the Java manifest +func groupIDFromJavaMetadata(pkgName string, metadata pkg.JavaMetadata) (groupID string) { + if groupID = groupIDFromPomProperties(metadata.PomProperties); groupID != "" { + return groupID + } + + if groupID = groupIDFromPomProject(metadata.PomProject); groupID != "" { + return groupID + } + + if groupID = groupIDFromKnownPackageList(pkgName); groupID != "" { + return groupID + } + + if groupID = groupIDFromJavaManifest(metadata.Manifest); groupID != "" { + return groupID + } + + return groupID +} + +func groupIDFromKnownPackageList(pkgName string) (groupID string) { + if groupID, ok := cpe.DefaultArtifactIDToGroupID[pkgName]; ok { + return groupID + } + return groupID +} + +func groupIDFromJavaManifest(manifest *pkg.JavaManifest) (groupID string) { + if manifest == nil { + return groupID + } + + groupIDS := cpe.GetManifestFieldGroupIDs(manifest, cpe.PrimaryJavaManifestGroupIDFields) + // assumes that primaryJavaManifestNameFields are ordered by priority + if len(groupIDS) != 0 { + return groupIDS[0] + } + + groupIDS = cpe.GetManifestFieldGroupIDs(manifest, cpe.SecondaryJavaManifestGroupIDFields) + + if len(groupIDS) != 0 { + return groupIDS[0] + } + + return groupID +} + +func groupIDFromPomProperties(properties *pkg.PomProperties) (groupID string) { + if properties == nil { + return groupID + } + + if looksLikeGroupID(properties.GroupID) { + return cleanGroupID(properties.GroupID) + } + + // sometimes the publisher puts the group ID in the artifact ID field unintentionally + if looksLikeGroupID(properties.ArtifactID) { + // there is a strong indication that the artifact ID is really a group ID + return cleanGroupID(properties.ArtifactID) + } + + return groupID +} + +func groupIDFromPomProject(project *pkg.PomProject) (groupID string) { + if project == nil { + return groupID + } + + // check the project details + if looksLikeGroupID(project.GroupID) { + return cleanGroupID(project.GroupID) + } + + // sometimes the publisher puts the group ID in the artifact ID field unintentionally + if looksLikeGroupID(project.ArtifactID) { + // there is a strong indication that the artifact ID is really a group ID + return cleanGroupID(project.ArtifactID) + } + + // let's check the parent details + // if the current project does not have a group ID, but the parent does, we'll use the parent's group ID + if project.Parent != nil { + if looksLikeGroupID(project.Parent.GroupID) { + return cleanGroupID(project.Parent.GroupID) + } + + // sometimes the publisher puts the group ID in the artifact ID field unintentionally + if looksLikeGroupID(project.Parent.ArtifactID) { + // there is a strong indication that the artifact ID is really a group ID + return cleanGroupID(project.Parent.ArtifactID) + } + } + + return groupID +} +func looksLikeGroupID(value string) bool { + return strings.Contains(value, ".") +} + +func cleanGroupID(groupID string) string { + return strings.TrimSpace(removeOSCIDirectives(groupID)) +} + +func removeOSCIDirectives(groupID string) string { + // for example: + // org.bar;uses:=“org.foo” -> org.bar + // more about OSGI directives see https://spring.io/blog/2008/10/20/understanding-the-osgi-uses-directive/ + return strings.Split(groupID, ";")[0] +} diff --git a/syft/pkg/cataloger/java/package_url_test.go b/syft/pkg/cataloger/java/package_url_test.go index ac5eef6a0..665827a2d 100644 --- a/syft/pkg/cataloger/java/package_url_test.go +++ b/syft/pkg/cataloger/java/package_url_test.go @@ -45,3 +45,59 @@ func Test_packageURL(t *testing.T) { }) } } + +func Test_groupIDFromJavaMetadata(t *testing.T) { + tests := []struct { + name string + pkgName string + metadata pkg.JavaMetadata + expect string + }{ + { + name: "pom properties", + metadata: pkg.JavaMetadata{ + PomProperties: &pkg.PomProperties{ + GroupID: "org.anchore", + }, + }, + expect: "org.anchore", + }, + { + name: "pom project", + metadata: pkg.JavaMetadata{ + PomProject: &pkg.PomProject{ + GroupID: "org.anchore", + }, + }, + expect: "org.anchore", + }, + { + name: "known package list", + pkgName: "ant-antlr", + metadata: pkg.JavaMetadata{}, + expect: "org.apache.ant", + }, + { + name: "java manifest", + metadata: pkg.JavaMetadata{ + Manifest: &pkg.JavaManifest{ + Main: map[string]string{ + "Implementation-Vendor": "org.anchore", + }, + }, + }, + expect: "org.anchore", + }, + { + name: "no group id", + metadata: pkg.JavaMetadata{}, + expect: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expect, groupIDFromJavaMetadata(tt.pkgName, tt.metadata)) + }) + } +} diff --git a/syft/pkg/cataloger/java/parse_pom_xml.go b/syft/pkg/cataloger/java/parse_pom_xml.go index 704cb5598..c59bfbf40 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml.go +++ b/syft/pkg/cataloger/java/parse_pom_xml.go @@ -178,7 +178,9 @@ func resolveProperty(pom gopom.Project, property *string) string { pomValueType = pomValue.Type() if pomValueType.Kind() == reflect.Ptr { pomValue = pomValue.Elem() - pomValueType = pomValue.Type() + if !pomValue.IsZero() { + pomValueType = pomValue.Type() + } } if partNum == numParts-1 { return fmt.Sprintf("%v", pomValue.Interface()) From 17d4203bbb047d05bbd92856c978e4bfa5fc983e Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 23 Aug 2023 10:06:34 -0400 Subject: [PATCH 014/106] Enable reading non-utf-8 encodings for java pom.xml files (#2047) * fix reading non utf8 encodings Signed-off-by: Alex Goodman * in cases where we cant tell the encoding use the UTF8 replacement char Signed-off-by: Alex Goodman * decompose the xml decoding func to get a valid utf8 reader first and test unknown encoding Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- go.mod | 1 + go.sum | 2 + syft/pkg/cataloger/java/parse_pom_xml.go | 39 ++++++++- syft/pkg/cataloger/java/parse_pom_xml_test.go | 83 +++++++++++++++++++ .../declared-iso-8859-encoded-pom.xml.base64 | 1 + ...undeclared-iso-8859-encoded-pom.xml.base64 | 1 + 6 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 syft/pkg/cataloger/java/test-fixtures/pom/declared-iso-8859-encoded-pom.xml.base64 create mode 100644 syft/pkg/cataloger/java/test-fixtures/pom/undeclared-iso-8859-encoded-pom.xml.base64 diff --git a/go.mod b/go.mod index d0ca06368..a83f439b4 100644 --- a/go.mod +++ b/go.mod @@ -72,6 +72,7 @@ require ( github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b github.com/opencontainers/go-digest v1.0.0 github.com/saferwall/pe v1.4.4 + github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/sassoftware/go-rpmutils v0.2.0 github.com/vbatts/go-mtree v0.5.3 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 diff --git a/go.sum b/go.sum index b097b954f..631828a1a 100644 --- a/go.sum +++ b/go.sum @@ -596,6 +596,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/saferwall/pe v1.4.4 h1:Ml++7/2/Z1iKwV4zCsd1nIqTEAdUQKAetwbbcCarhOg= github.com/saferwall/pe v1.4.4/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= +github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= +github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE= github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI= github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ= diff --git a/syft/pkg/cataloger/java/parse_pom_xml.go b/syft/pkg/cataloger/java/parse_pom_xml.go index c59bfbf40..532424fcc 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml.go +++ b/syft/pkg/cataloger/java/parse_pom_xml.go @@ -1,6 +1,7 @@ package java import ( + "bytes" "encoding/xml" "fmt" "io" @@ -8,6 +9,7 @@ import ( "regexp" "strings" + "github.com/saintfish/chardet" "github.com/vifraa/gopom" "golang.org/x/net/html/charset" @@ -99,9 +101,15 @@ func newPackageFromPom(pom gopom.Project, dep gopom.Dependency, locations ...fil } func decodePomXML(content io.Reader) (project gopom.Project, err error) { - decoder := xml.NewDecoder(content) - // prevent against warnings for "xml: encoding "iso-8859-1" declared but Decoder.CharsetReader is nil" + inputReader, err := getUtf8Reader(content) + if err != nil { + return project, fmt.Errorf("unable to read pom.xml: %w", err) + } + + decoder := xml.NewDecoder(inputReader) + // when an xml file has a character set declaration (e.g. '') read that and use the correct decoder decoder.CharsetReader = charset.NewReaderLabel + if err := decoder.Decode(&project); err != nil { return project, fmt.Errorf("unable to unmarshal pom.xml: %w", err) } @@ -109,6 +117,33 @@ func decodePomXML(content io.Reader) (project gopom.Project, err error) { return project, nil } +func getUtf8Reader(content io.Reader) (io.Reader, error) { + pomContents, err := io.ReadAll(content) + if err != nil { + return nil, err + } + + detector := chardet.NewTextDetector() + detection, err := detector.DetectBest(pomContents) + + var inputReader io.Reader + if err == nil && detection != nil { + if detection.Charset == "UTF-8" { + inputReader = bytes.NewReader(pomContents) + } else { + inputReader, err = charset.NewReaderLabel(detection.Charset, bytes.NewReader(pomContents)) + if err != nil { + return nil, fmt.Errorf("unable to get encoding: %w", err) + } + } + } else { + // we could not detect the encoding, but we want a valid file to read. Replace unreadable + // characters with the UTF-8 replacement character. + inputReader = strings.NewReader(strings.ToValidUTF8(string(pomContents), "�")) + } + return inputReader, nil +} + func pomParent(pom gopom.Project, parent *gopom.Parent) (result *pkg.PomParent) { if parent == nil { return nil diff --git a/syft/pkg/cataloger/java/parse_pom_xml_test.go b/syft/pkg/cataloger/java/parse_pom_xml_test.go index edcf54144..08a7571e9 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml_test.go +++ b/syft/pkg/cataloger/java/parse_pom_xml_test.go @@ -1,10 +1,14 @@ package java import ( + "encoding/base64" + "io" "os" + "strings" "testing" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/vifraa/gopom" "github.com/anchore/syft/syft/file" @@ -63,6 +67,60 @@ func Test_parserPomXML(t *testing.T) { } } +func Test_decodePomXML_surviveNonUtf8Encoding(t *testing.T) { + // regression for https://github.com/anchore/syft/issues/2044 + + // we are storing the base64 contents of the pom.xml file. We are doing this to prevent accidental changes to the + // file, which is extremely important for this test. + + // for instance, even changing a single character in the file and saving in an IntelliJ IDE will automatically + // convert the file to UTF-8, which will break this test: + + // xxd with the original pom.xml + // 00000780: 6964 3e0d 0a20 2020 2020 2020 2020 2020 id>.. + // 00000790: 203c 6e61 6d65 3e4a e972 f46d 6520 4d69 J.r.me Mi + // 000007a0: 7263 3c2f 6e61 6d65 3e0d 0a20 2020 2020 rc.. + + // xxd with the pom.xml converted to UTF-8 (from a simple change with IntelliJ) + // 00000780: 6964 3e0d 0a20 2020 2020 2020 2020 2020 id>.. + // 00000790: 203c 6e61 6d65 3e4a efbf bd72 efbf bd6d J...r...m + // 000007a0: 6520 4d69 7263 3c2f 6e61 6d65 3e0d 0a20 e Mirc.. + + // Note that the name "Jérôme Mirc" was originally interpreted as "J.r.me Mi" and after the save + // is now encoded as "J...r...m" which is not what we want (note the extra bytes for each non UTF-8 character. + // The original 0xe9 byte (é) was converted to 0xefbfbd (�) which is the UTF-8 replacement character. + // This is quite silly on the part of IntelliJ, but it is what it is. + + cases := []struct { + name string + fixture string + }{ + { + name: "undeclared encoding", + fixture: "test-fixtures/pom/undeclared-iso-8859-encoded-pom.xml.base64", + }, + { + name: "declared encoding", + fixture: "test-fixtures/pom/declared-iso-8859-encoded-pom.xml.base64", + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + fh, err := os.Open(c.fixture) + require.NoError(t, err) + + decoder := base64.NewDecoder(base64.StdEncoding, fh) + + proj, err := decodePomXML(decoder) + + require.NoError(t, err) + require.NotEmpty(t, proj.Developers) + }) + } + +} + func Test_parseCommonsTextPomXMLProject(t *testing.T) { tests := []struct { input string @@ -401,3 +459,28 @@ func Test_resolveProperty(t *testing.T) { func stringPointer(s string) *string { return &s } + +func Test_getUtf8Reader(t *testing.T) { + tests := []struct { + name string + contents string + }{ + { + name: "unknown encoding", + // random binary contents + contents: "BkiJz02JyEWE0nXR6TH///9NicpJweEETIucJIgAAABJicxPjQwhTY1JCE05WQh0BU2J0eunTYshTIusJIAAAAAPHwBNOeV1BUUx2+tWTIlUJDhMiUwkSEyJRCQgSIl8JFBMiQ==", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + decoder := base64.NewDecoder(base64.StdEncoding, strings.NewReader(tt.contents)) + + got, err := getUtf8Reader(decoder) + require.NoError(t, err) + gotBytes, err := io.ReadAll(got) + require.NoError(t, err) + // if we couldn't decode the section as UTF-8, we should get a replacement character + assert.Contains(t, string(gotBytes), "�") + }) + } +} diff --git a/syft/pkg/cataloger/java/test-fixtures/pom/declared-iso-8859-encoded-pom.xml.base64 b/syft/pkg/cataloger/java/test-fixtures/pom/declared-iso-8859-encoded-pom.xml.base64 new file mode 100644 index 000000000..431baec1d --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/pom/declared-iso-8859-encoded-pom.xml.base64 @@ -0,0 +1 @@ +PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iSVNPLTg4NTktMSI/PgoKPHByb2plY3QgeG1sbnM9Imh0dHA6Ly9tYXZlbi5hcGFjaGUub3JnL1BPTS80LjAuMCIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vbWF2ZW4uYXBhY2hlLm9yZy9QT00vNC4wLjAgaHR0cDovL21hdmVuLmFwYWNoZS5vcmcvbWF2ZW4tdjRfMF8wLnhzZCI+DQogICAgPG1vZGVsVmVyc2lvbj40LjAuMDwvbW9kZWxWZXJzaW9uPg0KDQogICAgPG5hbWU+Q2FtZWxlb240SmF2YSAtIENvbnRlbnRTREs8L25hbWU+DQoNCiAgICA8Z3JvdXBJZD5jb20uYWxvZ2llbnQuY2FtZWxlb24uamF2YS5zZGs8L2dyb3VwSWQ+DQogICAgPGFydGlmYWN0SWQ+Y2FtZWxlb240amF2YS1zZGs8L2FydGlmYWN0SWQ+DQoNCiAgICA8dmVyc2lvbj4xLjEyLjI8L3ZlcnNpb24+DQoNCiAgICA8cGFja2FnaW5nPmphcjwvcGFja2FnaW5nPg0KDQogICAgPHNjbT4NCiAgICAgICAgPGNvbm5lY3Rpb24+c2NtOmdpdDpzc2g6Ly9naXRAZ2l0aHViLmNvbS9BbG9naWVudC9DT1M1LVNESy5naXQ8L2Nvbm5lY3Rpb24+DQogICAgICAgIDxkZXZlbG9wZXJDb25uZWN0aW9uPnNjbTpnaXQ6c3NoOi8vZ2l0QGdpdGh1Yi5jb20vQWxvZ2llbnQvQ09TNS1TREsuZ2l0PC9kZXZlbG9wZXJDb25uZWN0aW9uPg0KICAgICAgICA8dXJsPmdpdEBnaXRodWIuY29tOkFsb2dpZW50L0NPUzUtU0RLLmdpdDwvdXJsPg0KICAgIDwvc2NtPg0KDQogICAgPGRlc2NyaXB0aW9uPkNhbWVsZW9uIGlzIGEgQ01TIGRlZGljYXRlZCB0byBkZXZlbG9wZXJzIGFzIHdlbGwgYXMgbm9uLUlUIGNvbnRyaWJ1dG9ycy48L2Rlc2NyaXB0aW9uPg0KICAgIA0KICAgIDxvcmdhbml6YXRpb24+DQogICAgICAgIDxuYW1lPkFsb2dpZW50PC9uYW1lPg0KICAgICAgICA8dXJsPmh0dHA6Ly93d3cuYWxvZ2llbnQuY29tPC91cmw+DQogICAgPC9vcmdhbml6YXRpb24+DQoNCiAgICA8dXJsPmh0dHA6Ly93d3cuY2FtZWxlb25jbXMuY29tPC91cmw+DQoNCiAgICA8ZGV2ZWxvcGVycz4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5hbW9yaW48L2lkPg0KICAgICAgICAgICAgPG5hbWU+QXJuYXVkIE1vcmluPC9uYW1lPg0KICAgICAgICAgICAgPGVtYWlsPmFtb3JpbkBhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5hbG9yZDwvaWQ+DQogICAgICAgICAgICA8bmFtZT5BbGV4YW5kcmUgTG9yZDwvbmFtZT4NCiAgICAgICAgICAgIDxlbWFpbD5hbG9yZEBhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5kam9tcGhlPC9pZD4NCiAgICAgICAgICAgIDxuYW1lPkRhbmllbCBKb21waGU8L25hbWU+DQogICAgICAgICAgICA8ZW1haWw+ZGpvbXBoZUBhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5qbWlyYzwvaWQ+DQogICAgICAgICAgICA8bmFtZT5K6XL0bWUgTWlyYzwvbmFtZT4NCiAgICAgICAgICAgIDxlbWFpbD5qbWlyY0BhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5wbHZlaWxsZXV4PC9pZD4NCiAgICAgICAgICAgIDxuYW1lPlBpZXJyZS1MdWMgVmVpbGxldXg8L25hbWU+DQogICAgICAgICAgICA8ZW1haWw+cGx2ZWlsbGV1eEBhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICA8L2RldmVsb3BlcnM+DQoNCg0KICAgIDxsaWNlbnNlcz4NCiAgICAgICAgPGxpY2Vuc2U+DQogICAgICAgICAgICA8bmFtZT5UaGUgQXBhY2hlIFNvZnR3YXJlIExpY2Vuc2UsIFZlcnNpb24gMi4wPC9uYW1lPg0KICAgICAgICAgICAgPHVybD5odHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjAudHh0PC91cmw+DQogICAgICAgICAgICA8ZGlzdHJpYnV0aW9uPnJlcG88L2Rpc3RyaWJ1dGlvbj4NCiAgICAgICAgPC9saWNlbnNlPg0KICAgIDwvbGljZW5zZXM+DQoNCiAgICA8YnVpbGQ+DQoNCiAgICAgICAgPHJlc291cmNlcz4NCiAgICAgICAgICAgIDxyZXNvdXJjZT4NCiAgICAgICAgICAgICAgICA8ZmlsdGVyaW5nPmZhbHNlPC9maWx0ZXJpbmc+DQogICAgICAgICAgICAgICAgPGRpcmVjdG9yeT5zcmMvbWFpbi9yZXNvdXJjZXM8L2RpcmVjdG9yeT4NCiAgICAgICAgICAgIDwvcmVzb3VyY2U+DQogICAgICAgICAgICA8cmVzb3VyY2U+DQogICAgICAgICAgICAgICAgPGZpbHRlcmluZz5mYWxzZTwvZmlsdGVyaW5nPg0KICAgICAgICAgICAgICAgIDxkaXJlY3Rvcnk+c3JjL21haW4vamF2YTwvZGlyZWN0b3J5Pg0KICAgICAgICAgICAgICAgIDxpbmNsdWRlcz4NCiAgICAgICAgICAgICAgICAgICAgPGluY2x1ZGU+Kio8L2luY2x1ZGU+DQogICAgICAgICAgICAgICAgPC9pbmNsdWRlcz4NCiAgICAgICAgICAgICAgICA8ZXhjbHVkZXM+DQogICAgICAgICAgICAgICAgICAgIDxleGNsdWRlPioqLyouamF2YTwvZXhjbHVkZT4NCiAgICAgICAgICAgICAgICA8L2V4Y2x1ZGVzPg0KICAgICAgICAgICAgPC9yZXNvdXJjZT4NCiAgICAgICAgPC9yZXNvdXJjZXM+DQogICAgICAgIDx0ZXN0UmVzb3VyY2VzPg0KICAgICAgICAgICAgPHRlc3RSZXNvdXJjZT4NCiAgICAgICAgICAgICAgICA8ZmlsdGVyaW5nPmZhbHNlPC9maWx0ZXJpbmc+DQogICAgICAgICAgICAgICAgPGRpcmVjdG9yeT5zcmMvdGVzdC9yZXNvdXJjZXM8L2RpcmVjdG9yeT4NCiAgICAgICAgICAgIDwvdGVzdFJlc291cmNlPg0KICAgICAgICAgICAgPHRlc3RSZXNvdXJjZT4NCiAgICAgICAgICAgICAgICA8ZmlsdGVyaW5nPmZhbHNlPC9maWx0ZXJpbmc+DQogICAgICAgICAgICAgICAgPGRpcmVjdG9yeT5zcmMvdGVzdC9qYXZhPC9kaXJlY3Rvcnk+DQogICAgICAgICAgICAgICAgPGluY2x1ZGVzPg0KICAgICAgICAgICAgICAgICAgICA8aW5jbHVkZT4qKjwvaW5jbHVkZT4NCiAgICAgICAgICAgICAgICA8L2luY2x1ZGVzPg0KICAgICAgICAgICAgICAgIDxleGNsdWRlcz4NCiAgICAgICAgICAgICAgICAgICAgPGV4Y2x1ZGU+KiovKi5qYXZhPC9leGNsdWRlPg0KICAgICAgICAgICAgICAgIDwvZXhjbHVkZXM+DQogICAgICAgICAgICA8L3Rlc3RSZXNvdXJjZT4NCiAgICAgICAgPC90ZXN0UmVzb3VyY2VzPg0KDQogICAgICAgIDxwbHVnaW5zPg0KICAgICAgICAgICAgPHBsdWdpbj4NCiAgICAgICAgICAgICAgICA8Z3JvdXBJZD5vcmcuYXBhY2hlLm1hdmVuLnBsdWdpbnM8L2dyb3VwSWQ+DQogICAgICAgICAgICAgICAgPGFydGlmYWN0SWQ+bWF2ZW4tY29tcGlsZXItcGx1Z2luPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgICAgIDx2ZXJzaW9uPjIuMy4yPC92ZXJzaW9uPg0KICAgICAgICAgICAgICAgIDxjb25maWd1cmF0aW9uPg0KICAgICAgICAgICAgICAgICAgICA8c291cmNlPjEuNjwvc291cmNlPg0KICAgICAgICAgICAgICAgICAgICA8dGFyZ2V0PjEuNjwvdGFyZ2V0Pg0KICAgICAgICAgICAgICAgIDwvY29uZmlndXJhdGlvbj4NCiAgICAgICAgICAgIDwvcGx1Z2luPg0KICAgICAgICAgICAgPHBsdWdpbj4NCiAgICAgICAgICAgICAgICA8Z3JvdXBJZD5vcmcuYXBhY2hlLm1hdmVuLnBsdWdpbnM8L2dyb3VwSWQ+DQogICAgICAgICAgICAgICAgPGFydGlmYWN0SWQ+bWF2ZW4tc291cmNlLXBsdWdpbjwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgICAgICA8dmVyc2lvbj4yLjEuMjwvdmVyc2lvbj4NCiAgICAgICAgICAgICAgICA8ZXhlY3V0aW9ucz4NCiAgICAgICAgICAgICAgICAgICAgPGV4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxpZD5hdHRhY2gtc291cmNlczwvaWQ+DQogICAgICAgICAgICAgICAgICAgICAgICA8cGhhc2U+dmVyaWZ5PC9waGFzZT4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxnb2Fscz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Z29hbD5qYXI8L2dvYWw+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2dvYWxzPg0KICAgICAgICAgICAgICAgICAgICA8L2V4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICA8L2V4ZWN1dGlvbnM+DQogICAgICAgICAgICA8L3BsdWdpbj4NCiAgICAgICAgICAgIDxwbHVnaW4+DQogICAgICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLmFwYWNoZS5tYXZlbi5wbHVnaW5zPC9ncm91cElkPg0KICAgICAgICAgICAgICAgIDxhcnRpZmFjdElkPm1hdmVuLWphdmFkb2MtcGx1Z2luPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgICAgIDx2ZXJzaW9uPjIuNzwvdmVyc2lvbj4NCiAgICAgICAgICAgICAgICA8ZXhlY3V0aW9ucz4NCiAgICAgICAgICAgICAgICAgICAgPGV4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxpZD5hdHRhY2gtamF2YWRvY3M8L2lkPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGdvYWxzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxnb2FsPmphcjwvZ29hbD4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvZ29hbHM+DQogICAgICAgICAgICAgICAgICAgIDwvZXhlY3V0aW9uPg0KICAgICAgICAgICAgICAgIDwvZXhlY3V0aW9ucz4NCiAgICAgICAgICAgIDwvcGx1Z2luPg0KICAgICAgICAgICAgPHBsdWdpbj4NCiAgICAgICAgICAgICAgICA8Z3JvdXBJZD5vcmcuYXBhY2hlLm1hdmVuLnBsdWdpbnM8L2dyb3VwSWQ+DQogICAgICAgICAgICAgICAgPGFydGlmYWN0SWQ+bWF2ZW4tcmVsZWFzZS1wbHVnaW48L2FydGlmYWN0SWQ+DQogICAgICAgICAgICAgICAgPHZlcnNpb24+Mi4xPC92ZXJzaW9uPg0KICAgICAgICAgICAgPC9wbHVnaW4+DQoJCQkJICAgIDxwbHVnaW4+DQogICAgICAJCQkJICA8Z3JvdXBJZD5vcmcuYXBhY2hlLm1hdmVuLnBsdWdpbnM8L2dyb3VwSWQ+DQogICAgICAgIAkJCQk8YXJ0aWZhY3RJZD5tYXZlbi1yZXBvc2l0b3J5LXBsdWdpbjwvYXJ0aWZhY3RJZD4NCiAgICAgICAgCQkJCTx2ZXJzaW9uPjIuMy4xPC92ZXJzaW9uPg0KICAgICAgCQkJPC9wbHVnaW4+DQogICAgICAgICAgICA8cGx1Z2luPg0KICAgICAgICAgICAgICAgIDxncm91cElkPm9yZy5hcGFjaGUubWF2ZW4ucGx1Z2luczwvZ3JvdXBJZD4NCiAgICAgICAgICAgICAgICA8YXJ0aWZhY3RJZD5tYXZlbi1ncGctcGx1Z2luPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgICAgIDx2ZXJzaW9uPjEuMTwvdmVyc2lvbj4NCiAgICAgICAgICAgICAgICA8ZXhlY3V0aW9ucz4NCiAgICAgICAgICAgICAgICAgICAgPGV4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxpZD5zaWduLWFydGlmYWN0czwvaWQ+DQogICAgICAgICAgICAgICAgICAgICAgICA8cGhhc2U+dmVyaWZ5PC9waGFzZT4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxnb2Fscz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Z29hbD5zaWduPC9nb2FsPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9nb2Fscz4NCiAgICAgICAgICAgICAgICAgICAgPC9leGVjdXRpb24+DQogICAgICAgICAgICAgICAgPC9leGVjdXRpb25zPg0KICAgICAgICAgICAgPC9wbHVnaW4+DQogICAgICAJIDwvcGx1Z2lucz4NCiAgICA8L2J1aWxkPg0KDQogICAgPGRlcGVuZGVuY2llcz4NCg0KICAgICAgICA8IS0tIFNQUklORyBERVBFTkRFTkNJRVMgLS0+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1jb3JlPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+My4wLjQuUkVMRUFTRTwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxleGNsdXNpb25zPg0KICAgICAgICAgICAgICAgIDxleGNsdXNpb24+DQogICAgICAgICAgICAgICAgICAgIDxhcnRpZmFjdElkPmNvbW1vbnMtbG9nZ2luZzwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgICAgICAgICAgPGdyb3VwSWQ+Y29tbW9ucy1sb2dnaW5nPC9ncm91cElkPg0KICAgICAgICAgICAgICAgIDwvZXhjbHVzaW9uPg0KICAgICAgICAgICAgPC9leGNsdXNpb25zPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy13ZWI8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuNC5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1vcm08L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuNC5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1hc3BlY3RzPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+My4wLjQuUkVMRUFTRTwvdmVyc2lvbj4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy5zcHJpbmdmcmFtZXdvcms8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5zcHJpbmctZXhwcmVzc2lvbjwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjMuMC40LlJFTEVBU0U8L3ZlcnNpb24+DQogICAgICAgIDwvZGVwZW5kZW5jeT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5vcmcuc3ByaW5nZnJhbWV3b3JrLnNlY3VyaXR5PC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+c3ByaW5nLXNlY3VyaXR5LWNvcmU8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuMy5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yay5zZWN1cml0eTwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1zZWN1cml0eS1jb25maWc8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuMy5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yay5zZWN1cml0eTwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1zZWN1cml0eS13ZWI8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuMy5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQoNCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5jb20uc3ByaW5nc291cmNlLmluc2lnaHQ8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5pbnNpZ2h0LWFubm90YXRpb248L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjAuMC5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICAgICAgPHNjb3BlPnByb3ZpZGVkPC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KDQogICAgICAgIDwhLS0gU0VSVkxFVCBERVBFTkRFTkNJRVMgLS0+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+amF2YXguc2VydmxldDwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNlcnZsZXQtYXBpPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+Mi41PC92ZXJzaW9uPg0KICAgICAgICAgICAgPHNjb3BlPnByb3ZpZGVkPC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KDQogICAgICAgIDwhLS0gTE9HR0lORyBERVBFTkRFTkNJRVMgLSBMT0c0SiAtLT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5jaC5xb3MubG9nYmFjazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPmxvZ2JhY2stY2xhc3NpYzwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjAuOS4yNjwvdmVyc2lvbj4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KDQogICAgICAgIDwhLS0gQVBBQ0hFIENPTU1PTlMgREVQRU5ERU5DSUVTIC0tPg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPmNvbW1vbnMtaW88L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5jb21tb25zLWlvPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+Mi4wPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+Y29tbW9ucy1sYW5nPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+Y29tbW9ucy1sYW5nPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+Mi41PC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLmFwYWNoZS5jb21tb25zPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+Y29tbW9ucy1lbWFpbDwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjEuMjwvdmVyc2lvbj4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPmNvbW1vbnMtYmVhbnV0aWxzPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+Y29tbW9ucy1iZWFudXRpbHM8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjguMzwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxleGNsdXNpb25zPg0KICAgICAgICAgICAgICAgIDxleGNsdXNpb24+DQogICAgICAgICAgICAgICAgICAgIDxhcnRpZmFjdElkPmNvbW1vbnMtbG9nZ2luZzwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgICAgICAgICAgPGdyb3VwSWQ+Y29tbW9ucy1sb2dnaW5nPC9ncm91cElkPg0KICAgICAgICAgICAgICAgIDwvZXhjbHVzaW9uPg0KICAgICAgICAgICAgPC9leGNsdXNpb25zPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+Y29tbW9ucy1jb2RlYzwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPmNvbW1vbnMtY29kZWM8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjQ8L3ZlcnNpb24+DQogICAgICAgIDwvZGVwZW5kZW5jeT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5jb21tb25zLWNvbmZpZ3VyYXRpb248L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5jb21tb25zLWNvbmZpZ3VyYXRpb248L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjY8L3ZlcnNpb24+DQogICAgICAgIDwvZGVwZW5kZW5jeT4NCg0KICAgICAgICA8IS0tIEhJQkVSTkFURSBERVBFTkRFTkNJRVMgLS0+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLmhpYmVybmF0ZTwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPmhpYmVybmF0ZS1jb3JlPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+My42LjAuRmluYWw8L3ZlcnNpb24+DQogICAgICAgICAgICA8ZXhjbHVzaW9ucz4NCiAgICAgICAgICAgICAgICA8ZXhjbHVzaW9uPg0KICAgICAgICAgICAgICAgICAgICA8YXJ0aWZhY3RJZD5zbGY0ai1hcGk8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICAgICAgICAgIDxncm91cElkPm9yZy5zbGY0ajwvZ3JvdXBJZD4NCiAgICAgICAgICAgICAgICA8L2V4Y2x1c2lvbj4NCiAgICAgICAgICAgICAgICA8ZXhjbHVzaW9uPg0KICAgICAgICAgICAgICAgICAgICA8YXJ0aWZhY3RJZD5jb21tb25zLWNvbGxlY3Rpb25zPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgICAgICAgICA8Z3JvdXBJZD5jb21tb25zLWNvbGxlY3Rpb25zPC9ncm91cElkPg0KICAgICAgICAgICAgICAgIDwvZXhjbHVzaW9uPg0KICAgICAgICAgICAgPC9leGNsdXNpb25zPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+amF2YXNzaXN0PC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+amF2YXNzaXN0PC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+My4xMS4wLkdBPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+aHNxbGRiPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+aHNxbGRiPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+MS44LjAuMTA8L3ZlcnNpb24+DQogICAgICAgICAgICA8c2NvcGU+dGVzdDwvc2NvcGU+DQogICAgICAgIDwvZGVwZW5kZW5jeT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5jb20ubWljcm9zb2Z0PC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+c3FsamRiYzwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjIuMDwvdmVyc2lvbj4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KDQogICAgICAgIDwhLS0gVU5JVCBURVNUSU5HIC0tPg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy51bml0aWxzPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+dW5pdGlsczwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjIuNDwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxzY29wZT50ZXN0PC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPmp1bml0PC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+anVuaXQ8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj40LjguMjwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxzY29wZT50ZXN0PC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy5kYnVuaXQ8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5kYnVuaXQ8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4yLjQuODwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxzY29wZT50ZXN0PC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy5tb2NraXRvPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+bW9ja2l0by1hbGw8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjguNTwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxzY29wZT50ZXN0PC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPnhtbHVuaXQ8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD54bWx1bml0PC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+MS4zPC92ZXJzaW9uPg0KICAgICAgICAgICAgPHNjb3BlPnRlc3Q8L3Njb3BlPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQoNCiAgICAgICAgPCEtLSBPVEhFUiAtLT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5vcmcuYXBhY2hlLnNvbHI8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5zb2xyLXNvbHJqPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+MS40LjE8L3ZlcnNpb24+DQogICAgICAgICAgICA8ZXhjbHVzaW9ucz4NCiAgICAgICAgICAgICAgICA8ZXhjbHVzaW9uPg0KICAgICAgICAgICAgICAgICAgICA8YXJ0aWZhY3RJZD5zbGY0ai1hcGk8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICAgICAgICAgIDxncm91cElkPm9yZy5zbGY0ajwvZ3JvdXBJZD4NCiAgICAgICAgICAgICAgICA8L2V4Y2x1c2lvbj4NCiAgICAgICAgICAgIDwvZXhjbHVzaW9ucz4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy5jb3MuY29tbW9uPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+Y29zLXZhbGlkYXRvcjwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjEuMC4wPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+Y29tLmdvb2dsZS5ndWF2YTwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPmd1YXZhPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+cjA3PC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQoNCiAgICA8L2RlcGVuZGVuY2llcz4NCg0KICAgIDxwcm9maWxlcz4NCiAgICAgICAgPHByb2ZpbGU+DQogICAgICAgICAgICA8aWQ+anJlYmVsPC9pZD4NCiAgICAgICAgICAgIDxhY3RpdmF0aW9uPg0KICAgICAgICAgICAgICAgIDxhY3RpdmVCeURlZmF1bHQ+ZmFsc2U8L2FjdGl2ZUJ5RGVmYXVsdD4NCiAgICAgICAgICAgIDwvYWN0aXZhdGlvbj4NCiAgICAgICAgICAgIDxidWlsZD4NCiAgICAgICAgICAgICAgICA8cGx1Z2lucz4NCiAgICAgICAgICAgICAgICAgICAgPHBsdWdpbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxncm91cElkPm9yZy56ZXJvdHVybmFyb3VuZDwvZ3JvdXBJZD4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhcnRpZmFjdElkPmphdmFyZWJlbC1tYXZlbi1wbHVnaW48L2FydGlmYWN0SWQ+DQogICAgICAgICAgICAgICAgICAgICAgICA8dmVyc2lvbj4xLjAuNTwvdmVyc2lvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxleGVjdXRpb25zPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxleGVjdXRpb24+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpZD5nZW5lcmF0ZS1yZWJlbC14bWw8L2lkPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cGhhc2U+cHJvY2Vzcy1yZXNvdXJjZXM8L3BoYXNlPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Z29hbHM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Z29hbD5nZW5lcmF0ZTwvZ29hbD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9nb2Fscz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2V4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhlY3V0aW9ucz4NCiAgICAgICAgICAgICAgICAgICAgPC9wbHVnaW4+DQogICAgICAgICAgICAgICAgPC9wbHVnaW5zPg0KICAgICAgICAgICAgPC9idWlsZD4NCiAgICAgICAgICAgIDxwcm9wZXJ0aWVzPg0KICAgICAgICAgICAgICAgIDxqZXR0eS1zY2FuSW50ZXJ2YWxTZWNvbmRzPjA8L2pldHR5LXNjYW5JbnRlcnZhbFNlY29uZHM+DQogICAgICAgICAgICA8L3Byb3BlcnRpZXM+DQogICAgICAgIDwvcHJvZmlsZT4NCiAgICA8L3Byb2ZpbGVzPg0KPC9wcm9qZWN0Pg0K diff --git a/syft/pkg/cataloger/java/test-fixtures/pom/undeclared-iso-8859-encoded-pom.xml.base64 b/syft/pkg/cataloger/java/test-fixtures/pom/undeclared-iso-8859-encoded-pom.xml.base64 new file mode 100644 index 000000000..6b8c3ce71 --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/pom/undeclared-iso-8859-encoded-pom.xml.base64 @@ -0,0 +1 @@ +PHByb2plY3QgeG1sbnM9Imh0dHA6Ly9tYXZlbi5hcGFjaGUub3JnL1BPTS80LjAuMCIgeG1sbnM6eHNpPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYS1pbnN0YW5jZSIgeHNpOnNjaGVtYUxvY2F0aW9uPSJodHRwOi8vbWF2ZW4uYXBhY2hlLm9yZy9QT00vNC4wLjAgaHR0cDovL21hdmVuLmFwYWNoZS5vcmcvbWF2ZW4tdjRfMF8wLnhzZCI+DQogICAgPG1vZGVsVmVyc2lvbj40LjAuMDwvbW9kZWxWZXJzaW9uPg0KDQogICAgPG5hbWU+Q2FtZWxlb240SmF2YSAtIENvbnRlbnRTREs8L25hbWU+DQoNCiAgICA8Z3JvdXBJZD5jb20uYWxvZ2llbnQuY2FtZWxlb24uamF2YS5zZGs8L2dyb3VwSWQ+DQogICAgPGFydGlmYWN0SWQ+Y2FtZWxlb240amF2YS1zZGs8L2FydGlmYWN0SWQ+DQoNCiAgICA8dmVyc2lvbj4xLjEyLjI8L3ZlcnNpb24+DQoNCiAgICA8cGFja2FnaW5nPmphcjwvcGFja2FnaW5nPg0KDQogICAgPHNjbT4NCiAgICAgICAgPGNvbm5lY3Rpb24+c2NtOmdpdDpzc2g6Ly9naXRAZ2l0aHViLmNvbS9BbG9naWVudC9DT1M1LVNESy5naXQ8L2Nvbm5lY3Rpb24+DQogICAgICAgIDxkZXZlbG9wZXJDb25uZWN0aW9uPnNjbTpnaXQ6c3NoOi8vZ2l0QGdpdGh1Yi5jb20vQWxvZ2llbnQvQ09TNS1TREsuZ2l0PC9kZXZlbG9wZXJDb25uZWN0aW9uPg0KICAgICAgICA8dXJsPmdpdEBnaXRodWIuY29tOkFsb2dpZW50L0NPUzUtU0RLLmdpdDwvdXJsPg0KICAgIDwvc2NtPg0KDQogICAgPGRlc2NyaXB0aW9uPkNhbWVsZW9uIGlzIGEgQ01TIGRlZGljYXRlZCB0byBkZXZlbG9wZXJzIGFzIHdlbGwgYXMgbm9uLUlUIGNvbnRyaWJ1dG9ycy48L2Rlc2NyaXB0aW9uPg0KICAgIA0KICAgIDxvcmdhbml6YXRpb24+DQogICAgICAgIDxuYW1lPkFsb2dpZW50PC9uYW1lPg0KICAgICAgICA8dXJsPmh0dHA6Ly93d3cuYWxvZ2llbnQuY29tPC91cmw+DQogICAgPC9vcmdhbml6YXRpb24+DQoNCiAgICA8dXJsPmh0dHA6Ly93d3cuY2FtZWxlb25jbXMuY29tPC91cmw+DQoNCiAgICA8ZGV2ZWxvcGVycz4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5hbW9yaW48L2lkPg0KICAgICAgICAgICAgPG5hbWU+QXJuYXVkIE1vcmluPC9uYW1lPg0KICAgICAgICAgICAgPGVtYWlsPmFtb3JpbkBhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5hbG9yZDwvaWQ+DQogICAgICAgICAgICA8bmFtZT5BbGV4YW5kcmUgTG9yZDwvbmFtZT4NCiAgICAgICAgICAgIDxlbWFpbD5hbG9yZEBhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5kam9tcGhlPC9pZD4NCiAgICAgICAgICAgIDxuYW1lPkRhbmllbCBKb21waGU8L25hbWU+DQogICAgICAgICAgICA8ZW1haWw+ZGpvbXBoZUBhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5qbWlyYzwvaWQ+DQogICAgICAgICAgICA8bmFtZT5K6XL0bWUgTWlyYzwvbmFtZT4NCiAgICAgICAgICAgIDxlbWFpbD5qbWlyY0BhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICAgICAgPGRldmVsb3Blcj4NCiAgICAgICAgICAgIDxpZD5wbHZlaWxsZXV4PC9pZD4NCiAgICAgICAgICAgIDxuYW1lPlBpZXJyZS1MdWMgVmVpbGxldXg8L25hbWU+DQogICAgICAgICAgICA8ZW1haWw+cGx2ZWlsbGV1eEBhbG9naWVudC5jb208L2VtYWlsPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvbj5BbG9naWVudDwvb3JnYW5pemF0aW9uPg0KICAgICAgICAgICAgPG9yZ2FuaXphdGlvblVybD5odHRwOi8vd3d3LmFsb2dpZW50LmNvbTwvb3JnYW5pemF0aW9uVXJsPg0KICAgICAgICA8L2RldmVsb3Blcj4NCiAgICA8L2RldmVsb3BlcnM+DQoNCg0KICAgIDxsaWNlbnNlcz4NCiAgICAgICAgPGxpY2Vuc2U+DQogICAgICAgICAgICA8bmFtZT5UaGUgQXBhY2hlIFNvZnR3YXJlIExpY2Vuc2UsIFZlcnNpb24gMi4wPC9uYW1lPg0KICAgICAgICAgICAgPHVybD5odHRwOi8vd3d3LmFwYWNoZS5vcmcvbGljZW5zZXMvTElDRU5TRS0yLjAudHh0PC91cmw+DQogICAgICAgICAgICA8ZGlzdHJpYnV0aW9uPnJlcG88L2Rpc3RyaWJ1dGlvbj4NCiAgICAgICAgPC9saWNlbnNlPg0KICAgIDwvbGljZW5zZXM+DQoNCiAgICA8YnVpbGQ+DQoNCiAgICAgICAgPHJlc291cmNlcz4NCiAgICAgICAgICAgIDxyZXNvdXJjZT4NCiAgICAgICAgICAgICAgICA8ZmlsdGVyaW5nPmZhbHNlPC9maWx0ZXJpbmc+DQogICAgICAgICAgICAgICAgPGRpcmVjdG9yeT5zcmMvbWFpbi9yZXNvdXJjZXM8L2RpcmVjdG9yeT4NCiAgICAgICAgICAgIDwvcmVzb3VyY2U+DQogICAgICAgICAgICA8cmVzb3VyY2U+DQogICAgICAgICAgICAgICAgPGZpbHRlcmluZz5mYWxzZTwvZmlsdGVyaW5nPg0KICAgICAgICAgICAgICAgIDxkaXJlY3Rvcnk+c3JjL21haW4vamF2YTwvZGlyZWN0b3J5Pg0KICAgICAgICAgICAgICAgIDxpbmNsdWRlcz4NCiAgICAgICAgICAgICAgICAgICAgPGluY2x1ZGU+Kio8L2luY2x1ZGU+DQogICAgICAgICAgICAgICAgPC9pbmNsdWRlcz4NCiAgICAgICAgICAgICAgICA8ZXhjbHVkZXM+DQogICAgICAgICAgICAgICAgICAgIDxleGNsdWRlPioqLyouamF2YTwvZXhjbHVkZT4NCiAgICAgICAgICAgICAgICA8L2V4Y2x1ZGVzPg0KICAgICAgICAgICAgPC9yZXNvdXJjZT4NCiAgICAgICAgPC9yZXNvdXJjZXM+DQogICAgICAgIDx0ZXN0UmVzb3VyY2VzPg0KICAgICAgICAgICAgPHRlc3RSZXNvdXJjZT4NCiAgICAgICAgICAgICAgICA8ZmlsdGVyaW5nPmZhbHNlPC9maWx0ZXJpbmc+DQogICAgICAgICAgICAgICAgPGRpcmVjdG9yeT5zcmMvdGVzdC9yZXNvdXJjZXM8L2RpcmVjdG9yeT4NCiAgICAgICAgICAgIDwvdGVzdFJlc291cmNlPg0KICAgICAgICAgICAgPHRlc3RSZXNvdXJjZT4NCiAgICAgICAgICAgICAgICA8ZmlsdGVyaW5nPmZhbHNlPC9maWx0ZXJpbmc+DQogICAgICAgICAgICAgICAgPGRpcmVjdG9yeT5zcmMvdGVzdC9qYXZhPC9kaXJlY3Rvcnk+DQogICAgICAgICAgICAgICAgPGluY2x1ZGVzPg0KICAgICAgICAgICAgICAgICAgICA8aW5jbHVkZT4qKjwvaW5jbHVkZT4NCiAgICAgICAgICAgICAgICA8L2luY2x1ZGVzPg0KICAgICAgICAgICAgICAgIDxleGNsdWRlcz4NCiAgICAgICAgICAgICAgICAgICAgPGV4Y2x1ZGU+KiovKi5qYXZhPC9leGNsdWRlPg0KICAgICAgICAgICAgICAgIDwvZXhjbHVkZXM+DQogICAgICAgICAgICA8L3Rlc3RSZXNvdXJjZT4NCiAgICAgICAgPC90ZXN0UmVzb3VyY2VzPg0KDQogICAgICAgIDxwbHVnaW5zPg0KICAgICAgICAgICAgPHBsdWdpbj4NCiAgICAgICAgICAgICAgICA8Z3JvdXBJZD5vcmcuYXBhY2hlLm1hdmVuLnBsdWdpbnM8L2dyb3VwSWQ+DQogICAgICAgICAgICAgICAgPGFydGlmYWN0SWQ+bWF2ZW4tY29tcGlsZXItcGx1Z2luPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgICAgIDx2ZXJzaW9uPjIuMy4yPC92ZXJzaW9uPg0KICAgICAgICAgICAgICAgIDxjb25maWd1cmF0aW9uPg0KICAgICAgICAgICAgICAgICAgICA8c291cmNlPjEuNjwvc291cmNlPg0KICAgICAgICAgICAgICAgICAgICA8dGFyZ2V0PjEuNjwvdGFyZ2V0Pg0KICAgICAgICAgICAgICAgIDwvY29uZmlndXJhdGlvbj4NCiAgICAgICAgICAgIDwvcGx1Z2luPg0KICAgICAgICAgICAgPHBsdWdpbj4NCiAgICAgICAgICAgICAgICA8Z3JvdXBJZD5vcmcuYXBhY2hlLm1hdmVuLnBsdWdpbnM8L2dyb3VwSWQ+DQogICAgICAgICAgICAgICAgPGFydGlmYWN0SWQ+bWF2ZW4tc291cmNlLXBsdWdpbjwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgICAgICA8dmVyc2lvbj4yLjEuMjwvdmVyc2lvbj4NCiAgICAgICAgICAgICAgICA8ZXhlY3V0aW9ucz4NCiAgICAgICAgICAgICAgICAgICAgPGV4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxpZD5hdHRhY2gtc291cmNlczwvaWQ+DQogICAgICAgICAgICAgICAgICAgICAgICA8cGhhc2U+dmVyaWZ5PC9waGFzZT4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxnb2Fscz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Z29hbD5qYXI8L2dvYWw+DQogICAgICAgICAgICAgICAgICAgICAgICA8L2dvYWxzPg0KICAgICAgICAgICAgICAgICAgICA8L2V4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICA8L2V4ZWN1dGlvbnM+DQogICAgICAgICAgICA8L3BsdWdpbj4NCiAgICAgICAgICAgIDxwbHVnaW4+DQogICAgICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLmFwYWNoZS5tYXZlbi5wbHVnaW5zPC9ncm91cElkPg0KICAgICAgICAgICAgICAgIDxhcnRpZmFjdElkPm1hdmVuLWphdmFkb2MtcGx1Z2luPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgICAgIDx2ZXJzaW9uPjIuNzwvdmVyc2lvbj4NCiAgICAgICAgICAgICAgICA8ZXhlY3V0aW9ucz4NCiAgICAgICAgICAgICAgICAgICAgPGV4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxpZD5hdHRhY2gtamF2YWRvY3M8L2lkPg0KICAgICAgICAgICAgICAgICAgICAgICAgPGdvYWxzPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxnb2FsPmphcjwvZ29hbD4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvZ29hbHM+DQogICAgICAgICAgICAgICAgICAgIDwvZXhlY3V0aW9uPg0KICAgICAgICAgICAgICAgIDwvZXhlY3V0aW9ucz4NCiAgICAgICAgICAgIDwvcGx1Z2luPg0KICAgICAgICAgICAgPHBsdWdpbj4NCiAgICAgICAgICAgICAgICA8Z3JvdXBJZD5vcmcuYXBhY2hlLm1hdmVuLnBsdWdpbnM8L2dyb3VwSWQ+DQogICAgICAgICAgICAgICAgPGFydGlmYWN0SWQ+bWF2ZW4tcmVsZWFzZS1wbHVnaW48L2FydGlmYWN0SWQ+DQogICAgICAgICAgICAgICAgPHZlcnNpb24+Mi4xPC92ZXJzaW9uPg0KICAgICAgICAgICAgPC9wbHVnaW4+DQoJCQkJICAgIDxwbHVnaW4+DQogICAgICAJCQkJICA8Z3JvdXBJZD5vcmcuYXBhY2hlLm1hdmVuLnBsdWdpbnM8L2dyb3VwSWQ+DQogICAgICAgIAkJCQk8YXJ0aWZhY3RJZD5tYXZlbi1yZXBvc2l0b3J5LXBsdWdpbjwvYXJ0aWZhY3RJZD4NCiAgICAgICAgCQkJCTx2ZXJzaW9uPjIuMy4xPC92ZXJzaW9uPg0KICAgICAgCQkJPC9wbHVnaW4+DQogICAgICAgICAgICA8cGx1Z2luPg0KICAgICAgICAgICAgICAgIDxncm91cElkPm9yZy5hcGFjaGUubWF2ZW4ucGx1Z2luczwvZ3JvdXBJZD4NCiAgICAgICAgICAgICAgICA8YXJ0aWZhY3RJZD5tYXZlbi1ncGctcGx1Z2luPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgICAgIDx2ZXJzaW9uPjEuMTwvdmVyc2lvbj4NCiAgICAgICAgICAgICAgICA8ZXhlY3V0aW9ucz4NCiAgICAgICAgICAgICAgICAgICAgPGV4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxpZD5zaWduLWFydGlmYWN0czwvaWQ+DQogICAgICAgICAgICAgICAgICAgICAgICA8cGhhc2U+dmVyaWZ5PC9waGFzZT4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxnb2Fscz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Z29hbD5zaWduPC9nb2FsPg0KICAgICAgICAgICAgICAgICAgICAgICAgPC9nb2Fscz4NCiAgICAgICAgICAgICAgICAgICAgPC9leGVjdXRpb24+DQogICAgICAgICAgICAgICAgPC9leGVjdXRpb25zPg0KICAgICAgICAgICAgPC9wbHVnaW4+DQogICAgICAJIDwvcGx1Z2lucz4NCiAgICA8L2J1aWxkPg0KDQogICAgPGRlcGVuZGVuY2llcz4NCg0KICAgICAgICA8IS0tIFNQUklORyBERVBFTkRFTkNJRVMgLS0+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1jb3JlPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+My4wLjQuUkVMRUFTRTwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxleGNsdXNpb25zPg0KICAgICAgICAgICAgICAgIDxleGNsdXNpb24+DQogICAgICAgICAgICAgICAgICAgIDxhcnRpZmFjdElkPmNvbW1vbnMtbG9nZ2luZzwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgICAgICAgICAgPGdyb3VwSWQ+Y29tbW9ucy1sb2dnaW5nPC9ncm91cElkPg0KICAgICAgICAgICAgICAgIDwvZXhjbHVzaW9uPg0KICAgICAgICAgICAgPC9leGNsdXNpb25zPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy13ZWI8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuNC5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1vcm08L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuNC5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1hc3BlY3RzPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+My4wLjQuUkVMRUFTRTwvdmVyc2lvbj4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy5zcHJpbmdmcmFtZXdvcms8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5zcHJpbmctZXhwcmVzc2lvbjwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjMuMC40LlJFTEVBU0U8L3ZlcnNpb24+DQogICAgICAgIDwvZGVwZW5kZW5jeT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5vcmcuc3ByaW5nZnJhbWV3b3JrLnNlY3VyaXR5PC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+c3ByaW5nLXNlY3VyaXR5LWNvcmU8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuMy5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yay5zZWN1cml0eTwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1zZWN1cml0eS1jb25maWc8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuMy5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLnNwcmluZ2ZyYW1ld29yay5zZWN1cml0eTwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNwcmluZy1zZWN1cml0eS13ZWI8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4zLjAuMy5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQoNCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5jb20uc3ByaW5nc291cmNlLmluc2lnaHQ8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5pbnNpZ2h0LWFubm90YXRpb248L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjAuMC5SRUxFQVNFPC92ZXJzaW9uPg0KICAgICAgICAgICAgPHNjb3BlPnByb3ZpZGVkPC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KDQogICAgICAgIDwhLS0gU0VSVkxFVCBERVBFTkRFTkNJRVMgLS0+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+amF2YXguc2VydmxldDwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPnNlcnZsZXQtYXBpPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+Mi41PC92ZXJzaW9uPg0KICAgICAgICAgICAgPHNjb3BlPnByb3ZpZGVkPC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KDQogICAgICAgIDwhLS0gTE9HR0lORyBERVBFTkRFTkNJRVMgLSBMT0c0SiAtLT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5jaC5xb3MubG9nYmFjazwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPmxvZ2JhY2stY2xhc3NpYzwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjAuOS4yNjwvdmVyc2lvbj4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KDQogICAgICAgIDwhLS0gQVBBQ0hFIENPTU1PTlMgREVQRU5ERU5DSUVTIC0tPg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPmNvbW1vbnMtaW88L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5jb21tb25zLWlvPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+Mi4wPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+Y29tbW9ucy1sYW5nPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+Y29tbW9ucy1sYW5nPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+Mi41PC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLmFwYWNoZS5jb21tb25zPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+Y29tbW9ucy1lbWFpbDwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjEuMjwvdmVyc2lvbj4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPmNvbW1vbnMtYmVhbnV0aWxzPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+Y29tbW9ucy1iZWFudXRpbHM8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjguMzwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxleGNsdXNpb25zPg0KICAgICAgICAgICAgICAgIDxleGNsdXNpb24+DQogICAgICAgICAgICAgICAgICAgIDxhcnRpZmFjdElkPmNvbW1vbnMtbG9nZ2luZzwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgICAgICAgICAgPGdyb3VwSWQ+Y29tbW9ucy1sb2dnaW5nPC9ncm91cElkPg0KICAgICAgICAgICAgICAgIDwvZXhjbHVzaW9uPg0KICAgICAgICAgICAgPC9leGNsdXNpb25zPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+Y29tbW9ucy1jb2RlYzwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPmNvbW1vbnMtY29kZWM8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjQ8L3ZlcnNpb24+DQogICAgICAgIDwvZGVwZW5kZW5jeT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5jb21tb25zLWNvbmZpZ3VyYXRpb248L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5jb21tb25zLWNvbmZpZ3VyYXRpb248L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjY8L3ZlcnNpb24+DQogICAgICAgIDwvZGVwZW5kZW5jeT4NCg0KICAgICAgICA8IS0tIEhJQkVSTkFURSBERVBFTkRFTkNJRVMgLS0+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+b3JnLmhpYmVybmF0ZTwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPmhpYmVybmF0ZS1jb3JlPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+My42LjAuRmluYWw8L3ZlcnNpb24+DQogICAgICAgICAgICA8ZXhjbHVzaW9ucz4NCiAgICAgICAgICAgICAgICA8ZXhjbHVzaW9uPg0KICAgICAgICAgICAgICAgICAgICA8YXJ0aWZhY3RJZD5zbGY0ai1hcGk8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICAgICAgICAgIDxncm91cElkPm9yZy5zbGY0ajwvZ3JvdXBJZD4NCiAgICAgICAgICAgICAgICA8L2V4Y2x1c2lvbj4NCiAgICAgICAgICAgICAgICA8ZXhjbHVzaW9uPg0KICAgICAgICAgICAgICAgICAgICA8YXJ0aWZhY3RJZD5jb21tb25zLWNvbGxlY3Rpb25zPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgICAgICAgICA8Z3JvdXBJZD5jb21tb25zLWNvbGxlY3Rpb25zPC9ncm91cElkPg0KICAgICAgICAgICAgICAgIDwvZXhjbHVzaW9uPg0KICAgICAgICAgICAgPC9leGNsdXNpb25zPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+amF2YXNzaXN0PC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+amF2YXNzaXN0PC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+My4xMS4wLkdBPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+aHNxbGRiPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+aHNxbGRiPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+MS44LjAuMTA8L3ZlcnNpb24+DQogICAgICAgICAgICA8c2NvcGU+dGVzdDwvc2NvcGU+DQogICAgICAgIDwvZGVwZW5kZW5jeT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5jb20ubWljcm9zb2Z0PC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+c3FsamRiYzwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjIuMDwvdmVyc2lvbj4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KDQogICAgICAgIDwhLS0gVU5JVCBURVNUSU5HIC0tPg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy51bml0aWxzPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+dW5pdGlsczwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjIuNDwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxzY29wZT50ZXN0PC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPmp1bml0PC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+anVuaXQ8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj40LjguMjwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxzY29wZT50ZXN0PC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy5kYnVuaXQ8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5kYnVuaXQ8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4yLjQuODwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxzY29wZT50ZXN0PC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy5tb2NraXRvPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+bW9ja2l0by1hbGw8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICA8dmVyc2lvbj4xLjguNTwvdmVyc2lvbj4NCiAgICAgICAgICAgIDxzY29wZT50ZXN0PC9zY29wZT4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPnhtbHVuaXQ8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD54bWx1bml0PC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+MS4zPC92ZXJzaW9uPg0KICAgICAgICAgICAgPHNjb3BlPnRlc3Q8L3Njb3BlPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQoNCiAgICAgICAgPCEtLSBPVEhFUiAtLT4NCiAgICAgICAgPGRlcGVuZGVuY3k+DQogICAgICAgICAgICA8Z3JvdXBJZD5vcmcuYXBhY2hlLnNvbHI8L2dyb3VwSWQ+DQogICAgICAgICAgICA8YXJ0aWZhY3RJZD5zb2xyLXNvbHJqPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+MS40LjE8L3ZlcnNpb24+DQogICAgICAgICAgICA8ZXhjbHVzaW9ucz4NCiAgICAgICAgICAgICAgICA8ZXhjbHVzaW9uPg0KICAgICAgICAgICAgICAgICAgICA8YXJ0aWZhY3RJZD5zbGY0ai1hcGk8L2FydGlmYWN0SWQ+DQogICAgICAgICAgICAgICAgICAgIDxncm91cElkPm9yZy5zbGY0ajwvZ3JvdXBJZD4NCiAgICAgICAgICAgICAgICA8L2V4Y2x1c2lvbj4NCiAgICAgICAgICAgIDwvZXhjbHVzaW9ucz4NCiAgICAgICAgPC9kZXBlbmRlbmN5Pg0KICAgICAgICA8ZGVwZW5kZW5jeT4NCiAgICAgICAgICAgIDxncm91cElkPm9yZy5jb3MuY29tbW9uPC9ncm91cElkPg0KICAgICAgICAgICAgPGFydGlmYWN0SWQ+Y29zLXZhbGlkYXRvcjwvYXJ0aWZhY3RJZD4NCiAgICAgICAgICAgIDx2ZXJzaW9uPjEuMC4wPC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQogICAgICAgIDxkZXBlbmRlbmN5Pg0KICAgICAgICAgICAgPGdyb3VwSWQ+Y29tLmdvb2dsZS5ndWF2YTwvZ3JvdXBJZD4NCiAgICAgICAgICAgIDxhcnRpZmFjdElkPmd1YXZhPC9hcnRpZmFjdElkPg0KICAgICAgICAgICAgPHZlcnNpb24+cjA3PC92ZXJzaW9uPg0KICAgICAgICA8L2RlcGVuZGVuY3k+DQoNCiAgICA8L2RlcGVuZGVuY2llcz4NCg0KICAgIDxwcm9maWxlcz4NCiAgICAgICAgPHByb2ZpbGU+DQogICAgICAgICAgICA8aWQ+anJlYmVsPC9pZD4NCiAgICAgICAgICAgIDxhY3RpdmF0aW9uPg0KICAgICAgICAgICAgICAgIDxhY3RpdmVCeURlZmF1bHQ+ZmFsc2U8L2FjdGl2ZUJ5RGVmYXVsdD4NCiAgICAgICAgICAgIDwvYWN0aXZhdGlvbj4NCiAgICAgICAgICAgIDxidWlsZD4NCiAgICAgICAgICAgICAgICA8cGx1Z2lucz4NCiAgICAgICAgICAgICAgICAgICAgPHBsdWdpbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxncm91cElkPm9yZy56ZXJvdHVybmFyb3VuZDwvZ3JvdXBJZD4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxhcnRpZmFjdElkPmphdmFyZWJlbC1tYXZlbi1wbHVnaW48L2FydGlmYWN0SWQ+DQogICAgICAgICAgICAgICAgICAgICAgICA8dmVyc2lvbj4xLjAuNTwvdmVyc2lvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDxleGVjdXRpb25zPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxleGVjdXRpb24+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxpZD5nZW5lcmF0ZS1yZWJlbC14bWw8L2lkPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8cGhhc2U+cHJvY2Vzcy1yZXNvdXJjZXM8L3BoYXNlPg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Z29hbHM+DQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8Z29hbD5nZW5lcmF0ZTwvZ29hbD4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgPC9nb2Fscz4NCiAgICAgICAgICAgICAgICAgICAgICAgICAgICA8L2V4ZWN1dGlvbj4NCiAgICAgICAgICAgICAgICAgICAgICAgIDwvZXhlY3V0aW9ucz4NCiAgICAgICAgICAgICAgICAgICAgPC9wbHVnaW4+DQogICAgICAgICAgICAgICAgPC9wbHVnaW5zPg0KICAgICAgICAgICAgPC9idWlsZD4NCiAgICAgICAgICAgIDxwcm9wZXJ0aWVzPg0KICAgICAgICAgICAgICAgIDxqZXR0eS1zY2FuSW50ZXJ2YWxTZWNvbmRzPjA8L2pldHR5LXNjYW5JbnRlcnZhbFNlY29uZHM+DQogICAgICAgICAgICA8L3Byb3BlcnRpZXM+DQogICAgICAgIDwvcHJvZmlsZT4NCiAgICA8L3Byb2ZpbGVzPg0KPC9wcm9qZWN0Pg0K From a2b389523d8a72df9588e316896779a434d6dbe9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 13:41:17 -0400 Subject: [PATCH 015/106] chore(deps): bump github.com/charmbracelet/lipgloss from 0.7.1 to 0.8.0 (#2053) Bumps [github.com/charmbracelet/lipgloss](https://github.com/charmbracelet/lipgloss) from 0.7.1 to 0.8.0. - [Release notes](https://github.com/charmbracelet/lipgloss/releases) - [Commits](https://github.com/charmbracelet/lipgloss/compare/v0.7.1...v0.8.0) --- updated-dependencies: - dependency-name: github.com/charmbracelet/lipgloss dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index a83f439b4..4fdfd2d76 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,7 @@ require ( github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 github.com/charmbracelet/bubbletea v0.24.2 - github.com/charmbracelet/lipgloss v0.7.1 + github.com/charmbracelet/lipgloss v0.8.0 github.com/dave/jennifer v1.7.0 github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da github.com/docker/distribution v2.8.2+incompatible @@ -146,7 +146,7 @@ require ( github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.15.1 // indirect + github.com/muesli/termenv v0.15.2 // indirect github.com/nwaples/rardecode v1.1.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc3 // indirect github.com/pborman/indent v1.2.1 // indirect diff --git a/go.sum b/go.sum index 631828a1a..e483614ab 100644 --- a/go.sum +++ b/go.sum @@ -147,8 +147,8 @@ github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06 github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E= -github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c= +github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU= +github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -529,8 +529,8 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs= -github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ= +github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= From 07ac640ac5b53c9f507ccb9189c2364edcb4909e Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Wed, 23 Aug 2023 19:51:07 +0200 Subject: [PATCH 016/106] fix: properly parse conan ref and include user and channel (#2034) * fix: properly parse conan ref and include user and channel Signed-off-by: Stefan Profanter * unexport the conanRef type Signed-off-by: Alex Goodman --------- Signed-off-by: Stefan Profanter Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman --- syft/pkg/cataloger/cpp/package.go | 95 ++++++++++++++----- syft/pkg/cataloger/cpp/parse_conanfile.go | 2 +- .../pkg/cataloger/cpp/parse_conanfile_test.go | 10 +- .../pkg/cataloger/cpp/parse_conanlock_test.go | 43 +++++++-- .../cataloger/cpp/test-fixtures/conan.lock | 23 +++-- .../cataloger/cpp/test-fixtures/conanfile.txt | 6 +- 6 files changed, 131 insertions(+), 48 deletions(-) diff --git a/syft/pkg/cataloger/cpp/package.go b/syft/pkg/cataloger/cpp/package.go index dbbdd0b90..b093c928d 100644 --- a/syft/pkg/cataloger/cpp/package.go +++ b/syft/pkg/cataloger/cpp/package.go @@ -8,23 +8,67 @@ import ( "github.com/anchore/syft/syft/pkg" ) -func newConanfilePackage(m pkg.ConanMetadata, locations ...file.Location) *pkg.Package { - fields := strings.Split(strings.TrimSpace(m.Ref), "/") - if len(fields) < 2 { - return nil +type conanRef struct { + Name string + Version string + User string + Channel string + Revision string + Timestamp string +} + +func splitConanRef(ref string) *conanRef { + // Conan ref format is: + // pkg/0.1@user/channel#rrev%timestamp + // This method is based on conan's ref.loads method: + // https://github.com/conan-io/conan/blob/release/2.0/conans/model/recipe_ref.py#L93C21-L93C21 + + var cref conanRef + + // timestamp + tokens := strings.Split(ref, "%") + text := tokens[0] + if len(tokens) == 2 { + cref.Timestamp = tokens[1] } - pkgName, pkgVersion := fields[0], fields[1] + // revision + tokens = strings.Split(text, "#") + ref = tokens[0] + if len(tokens) == 2 { + cref.Revision = tokens[1] + } - if pkgName == "" || pkgVersion == "" { + // name and version are always given + tokens = strings.Split(ref, "@") + nameAndVersion := strings.Split(tokens[0], "/") + if len(nameAndVersion) < 2 || nameAndVersion[0] == "" || nameAndVersion[1] == "" { + return nil + } + cref.Name = nameAndVersion[0] + cref.Version = nameAndVersion[1] + // user and channel + if len(tokens) == 2 && tokens[1] != "" { + tokens = strings.Split(tokens[1], "/") + if len(tokens) == 2 { + cref.User = tokens[0] + cref.Channel = tokens[1] + } + } + return &cref +} + +func newConanfilePackage(m pkg.ConanMetadata, locations ...file.Location) *pkg.Package { + ref := splitConanRef(m.Ref) + if ref == nil { return nil } p := pkg.Package{ - Name: pkgName, - Version: pkgVersion, + Name: ref.Name, + Version: ref.Version, Locations: file.NewLocationSet(locations...), - PURL: packageURL(pkgName, pkgVersion), + PURL: packageURL(ref), Language: pkg.CPP, Type: pkg.ConanPkg, MetadataType: pkg.ConanMetadataType, @@ -37,22 +81,16 @@ func newConanfilePackage(m pkg.ConanMetadata, locations ...file.Location) *pkg.P } func newConanlockPackage(m pkg.ConanLockMetadata, locations ...file.Location) *pkg.Package { - fields := strings.Split(strings.Split(m.Ref, "@")[0], "/") - if len(fields) < 2 { - return nil - } - - pkgName, pkgVersion := fields[0], fields[1] - - if pkgName == "" || pkgVersion == "" { + ref := splitConanRef(m.Ref) + if ref == nil { return nil } p := pkg.Package{ - Name: pkgName, - Version: pkgVersion, + Name: ref.Name, + Version: ref.Version, Locations: file.NewLocationSet(locations...), - PURL: packageURL(pkgName, pkgVersion), + PURL: packageURL(ref), Language: pkg.CPP, Type: pkg.ConanPkg, MetadataType: pkg.ConanLockMetadataType, @@ -64,13 +102,20 @@ func newConanlockPackage(m pkg.ConanLockMetadata, locations ...file.Location) *p return &p } -func packageURL(name, version string) string { +func packageURL(ref *conanRef) string { + qualifiers := packageurl.Qualifiers{} + if ref.Channel != "" { + qualifiers = append(qualifiers, packageurl.Qualifier{ + Key: "channel", + Value: ref.Channel, + }) + } return packageurl.NewPackageURL( packageurl.TypeConan, - "", - name, - version, - nil, // TODO: no qualifiers (...yet) + ref.User, + ref.Name, + ref.Version, + qualifiers, "", ).ToString() } diff --git a/syft/pkg/cataloger/cpp/parse_conanfile.go b/syft/pkg/cataloger/cpp/parse_conanfile.go index f9ae172f3..bf60706db 100644 --- a/syft/pkg/cataloger/cpp/parse_conanfile.go +++ b/syft/pkg/cataloger/cpp/parse_conanfile.go @@ -36,7 +36,7 @@ func parseConanfile(_ file.Resolver, _ *generic.Environment, reader file.Locatio switch { case strings.Contains(line, "[requires]"): inRequirements = true - case strings.ContainsAny(line, "[]#"): + case strings.ContainsAny(line, "[]") || strings.HasPrefix(strings.TrimSpace(line), "#"): inRequirements = false } diff --git a/syft/pkg/cataloger/cpp/parse_conanfile_test.go b/syft/pkg/cataloger/cpp/parse_conanfile_test.go index bca49223a..93ba846a2 100644 --- a/syft/pkg/cataloger/cpp/parse_conanfile_test.go +++ b/syft/pkg/cataloger/cpp/parse_conanfile_test.go @@ -52,13 +52,13 @@ func TestParseConanfile(t *testing.T) { { Name: "spdlog", Version: "1.9.2", - PURL: "pkg:conan/spdlog@1.9.2", + PURL: "pkg:conan/my_user/spdlog@1.9.2?channel=my_channel", Locations: fixtureLocationSet, Language: pkg.CPP, Type: pkg.ConanPkg, MetadataType: pkg.ConanMetadataType, Metadata: pkg.ConanMetadata{ - Ref: "spdlog/1.9.2", + Ref: "spdlog/1.9.2@my_user/my_channel#1234567%%987654", }, }, { @@ -70,19 +70,19 @@ func TestParseConanfile(t *testing.T) { Type: pkg.ConanPkg, MetadataType: pkg.ConanMetadataType, Metadata: pkg.ConanMetadata{ - Ref: "sdl/2.0.20", + Ref: "sdl/2.0.20#1234567%%987654", }, }, { Name: "fltk", Version: "1.3.8", - PURL: "pkg:conan/fltk@1.3.8", + PURL: "pkg:conan/my_user/fltk@1.3.8?channel=my_channel", Locations: fixtureLocationSet, Language: pkg.CPP, Type: pkg.ConanPkg, MetadataType: pkg.ConanMetadataType, Metadata: pkg.ConanMetadata{ - Ref: "fltk/1.3.8", + Ref: "fltk/1.3.8@my_user/my_channel", }, }, } diff --git a/syft/pkg/cataloger/cpp/parse_conanlock_test.go b/syft/pkg/cataloger/cpp/parse_conanlock_test.go index b699081de..66dc9ca92 100644 --- a/syft/pkg/cataloger/cpp/parse_conanlock_test.go +++ b/syft/pkg/cataloger/cpp/parse_conanlock_test.go @@ -13,20 +13,49 @@ func TestParseConanlock(t *testing.T) { fixture := "test-fixtures/conan.lock" expected := []pkg.Package{ { - Name: "zlib", - Version: "1.2.12", - PURL: "pkg:conan/zlib@1.2.12", + Name: "spdlog", + Version: "1.11.0", + PURL: "pkg:conan/spdlog@1.11.0", Locations: file.NewLocationSet(file.NewLocation(fixture)), Language: pkg.CPP, Type: pkg.ConanPkg, MetadataType: pkg.ConanLockMetadataType, Metadata: pkg.ConanLockMetadata{ - Ref: "zlib/1.2.12", + Ref: "spdlog/1.11.0", Options: map[string]string{ - "fPIC": "True", - "shared": "False", + "fPIC": "True", + "header_only": "False", + "no_exceptions": "False", + "shared": "False", + "wchar_filenames": "False", + "wchar_support": "False", + "fmt:fPIC": "True", + "fmt:header_only": "False", + "fmt:shared": "False", + "fmt:with_fmt_alias": "False", + "fmt:with_os_api": "True", + }, + Path: "conanfile.py", + Context: "host", + }, + }, + { + Name: "fmt", + Version: "9.1.0", + PURL: "pkg:conan/my_user/fmt@9.1.0?channel=my_channel", + Locations: file.NewLocationSet(file.NewLocation(fixture)), + Language: pkg.CPP, + Type: pkg.ConanPkg, + MetadataType: pkg.ConanLockMetadataType, + Metadata: pkg.ConanLockMetadata{ + Ref: "fmt/9.1.0@my_user/my_channel#6708c9d84f98d56a6d9f2e6c2d5639ba", + Options: map[string]string{ + "fPIC": "True", + "header_only": "False", + "shared": "False", + "with_fmt_alias": "False", + "with_os_api": "True", }, - Path: "all/conanfile.py", Context: "host", }, }, diff --git a/syft/pkg/cataloger/cpp/test-fixtures/conan.lock b/syft/pkg/cataloger/cpp/test-fixtures/conan.lock index cbae840d9..2632386b9 100644 --- a/syft/pkg/cataloger/cpp/test-fixtures/conan.lock +++ b/syft/pkg/cataloger/cpp/test-fixtures/conan.lock @@ -2,15 +2,24 @@ "graph_lock": { "nodes": { "0": { - "ref": "zlib/1.2.12", - "options": "fPIC=True\nshared=False", - "requires": [], - "path": "all/conanfile.py", + "ref": "spdlog/1.11.0", + "options": "fPIC=True\nheader_only=False\nno_exceptions=False\nshared=False\nwchar_filenames=False\nwchar_support=False\nfmt:fPIC=True\nfmt:header_only=False\nfmt:shared=False\nfmt:with_fmt_alias=False\nfmt:with_os_api=True", + "requires": [ + "1" + ], + "path": "conanfile.py", + "context": "host" + }, + "1": { + "ref": "fmt/9.1.0@my_user/my_channel#6708c9d84f98d56a6d9f2e6c2d5639ba", + "options": "fPIC=True\nheader_only=False\nshared=False\nwith_fmt_alias=False\nwith_os_api=True", + "package_id": "2c09c8f84c016041549fcee94e4caae5d89424b6", + "prev": "9f5ab13fc7c73e4a9f87e4e213f2cfa4", "context": "host" } }, - "revisions_enabled": false + "revisions_enabled": true }, "version": "0.4", - "profile_host": "[settings]\narch=x86_64\narch_build=x86_64\nbuild_type=Release\ncompiler=gcc\ncompiler.libcxx=libstdc++\ncompiler.version=9\nos=Linux\nos_build=Linux\n[options]\n[build_requires]\n[env]\n" -} + "profile_host": "[settings]\narch=x86_64\narch.march=None\narch_build=x86_64\nbuild_type=Release\ncompiler=gcc\ncompiler.libcxx=libstdc++11\ncompiler.version=11\nos=Linux\nos_build=Linux\n[options]\n[build_requires]\n[env]\n" +} \ No newline at end of file diff --git a/syft/pkg/cataloger/cpp/test-fixtures/conanfile.txt b/syft/pkg/cataloger/cpp/test-fixtures/conanfile.txt index c265b1328..21db293a9 100644 --- a/syft/pkg/cataloger/cpp/test-fixtures/conanfile.txt +++ b/syft/pkg/cataloger/cpp/test-fixtures/conanfile.txt @@ -4,9 +4,9 @@ catch2/2.13.8 docopt.cpp/0.6.3 fmt/8.1.1 -spdlog/1.9.2 -sdl/2.0.20 -fltk/1.3.8 +spdlog/1.9.2@my_user/my_channel#1234567%%987654 +sdl/2.0.20#1234567%%987654 +fltk/1.3.8@my_user/my_channel [generators] cmake_find_package_multi From 62f689824c8d8f820d0eee1dbf753fa430704b8b Mon Sep 17 00:00:00 2001 From: Sirish Bathina Date: Thu, 24 Aug 2023 03:49:59 -1000 Subject: [PATCH 017/106] Detect golang boring crypto and fipsonly modules (#2021) * Extending build info to include crypto settings Signed-off-by: Sirish Bathina * Use kasten fork for goversion module Signed-off-by: Sirish Bathina * go mod tidy Signed-off-by: Sirish Bathina * change key to GoCryptoSettings and lint fix Signed-off-by: Sirish Bathina * Addressing feedback Signed-off-by: Sirish Bathina --------- Signed-off-by: Sirish Bathina --- go.mod | 1 + go.sum | 2 + internal/constants.go | 2 +- schema/json/schema-10.0.1.json | 1929 +++++++++++++++++ syft/pkg/cataloger/golang/package.go | 3 +- syft/pkg/cataloger/golang/parse_go_binary.go | 8 +- .../cataloger/golang/parse_go_binary_test.go | 303 +-- syft/pkg/cataloger/golang/scan_binary.go | 43 +- syft/pkg/cataloger/golang/scan_binary_test.go | 70 + syft/pkg/golang_metadata.go | 1 + 10 files changed, 2215 insertions(+), 147 deletions(-) create mode 100644 schema/json/schema-10.0.1.json diff --git a/go.mod b/go.mod index 4fdfd2d76..ab4219ff0 100644 --- a/go.mod +++ b/go.mod @@ -69,6 +69,7 @@ require ( github.com/google/go-containerregistry v0.16.1 github.com/google/licensecheck v0.3.1 github.com/invopop/jsonschema v0.7.0 + github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b github.com/opencontainers/go-digest v1.0.0 github.com/saferwall/pe v1.4.4 diff --git a/go.sum b/go.sum index e483614ab..ae40a3b0f 100644 --- a/go.sum +++ b/go.sum @@ -428,6 +428,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 h1:WdAeg/imY2JFPc/9CST4bZ80nNJbiBFCAdSZCSgrS5Y= +github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953/go.mod h1:6o+UrvuZWc4UTyBhQf0LGjW9Ld7qJxLz/OqvSOWWlEc= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= diff --git a/internal/constants.go b/internal/constants.go index 354a54274..259b2d77c 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -6,5 +6,5 @@ const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "10.0.0" + JSONSchemaVersion = "10.0.1" ) diff --git a/schema/json/schema-10.0.1.json b/schema/json/schema-10.0.1.json new file mode 100644 index 000000000..730db95eb --- /dev/null +++ b/schema/json/schema-10.0.1.json @@ -0,0 +1,1929 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/10.0.1/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "type": "string" + }, + "build_requires": { + "type": "string" + }, + "py_requires": { + "type": "string" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableMetadata": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GemMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelMetadata": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModuleMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NixStoreMetadata": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelModuleMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NixStoreMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/PythonRequirementsMetadata" + }, + { + "$ref": "#/$defs/RDescriptionFileMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + }, + { + "$ref": "#/$defs/SwiftPackageManagerMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonRequirementsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "RDescriptionFileMetadata": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerMetadata": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/syft/pkg/cataloger/golang/package.go b/syft/pkg/cataloger/golang/package.go index 30ba083b4..37942563b 100644 --- a/syft/pkg/cataloger/golang/package.go +++ b/syft/pkg/cataloger/golang/package.go @@ -11,7 +11,7 @@ import ( "github.com/anchore/syft/syft/pkg" ) -func (c *goBinaryCataloger) newGoBinaryPackage(resolver file.Resolver, dep *debug.Module, mainModule, goVersion, architecture string, buildSettings map[string]string, locations ...file.Location) pkg.Package { +func (c *goBinaryCataloger) newGoBinaryPackage(resolver file.Resolver, dep *debug.Module, mainModule, goVersion, architecture string, buildSettings map[string]string, cryptoSettings []string, locations ...file.Location) pkg.Package { if dep.Replace != nil { dep = dep.Replace } @@ -36,6 +36,7 @@ func (c *goBinaryCataloger) newGoBinaryPackage(resolver file.Resolver, dep *debu Architecture: architecture, BuildSettings: buildSettings, MainModule: mainModule, + GoCryptoSettings: cryptoSettings, }, } diff --git a/syft/pkg/cataloger/golang/parse_go_binary.go b/syft/pkg/cataloger/golang/parse_go_binary.go index 89ed4ba07..6da185839 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary.go +++ b/syft/pkg/cataloger/golang/parse_go_binary.go @@ -66,7 +66,7 @@ func (c *goBinaryCataloger) parseGoBinary(resolver file.Resolver, _ *generic.Env return pkgs, nil, nil } -func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *debug.BuildInfo, arch string, location file.Location) pkg.Package { +func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *extendedBuildInfo, arch string, location file.Location) pkg.Package { gbs := getBuildSettings(mod.Settings) main := c.newGoBinaryPackage( resolver, @@ -75,6 +75,7 @@ func (c *goBinaryCataloger) makeGoMainPackage(resolver file.Resolver, mod *debug mod.GoVersion, arch, gbs, + mod.cryptoSettings, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ) @@ -153,7 +154,7 @@ func extractVersionFromLDFlags(ldflags string) (majorVersion string, fullVersion // getArchs finds a binary architecture by two ways: // 1) reading build info from binaries compiled by go1.18+ // 2) reading file headers from binaries compiled by < go1.18 -func getArchs(readers []io.ReaderAt, builds []*debug.BuildInfo) []string { +func getArchs(readers []io.ReaderAt, builds []*extendedBuildInfo) []string { if len(readers) != len(builds) { log.Trace("golang cataloger: bin parsing: number of builds and readers doesn't match") return nil @@ -255,7 +256,7 @@ func createMainModuleFromPath(path string) (mod debug.Module) { return } -func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file.Location, mod *debug.BuildInfo, arch string) []pkg.Package { +func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file.Location, mod *extendedBuildInfo, arch string) []pkg.Package { var pkgs []pkg.Package if mod == nil { return pkgs @@ -277,6 +278,7 @@ func (c *goBinaryCataloger) buildGoPkgInfo(resolver file.Resolver, location file mod.GoVersion, arch, nil, + mod.cryptoSettings, location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ) if pkg.IsValid(&p) { diff --git a/syft/pkg/cataloger/golang/parse_go_binary_test.go b/syft/pkg/cataloger/golang/parse_go_binary_test.go index d578b46ad..901444564 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary_test.go +++ b/syft/pkg/cataloger/golang/parse_go_binary_test.go @@ -155,7 +155,7 @@ func TestBuildGoPkgInfo(t *testing.T) { tests := []struct { name string - mod *debug.BuildInfo + mod *extendedBuildInfo arch string expected []pkg.Package }{ @@ -166,16 +166,18 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "package without name", - mod: &debug.BuildInfo{ - Deps: []*debug.Module{ - { - Path: "github.com/adrg/xdg", + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + Deps: []*debug.Module{ + { + Path: "github.com/adrg/xdg", + }, + { + Path: "", + Version: "v0.2.1", + }, }, - { - Path: "", - Version: "v0.2.1", - }, - }, + }, nil, }, expected: []pkg.Package{ { @@ -198,26 +200,28 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "buildGoPkgInfo parses a blank mod and returns no packages", - mod: &debug.BuildInfo{}, + mod: &extendedBuildInfo{&debug.BuildInfo{}, nil}, expected: []pkg.Package(nil), }, { name: "parse a mod without main module", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - }, - Deps: []*debug.Module{ - { - Path: "github.com/adrg/xdg", - Version: "v0.2.1", - Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, }, - }, + Deps: []*debug.Module{ + { + Path: "github.com/adrg/xdg", + Version: "v0.2.1", + Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", + }, + }, + }, nil, }, expected: []pkg.Package{ { @@ -246,14 +250,16 @@ func TestBuildGoPkgInfo(t *testing.T) { { name: "parse a mod with path but no main module", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - }, - Path: "github.com/a/b/c", + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + }, + Path: "github.com/a/b/c", + }, []string{"boringcrypto + fips"}, }, expected: []pkg.Package{ { @@ -280,7 +286,8 @@ func TestBuildGoPkgInfo(t *testing.T) { "GOARCH": "amd64", "GOOS": "darwin", }, - MainModule: "github.com/a/b/c", + MainModule: "github.com/a/b/c", + GoCryptoSettings: []string{"boringcrypto + fips"}, }, }, }, @@ -288,31 +295,35 @@ func TestBuildGoPkgInfo(t *testing.T) { { name: "parse a mod without packages", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - }, + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + }, + }, nil, }, expected: []pkg.Package{unmodifiedMain}, }, { name: "parse main mod and replace devel pseudo version and ldflags exists (but contains no version)", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, - {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, - {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`}, - }, + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, + {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`}, + }, + }, nil, }, expected: []pkg.Package{ { @@ -349,17 +360,19 @@ func TestBuildGoPkgInfo(t *testing.T) { { name: "parse main mod and replace devel version with one from ldflags with vcs. build settings", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, - {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, - {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, - }, + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, + {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, + }, + }, nil, }, expected: []pkg.Package{ { @@ -396,15 +409,17 @@ func TestBuildGoPkgInfo(t *testing.T) { { name: "parse main mod and replace devel version with one from ldflags without any vcs. build settings", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, - }, + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, + }, + }, nil, }, expected: []pkg.Package{ { @@ -439,15 +454,17 @@ func TestBuildGoPkgInfo(t *testing.T) { { name: "parse main mod and replace devel version with one from ldflags main.version without any vcs. build settings", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`}, - }, + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`}, + }, + }, nil, }, expected: []pkg.Package{ { @@ -482,15 +499,17 @@ func TestBuildGoPkgInfo(t *testing.T) { { name: "parse main mod and replace devel version with one from ldflags main.Version without any vcs. build settings", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`}, - }, + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`}, + }, + }, nil, }, expected: []pkg.Package{ { @@ -525,16 +544,18 @@ func TestBuildGoPkgInfo(t *testing.T) { { name: "parse main mod and replace devel version with a pseudo version", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, - {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, - }, + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, + {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, + {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, + }, + }, nil, }, expected: []pkg.Package{ { @@ -570,26 +591,28 @@ func TestBuildGoPkgInfo(t *testing.T) { { name: "parse a populated mod string and returns packages but no source info", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - }, - Deps: []*debug.Module{ - { - Path: "github.com/adrg/xdg", - Version: "v0.2.1", - Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, }, - { - Path: "github.com/anchore/client-go", - Version: "v0.0.0-20210222170800-9c70f9b80bcf", - Sum: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=", + Deps: []*debug.Module{ + { + Path: "github.com/adrg/xdg", + Version: "v0.2.1", + Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", + }, + { + Path: "github.com/anchore/client-go", + Version: "v0.0.0-20210222170800-9c70f9b80bcf", + Sum: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=", + }, }, - }, + }, nil, }, expected: []pkg.Package{ { @@ -642,31 +665,33 @@ func TestBuildGoPkgInfo(t *testing.T) { { name: "parse a populated mod string and returns packages when a replace directive exists", arch: archDetails, - mod: &debug.BuildInfo{ - GoVersion: goCompiledVersion, - Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, - Settings: []debug.BuildSetting{ - {Key: "GOARCH", Value: archDetails}, - {Key: "GOOS", Value: "darwin"}, - {Key: "GOAMD64", Value: "v1"}, - }, - Deps: []*debug.Module{ - { - Path: "golang.org/x/sys", - Version: "v0.0.0-20211006194710-c8a6f5223071", - Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=", + mod: &extendedBuildInfo{ + &debug.BuildInfo{ + GoVersion: goCompiledVersion, + Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, + Settings: []debug.BuildSetting{ + {Key: "GOARCH", Value: archDetails}, + {Key: "GOOS", Value: "darwin"}, + {Key: "GOAMD64", Value: "v1"}, }, - { - Path: "golang.org/x/term", - Version: "v0.0.0-20210927222741-03fcf44c2211", - Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=", - Replace: &debug.Module{ + Deps: []*debug.Module{ + { + Path: "golang.org/x/sys", + Version: "v0.0.0-20211006194710-c8a6f5223071", + Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=", + }, + { Path: "golang.org/x/term", - Version: "v0.0.0-20210916214954-140adaaadfaf", - Sum: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=", + Version: "v0.0.0-20210927222741-03fcf44c2211", + Sum: "h1:PjhxBct4MZii8FFR8+oeS7QOvxKOTZXgk63EU2XpfJE=", + Replace: &debug.Module{ + Path: "golang.org/x/term", + Version: "v0.0.0-20210916214954-140adaaadfaf", + Sum: "h1:Ihq/mm/suC88gF8WFcVwk+OV6Tq+wyA1O0E5UEvDglI=", + }, }, }, - }, + }, nil, }, expected: []pkg.Package{ { diff --git a/syft/pkg/cataloger/golang/scan_binary.go b/syft/pkg/cataloger/golang/scan_binary.go index c010b2a88..b19e24274 100644 --- a/syft/pkg/cataloger/golang/scan_binary.go +++ b/syft/pkg/cataloger/golang/scan_binary.go @@ -6,12 +6,19 @@ import ( "io" "runtime/debug" + "github.com/kastenhq/goversion/version" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader" ) +type extendedBuildInfo struct { + *debug.BuildInfo + cryptoSettings []string +} + // scanFile scans file to try to report the Go and module versions. -func scanFile(reader unionreader.UnionReader, filename string) ([]*debug.BuildInfo, []string) { +func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuildInfo, []string) { // NOTE: multiple readers are returned to cover universal binaries, which are files // with more than one binary readers, err := unionreader.GetReaders(reader) @@ -20,7 +27,7 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*debug.BuildIn return nil, nil } - var builds []*debug.BuildInfo + var builds []*extendedBuildInfo for _, r := range readers { bi, err := getBuildInfo(r) if err != nil { @@ -30,7 +37,14 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*debug.BuildIn if bi == nil { continue } - builds = append(builds, bi) + + v, err := getCryptoInformation(r) + if err != nil { + log.WithFields("file", filename, "error", err).Trace("unable to read golang version info") + continue + } + + builds = append(builds, &extendedBuildInfo{bi, v}) } archs := getArchs(readers, builds) @@ -38,6 +52,29 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*debug.BuildIn return builds, archs } +func getCryptoInformation(reader io.ReaderAt) ([]string, error) { + v, err := version.ReadExeFromReader(reader) + if err != nil { + return nil, err + } + + return getCryptoSettingsFromVersion(v), nil +} + +func getCryptoSettingsFromVersion(v version.Version) []string { + cryptoSettings := []string{} + if v.StandardCrypto { + cryptoSettings = append(cryptoSettings, "standard-crypto") + } + if v.BoringCrypto { + cryptoSettings = append(cryptoSettings, "boring-crypto") + } + if v.FIPSOnly { + cryptoSettings = append(cryptoSettings, "crypto/tls/fipsonly") + } + return cryptoSettings +} + func getBuildInfo(r io.ReaderAt) (bi *debug.BuildInfo, err error) { defer func() { if r := recover(); r != nil { diff --git a/syft/pkg/cataloger/golang/scan_binary_test.go b/syft/pkg/cataloger/golang/scan_binary_test.go index 8a5541137..a1c8dad7e 100644 --- a/syft/pkg/cataloger/golang/scan_binary_test.go +++ b/syft/pkg/cataloger/golang/scan_binary_test.go @@ -6,6 +6,7 @@ import ( "runtime/debug" "testing" + "github.com/kastenhq/goversion/version" "github.com/stretchr/testify/assert" ) @@ -38,3 +39,72 @@ func Test_getBuildInfo(t *testing.T) { }) } } + +func Test_getCryptoSettingsFromVersion(t *testing.T) { + for _, tt := range []struct { + name string + version version.Version + result []string + }{ + { + name: "standard crypto", + version: version.Version{ + StandardCrypto: true, + }, + result: []string{"standard-crypto"}, + }, + { + name: "boring crypto", + version: version.Version{ + BoringCrypto: true, + }, + result: []string{"boring-crypto"}, + }, + { // Should never see this. Boring crypto is required for fipsonly + name: "fipsonly", + version: version.Version{ + FIPSOnly: true, + }, + result: []string{"crypto/tls/fipsonly"}, + }, + { + name: "boring crypto and fipsonly", + version: version.Version{ + BoringCrypto: true, + FIPSOnly: true, + }, + result: []string{"boring-crypto", "crypto/tls/fipsonly"}, + }, + { // Should never see this. + name: "boring and standard crypto!", + version: version.Version{ + BoringCrypto: true, + StandardCrypto: true, + }, + result: []string{"boring-crypto", "standard-crypto"}, + }, + { // Should never see this. Boring crypto is required for fipsonly + name: "fipsonly and standard crypto!", + version: version.Version{ + FIPSOnly: true, + StandardCrypto: true, + }, + result: []string{"crypto/tls/fipsonly", "standard-crypto"}, + }, + + { // Should never see this. Boring crypto is required for fipsonly + name: "fipsonly boringcrypto and standard crypto!", + version: version.Version{ + FIPSOnly: true, + StandardCrypto: true, + BoringCrypto: true, + }, + result: []string{"crypto/tls/fipsonly", "standard-crypto", "boring-crypto"}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + res := getCryptoSettingsFromVersion(tt.version) + assert.ElementsMatch(t, res, tt.result) + }) + } +} diff --git a/syft/pkg/golang_metadata.go b/syft/pkg/golang_metadata.go index fd7309588..791b7fcc3 100644 --- a/syft/pkg/golang_metadata.go +++ b/syft/pkg/golang_metadata.go @@ -7,6 +7,7 @@ type GolangBinMetadata struct { Architecture string `json:"architecture" cyclonedx:"architecture"` H1Digest string `json:"h1Digest,omitempty" cyclonedx:"h1Digest"` MainModule string `json:"mainModule,omitempty" cyclonedx:"mainModule"` + GoCryptoSettings []string `json:"goCryptoSettings,omitempty" cyclonedx:"goCryptoSettings"` } // GolangModMetadata represents all captured data for a Golang source scan with go.mod/go.sum From 1848aa22cf3d1dae213ebe7bd515a16c2909bcbf Mon Sep 17 00:00:00 2001 From: witchcraze <67056980+witchcraze@users.noreply.github.com> Date: Thu, 24 Aug 2023 23:13:59 +0900 Subject: [PATCH 018/106] feat: add bash classifier (#2055) Signed-off-by: witchcraze --- syft/pkg/cataloger/binary/cataloger_test.go | 12 ++++++++++++ syft/pkg/cataloger/binary/default_classifiers.go | 14 ++++++++++++++ .../classifiers/positive/bash-5.2.15/bash | 1 + 3 files changed, 27 insertions(+) create mode 100644 syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/bash-5.2.15/bash diff --git a/syft/pkg/cataloger/binary/cataloger_test.go b/syft/pkg/cataloger/binary/cataloger_test.go index c66cb88db..188069e8b 100644 --- a/syft/pkg/cataloger/binary/cataloger_test.go +++ b/syft/pkg/cataloger/binary/cataloger_test.go @@ -667,6 +667,18 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) { Metadata: metadata("nginx-binary"), }, }, + { + name: "positive-bash-5.2.15", + fixtureDir: "test-fixtures/classifiers/positive/bash-5.2.15", + expected: pkg.Package{ + Name: "bash", + Version: "5.2.15", + Type: "binary", + PURL: "pkg:generic/bash@5.2.15", + Locations: locations("bash"), + Metadata: metadata("bash-binary"), + }, + }, } for _, test := range tests { diff --git a/syft/pkg/cataloger/binary/default_classifiers.go b/syft/pkg/cataloger/binary/default_classifiers.go index 93ed25d30..24cb43219 100644 --- a/syft/pkg/cataloger/binary/default_classifiers.go +++ b/syft/pkg/cataloger/binary/default_classifiers.go @@ -269,6 +269,20 @@ var defaultClassifiers = []classifier{ cpe.Must("cpe:2.3:a:nginx:nginx:*:*:*:*:*:*:*:*"), }, }, + { + Class: "bash-binary", + FileGlob: "**/bash", + EvidenceMatcher: fileContentsVersionMatcher( + // @(#)Bash version 5.2.15(1) release GNU + // @(#)Bash version 5.2.0(1) alpha GNU + // @(#)Bash version 5.2.0(1) beta GNU + // @(#)Bash version 5.2.0(1) rc4 GNU + `(?m)@\(#\)Bash version (?P[0-9]+\.[0-9]+\.[0-9]+)\([0-9]\) [a-z0-9]+ GNU`, + ), + Package: "bash", + PURL: mustPURL("pkg:generic/bash@version"), + CPEs: singleCPE("cpe:2.3:a:gnu:bash:*:*:*:*:*:*:*:*"), + }, } // in both binaries and shared libraries, the version pattern is [NUL]3.11.2[NUL] diff --git a/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/bash-5.2.15/bash b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/bash-5.2.15/bash new file mode 100644 index 000000000..17c8fc2a0 --- /dev/null +++ b/syft/pkg/cataloger/binary/test-fixtures/classifiers/positive/bash-5.2.15/bash @@ -0,0 +1 @@ +@(#)Bash version 5.2.15(1) release GNU From 5ceef489494ba79e4074670a6fa52034d41ee6f9 Mon Sep 17 00:00:00 2001 From: Chris Selzo Date: Thu, 24 Aug 2023 07:45:22 -0700 Subject: [PATCH 019/106] fix: do not double-prefix symlink paths that already contain volume names (#2051) Signed-off-by: Joseph Palermo Signed-off-by: Chris Selzo Co-authored-by: Joseph Palermo --- syft/internal/fileresolver/directory_indexer.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/syft/internal/fileresolver/directory_indexer.go b/syft/internal/fileresolver/directory_indexer.go index c9b5567a9..b6432f000 100644 --- a/syft/internal/fileresolver/directory_indexer.go +++ b/syft/internal/fileresolver/directory_indexer.go @@ -349,9 +349,13 @@ func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string, } if filepath.IsAbs(linkTarget) { + linkTarget = filepath.Clean(linkTarget) // if the link is absolute (e.g, /bin/ls -> /bin/busybox) we need to - // resolve relative to the root of the base directory - linkTarget = filepath.Join(r.base, filepath.Clean(linkTarget)) + // resolve relative to the root of the base directory, if it is not already + // prefixed with a volume name + if filepath.VolumeName(linkTarget) == "" { + linkTarget = filepath.Join(r.base, filepath.Clean(linkTarget)) + } } else { // if the link is not absolute (e.g, /dev/stderr -> fd/2 ) we need to // resolve it relative to the directory in question (e.g. resolve to From 9a2a988e7fab0629127e97b097e016f10351b806 Mon Sep 17 00:00:00 2001 From: William Murphy Date: Thu, 24 Aug 2023 11:28:44 -0400 Subject: [PATCH 020/106] chore: trace log pom property reflect usage (#2059) This reflect code occasionally throws an obscure panic, but not enough information is logged before the panic to know why it panicked. Log enough to tell what property and package are being analyzed when the panic occurs. Signed-off-by: Will Murphy --- syft/pkg/cataloger/java/parse_pom_xml.go | 21 +++++++++++-------- syft/pkg/cataloger/java/parse_pom_xml_test.go | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/syft/pkg/cataloger/java/parse_pom_xml.go b/syft/pkg/cataloger/java/parse_pom_xml.go index 532424fcc..f39bef1d6 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml.go +++ b/syft/pkg/cataloger/java/parse_pom_xml.go @@ -13,6 +13,7 @@ import ( "github.com/vifraa/gopom" "golang.org/x/net/html/charset" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -60,12 +61,13 @@ func newPomProject(path string, p gopom.Project) *pkg.PomProject { artifactID := safeString(p.ArtifactID) name := safeString(p.Name) projectURL := safeString(p.URL) + log.WithFields("path", path, "artifactID", artifactID, "name", name, "projectURL", projectURL).Trace("parsing pom.xml") return &pkg.PomProject{ Path: path, Parent: pomParent(p, p.Parent), - GroupID: resolveProperty(p, p.GroupID), + GroupID: resolveProperty(p, p.GroupID, "groupId"), ArtifactID: artifactID, - Version: resolveProperty(p, p.Version), + Version: resolveProperty(p, p.Version, "version"), Name: name, Description: cleanDescription(p.Description), URL: projectURL, @@ -75,14 +77,14 @@ func newPomProject(path string, p gopom.Project) *pkg.PomProject { func newPackageFromPom(pom gopom.Project, dep gopom.Dependency, locations ...file.Location) pkg.Package { m := pkg.JavaMetadata{ PomProperties: &pkg.PomProperties{ - GroupID: resolveProperty(pom, dep.GroupID), - ArtifactID: resolveProperty(pom, dep.ArtifactID), - Scope: resolveProperty(pom, dep.Scope), + GroupID: resolveProperty(pom, dep.GroupID, "groupId"), + ArtifactID: resolveProperty(pom, dep.ArtifactID, "artifactId"), + Scope: resolveProperty(pom, dep.Scope, "scope"), }, } name := safeString(dep.ArtifactID) - version := resolveProperty(pom, dep.Version) + version := resolveProperty(pom, dep.Version, "version") p := pkg.Package{ Name: name, @@ -151,9 +153,9 @@ func pomParent(pom gopom.Project, parent *gopom.Parent) (result *pkg.PomParent) artifactID := safeString(parent.ArtifactID) result = &pkg.PomParent{ - GroupID: resolveProperty(pom, parent.GroupID), + GroupID: resolveProperty(pom, parent.GroupID, "groupId"), ArtifactID: artifactID, - Version: resolveProperty(pom, parent.Version), + Version: resolveProperty(pom, parent.Version, "version"), } if result.GroupID == "" && result.ArtifactID == "" && result.Version == "" { @@ -182,8 +184,9 @@ func cleanDescription(original *string) (cleaned string) { // If no match is found, the entire expression including ${} is returned // //nolint:gocognit -func resolveProperty(pom gopom.Project, property *string) string { +func resolveProperty(pom gopom.Project, property *string, propertyName string) string { propertyCase := safeString(property) + log.WithFields("existingPropertyValue", propertyCase, "propertyName", propertyName).Trace("resolving property") return propertyMatcher.ReplaceAllStringFunc(propertyCase, func(match string) string { propertyName := strings.TrimSpace(match[2 : len(match)-1]) entries := pomProperties(pom) diff --git a/syft/pkg/cataloger/java/parse_pom_xml_test.go b/syft/pkg/cataloger/java/parse_pom_xml_test.go index 08a7571e9..985514521 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml_test.go +++ b/syft/pkg/cataloger/java/parse_pom_xml_test.go @@ -450,7 +450,7 @@ func Test_resolveProperty(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - resolved := resolveProperty(test.pom, stringPointer(test.property)) + resolved := resolveProperty(test.pom, stringPointer(test.property), test.name) assert.Equal(t, test.expected, resolved) }) } From faa902209e836c98de52eeb4ab53519f21a7235b Mon Sep 17 00:00:00 2001 From: William Murphy Date: Thu, 24 Aug 2023 13:28:40 -0400 Subject: [PATCH 021/106] Fix: don't validate pom declared group (#2054) Signed-off-by: Will Murphy --- syft/pkg/cataloger/java/package_url.go | 6 +- syft/pkg/cataloger/java/package_url_test.go | 86 +++++++++++++++++++++ 2 files changed, 89 insertions(+), 3 deletions(-) diff --git a/syft/pkg/cataloger/java/package_url.go b/syft/pkg/cataloger/java/package_url.go index d29f1ae3c..df1baf791 100644 --- a/syft/pkg/cataloger/java/package_url.go +++ b/syft/pkg/cataloger/java/package_url.go @@ -84,7 +84,7 @@ func groupIDFromPomProperties(properties *pkg.PomProperties) (groupID string) { return groupID } - if looksLikeGroupID(properties.GroupID) { + if properties.GroupID != "" { return cleanGroupID(properties.GroupID) } @@ -103,7 +103,7 @@ func groupIDFromPomProject(project *pkg.PomProject) (groupID string) { } // check the project details - if looksLikeGroupID(project.GroupID) { + if project.GroupID != "" { return cleanGroupID(project.GroupID) } @@ -116,7 +116,7 @@ func groupIDFromPomProject(project *pkg.PomProject) (groupID string) { // let's check the parent details // if the current project does not have a group ID, but the parent does, we'll use the parent's group ID if project.Parent != nil { - if looksLikeGroupID(project.Parent.GroupID) { + if project.Parent.GroupID != "" { return cleanGroupID(project.Parent.GroupID) } diff --git a/syft/pkg/cataloger/java/package_url_test.go b/syft/pkg/cataloger/java/package_url_test.go index 665827a2d..7ffb0a365 100644 --- a/syft/pkg/cataloger/java/package_url_test.go +++ b/syft/pkg/cataloger/java/package_url_test.go @@ -10,10 +10,12 @@ import ( func Test_packageURL(t *testing.T) { tests := []struct { + name string pkg pkg.Package expect string }{ { + name: "maven", pkg: pkg.Package{ Name: "example-java-app-maven", Version: "0.1.0", @@ -38,6 +40,90 @@ func Test_packageURL(t *testing.T) { }, expect: "pkg:maven/org.anchore/example-java-app-maven@0.1.0", }, + { + name: "POM properties have explicit group ID without . in it", + pkg: pkg.Package{ + Name: "example-java-app-maven", + Version: "0.1.0", + Language: pkg.Java, + Type: pkg.JavaPkg, + MetadataType: pkg.JavaMetadataType, + Metadata: pkg.JavaMetadata{ + VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar", + Manifest: &pkg.JavaManifest{ + Main: map[string]string{ + "Manifest-Version": "1.0", + }, + }, + PomProperties: &pkg.PomProperties{ + Path: "META-INF/maven/org.anchore/example-java-app-maven/pom.properties", + GroupID: "commons", + ArtifactID: "example-java-app-maven", + Version: "0.1.0", + Extra: make(map[string]string), + }, + }, + }, + expect: "pkg:maven/commons/example-java-app-maven@0.1.0", + }, + { + name: "POM project has explicit group ID without . in it", + pkg: pkg.Package{ + Name: "example-java-app-maven", + Version: "0.1.0", + Language: pkg.Java, + Type: pkg.JavaPkg, + MetadataType: pkg.JavaMetadataType, + Metadata: pkg.JavaMetadata{ + VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar", + Manifest: &pkg.JavaManifest{ + Main: map[string]string{ + "Manifest-Version": "1.0", + }, + }, + PomProperties: &pkg.PomProperties{ + Path: "META-INF/maven/org.anchore/example-java-app-maven/pom.properties", + ArtifactID: "example-java-app-maven", + Version: "0.1.0", + Extra: make(map[string]string), + }, + PomProject: &pkg.PomProject{ + GroupID: "commons", + }, + }, + }, + expect: "pkg:maven/commons/example-java-app-maven@0.1.0", + }, + { + name: "POM project has explicit group ID without . in it", + pkg: pkg.Package{ + Name: "example-java-app-maven", + Version: "0.1.0", + Language: pkg.Java, + Type: pkg.JavaPkg, + MetadataType: pkg.JavaMetadataType, + Metadata: pkg.JavaMetadata{ + VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar", + Manifest: &pkg.JavaManifest{ + Main: map[string]string{ + "Manifest-Version": "1.0", + }, + }, + PomProperties: &pkg.PomProperties{ + Path: "META-INF/maven/org.anchore/example-java-app-maven/pom.properties", + ArtifactID: "example-java-app-maven", + Version: "0.1.0", + Extra: make(map[string]string), + }, + PomProject: &pkg.PomProject{ + Parent: &pkg.PomParent{ + GroupID: "parent", + }, + }, + }, + }, + expect: "pkg:maven/parent/example-java-app-maven@0.1.0", + }, } for _, tt := range tests { t.Run(tt.expect, func(t *testing.T) { From d08e2be768855d5e9c6e19d7e4f5e56642e4d936 Mon Sep 17 00:00:00 2001 From: William Murphy Date: Fri, 25 Aug 2023 12:04:57 -0400 Subject: [PATCH 022/106] Fix panic in pom parsing (#2064) A recent update to gopom changed many fields to be omitted when empty, resulting in a number of nil pointers inside the nested structure of the pom that previously didn't exist. Defend against these in the search for the property value. Signed-off-by: Will Murphy --- syft/pkg/cataloger/java/parse_pom_xml.go | 14 ++++++++++++-- syft/pkg/cataloger/java/parse_pom_xml_test.go | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/syft/pkg/cataloger/java/parse_pom_xml.go b/syft/pkg/cataloger/java/parse_pom_xml.go index f39bef1d6..a6f5340cf 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml.go +++ b/syft/pkg/cataloger/java/parse_pom_xml.go @@ -188,7 +188,7 @@ func resolveProperty(pom gopom.Project, property *string, propertyName string) s propertyCase := safeString(property) log.WithFields("existingPropertyValue", propertyCase, "propertyName", propertyName).Trace("resolving property") return propertyMatcher.ReplaceAllStringFunc(propertyCase, func(match string) string { - propertyName := strings.TrimSpace(match[2 : len(match)-1]) + propertyName := strings.TrimSpace(match[2 : len(match)-1]) // remove leading ${ and trailing } entries := pomProperties(pom) if value, ok := entries[propertyName]; ok { return value @@ -210,16 +210,26 @@ func resolveProperty(pom gopom.Project, property *string, propertyName string) s for fieldNum := 0; fieldNum < pomValueType.NumField(); fieldNum++ { f := pomValueType.Field(fieldNum) tag := f.Tag.Get("xml") - tag = strings.TrimSuffix(tag, ",omitempty") + tag = strings.Split(tag, ",")[0] + // a segment of the property name matches the xml tag for the field, + // so we need to recurse down the nested structs or return a match + // if we're done. if part == tag { pomValue = pomValue.Field(fieldNum) pomValueType = pomValue.Type() if pomValueType.Kind() == reflect.Ptr { + // we were recursing down the nested structs, but one of the steps + // we need to take is a nil pointer, so give up and return the original match + if pomValue.IsNil() { + return match + } pomValue = pomValue.Elem() if !pomValue.IsZero() { + // we found a non-zero value whose tag matches this part of the property name pomValueType = pomValue.Type() } } + // If this was the last part of the property name, return the value if partNum == numParts-1 { return fmt.Sprintf("%v", pomValue.Interface()) } diff --git a/syft/pkg/cataloger/java/parse_pom_xml_test.go b/syft/pkg/cataloger/java/parse_pom_xml_test.go index 985514521..9e79fe96e 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml_test.go +++ b/syft/pkg/cataloger/java/parse_pom_xml_test.go @@ -446,6 +446,24 @@ func Test_resolveProperty(t *testing.T) { }, expected: "org.some.parent", }, + { + name: "nil pointer halts search", + property: "${project.parent.groupId}", + pom: gopom.Project{ + Parent: nil, + }, + expected: "${project.parent.groupId}", + }, + { + name: "nil string pointer halts search", + property: "${project.parent.groupId}", + pom: gopom.Project{ + Parent: &gopom.Parent{ + GroupID: nil, + }, + }, + expected: "${project.parent.groupId}", + }, } for _, test := range tests { From 4ae94c37ebb1ce32e776f338f407a4a791ad1dd7 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Fri, 25 Aug 2023 12:19:01 -0400 Subject: [PATCH 023/106] fix: duplicate entries in cyclonedx dependency list (#2063) Signed-off-by: Keith Zantow --- .../formats/common/cyclonedxhelpers/format.go | 34 ++++++++--- .../common/cyclonedxhelpers/format_test.go | 61 ++++++++++--------- 2 files changed, 59 insertions(+), 36 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/format.go b/syft/formats/common/cyclonedxhelpers/format.go index 9582ba85d..e36149e12 100644 --- a/syft/formats/common/cyclonedxhelpers/format.go +++ b/syft/formats/common/cyclonedxhelpers/format.go @@ -5,6 +5,7 @@ import ( "github.com/CycloneDX/cyclonedx-go" "github.com/google/uuid" + "golang.org/x/exp/slices" "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" @@ -139,7 +140,7 @@ func isExpressiblePackageRelationship(ty artifact.RelationshipType) bool { } func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependency { - result := make([]cyclonedx.Dependency, 0) + dependencies := map[string]*cyclonedx.Dependency{} for _, r := range relationships { exists := isExpressiblePackageRelationship(r.Type) if !exists { @@ -160,15 +161,32 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc continue } - // ind dep + toRef := deriveBomRef(toPkg) + dep := dependencies[toRef] + if dep == nil { + dep = &cyclonedx.Dependency{ + Ref: toRef, + Dependencies: &[]string{}, + } + dependencies[toRef] = dep + } - innerDeps := []string{} - innerDeps = append(innerDeps, deriveBomRef(fromPkg)) - result = append(result, cyclonedx.Dependency{ - Ref: deriveBomRef(toPkg), - Dependencies: &innerDeps, - }) + fromRef := deriveBomRef(fromPkg) + if !slices.Contains(*dep.Dependencies, fromRef) { + *dep.Dependencies = append(*dep.Dependencies, fromRef) + } } + + result := make([]cyclonedx.Dependency, 0, len(dependencies)) + for _, dep := range dependencies { + slices.Sort(*dep.Dependencies) + result = append(result, *dep) + } + + slices.SortFunc(result, func(a, b cyclonedx.Dependency) bool { + return a.Ref < b.Ref + }) + return result } diff --git a/syft/formats/common/cyclonedxhelpers/format_test.go b/syft/formats/common/cyclonedxhelpers/format_test.go index d16a390a6..c62afb8f4 100644 --- a/syft/formats/common/cyclonedxhelpers/format_test.go +++ b/syft/formats/common/cyclonedxhelpers/format_test.go @@ -1,6 +1,7 @@ package cyclonedxhelpers import ( + "fmt" "testing" "github.com/CycloneDX/cyclonedx-go" @@ -43,28 +44,34 @@ func Test_relationships(t *testing.T) { p1 := pkg.Package{ Name: "p1", } - p1.SetID() p2 := pkg.Package{ Name: "p2", } - p2.SetID() p3 := pkg.Package{ Name: "p3", } - p3.SetID() + + p4 := pkg.Package{ + Name: "p4", + } + + for _, p := range []*pkg.Package{&p1, &p2, &p3, &p4} { + p.PURL = fmt.Sprintf("pkg:generic/%s@%s", p.Name, p.Name) + p.SetID() + } tests := []struct { name string sbom sbom.SBOM - expected []string + expected *[]cyclonedx.Dependency }{ { name: "package dependencyOf relationships output as dependencies", sbom: sbom.SBOM{ Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(p1, p2, p3), + Packages: pkg.NewCollection(p1, p2, p3, p4), }, Relationships: []artifact.Relationship{ { @@ -77,9 +84,28 @@ func Test_relationships(t *testing.T) { To: p1, Type: artifact.DependencyOfRelationship, }, + { + From: p4, + To: p2, + Type: artifact.DependencyOfRelationship, + }, + }, + }, + expected: &[]cyclonedx.Dependency{ + { + Ref: deriveBomRef(p1), + Dependencies: &[]string{ + deriveBomRef(p2), + deriveBomRef(p3), + }, + }, + { + Ref: deriveBomRef(p2), + Dependencies: &[]string{ + deriveBomRef(p4), + }, }, }, - expected: []string{p2.Name, p3.Name}, }, { name: "package contains relationships not output", @@ -108,28 +134,7 @@ func Test_relationships(t *testing.T) { t.Run(test.name, func(t *testing.T) { cdx := ToFormatModel(test.sbom) got := cdx.Dependencies - - var deps []string - if got != nil { - for _, r := range *got { - for _, d := range *r.Dependencies { - c := findComponent(cdx, d) - require.NotNil(t, c) - deps = append(deps, c.Name) - } - - } - } - require.Equal(t, test.expected, deps) + require.Equal(t, test.expected, got) }) } } - -func findComponent(cdx *cyclonedx.BOM, bomRef string) *cyclonedx.Component { - for _, c := range *cdx.Components { - if c.BOMRef == bomRef { - return &c - } - } - return nil -} From dd09e0362e46bee7285e0fca5ed96e398c314380 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Fri, 25 Aug 2023 16:45:04 -0400 Subject: [PATCH 024/106] chore: update quill to the latest version (#2065) Signed-off-by: Keith Zantow --- .github/workflows/update-bootstrap-tools.yml | 4 ++++ Makefile | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/update-bootstrap-tools.yml b/.github/workflows/update-bootstrap-tools.yml index 3d5624275..8a21df672 100644 --- a/.github/workflows/update-bootstrap-tools.yml +++ b/.github/workflows/update-bootstrap-tools.yml @@ -29,6 +29,7 @@ jobs: GOSIMPORTS_LATEST_VERSION=$(go list -m -json github.com/rinchsan/gosimports@latest 2>/dev/null | jq -r '.Version') YAJSV_LATEST_VERSION=$(go list -m -json github.com/neilpa/yajsv@latest 2>/dev/null | jq -r '.Version') COSIGN_LATEST_VERSION=$(go list -m -json github.com/sigstore/cosign/v2@latest 2>/dev/null | jq -r '.Version') + QUILL_LATEST_VERSION=$(go list -m -json github.com/anchore/quill@latest 2>/dev/null | jq -r '.Version') GLOW_LATEST_VERSION=$(go list -m -json github.com/charmbracelet/glow@latest 2>/dev/null | jq -r '.Version') # update version variables in the Makefile @@ -39,6 +40,7 @@ jobs: sed -r -i -e 's/^(GOSIMPORTS_VERSION := ).*/\1'${GOSIMPORTS_LATEST_VERSION}'/' Makefile sed -r -i -e 's/^(YAJSV_VERSION := ).*/\1'${YAJSV_LATEST_VERSION}'/' Makefile sed -r -i -e 's/^(COSIGN_VERSION := ).*/\1'${COSIGN_LATEST_VERSION}'/' Makefile + sed -r -i -e 's/^(QUILL_VERSION := ).*/\1'${QUILL_LATEST_VERSION}'/' Makefile sed -r -i -e 's/^(GLOW_VERSION := ).*/\1'${GLOW_LATEST_VERSION}'/' Makefile # export the versions for use with create-pull-request @@ -49,6 +51,7 @@ jobs: echo "GOSIMPORTS=$GOSIMPORTS_LATEST_VERSION" >> $GITHUB_OUTPUT echo "YAJSV=$YAJSV_LATEST_VERSION" >> $GITHUB_OUTPUT echo "COSIGN=$COSIGN_LATEST_VERSION" >> $GITHUB_OUTPUT + echo "QUILL=$QUILL_LATEST_VERSION" >> $GITHUB_OUTPUT echo "GLOW=GLOW_LATEST_VERSION" >> $GITHUB_OUTPUT id: latest-versions @@ -74,6 +77,7 @@ jobs: - [gosimports ${{ steps.latest-versions.outputs.GOSIMPORTS }}](https://github.com/rinchsan/gosimports/releases/tag/${{ steps.latest-versions.outputs.GOSIMPORTS }}) - [yajsv ${{ steps.latest-versions.outputs.YAJSV }}](https://github.com/neilpa/yajsv/releases/tag/${{ steps.latest-versions.outputs.YAJSV }}) - [cosign ${{ steps.latest-versions.outputs.COSIGN }}](https://github.com/sigstore/cosign/releases/tag/${{ steps.latest-versions.outputs.COSIGN }}) + - [quill ${{ steps.latest-versions.outputs.QUILL }}](https://github.com/anchore/quill/releases/tag/${{ steps.latest-versions.outputs.QUILL }}) - [glow ${{ steps.latest-versions.outputs.GLOW }}](https://github.com/charmbracelet/glow/releases/tag/${{ steps.latest-versions.outputs.GLOW }}) This is an auto-generated pull request to update all of the bootstrap tools to the latest versions. token: ${{ steps.generate-token.outputs.token }} diff --git a/Makefile b/Makefile index 7647cee79..852269117 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ CHRONICLE_VERSION := v0.7.0 GORELEASER_VERSION := v1.20.0 YAJSV_VERSION := v1.4.1 COSIGN_VERSION := v2.1.1 -QUILL_VERSION := v0.2.0 +QUILL_VERSION := v0.4.1 GLOW_VERSION := v1.5.1 # Formatting variables ################################# From cedfa05e93f9ad9649b514e5a8b9127fe1d44f85 Mon Sep 17 00:00:00 2001 From: witchcraze <67056980+witchcraze@users.noreply.github.com> Date: Mon, 28 Aug 2023 21:28:01 +0900 Subject: [PATCH 025/106] fix: CPE generation for django (#2068) Signed-off-by: witchcraze --- .../pkg/cataloger/common/cpe/candidate_by_package_type.go | 5 +++++ syft/pkg/cataloger/common/cpe/generate_test.go | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go index bc62d3909..bc1e5fc62 100644 --- a/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go +++ b/syft/pkg/cataloger/common/cpe/candidate_by_package_type.go @@ -183,6 +183,11 @@ var defaultCandidateAdditions = buildCandidateLookup( candidateKey{PkgName: "pip"}, candidateAddition{AdditionalVendors: []string{"pypa"}}, }, + { + pkg.PythonPkg, + candidateKey{PkgName: "Django"}, + candidateAddition{AdditionalVendors: []string{"djangoproject"}}, + }, // Alpine packages { pkg.ApkPkg, diff --git a/syft/pkg/cataloger/common/cpe/generate_test.go b/syft/pkg/cataloger/common/cpe/generate_test.go index 939c2d3eb..2e6b131d7 100644 --- a/syft/pkg/cataloger/common/cpe/generate_test.go +++ b/syft/pkg/cataloger/common/cpe/generate_test.go @@ -896,6 +896,14 @@ func TestCandidateVendor(t *testing.T) { }, expected: []string{"apache"}, }, + { + name: "Django", + p: pkg.Package{ + Name: "Django", + Type: pkg.PythonPkg, + }, + expected: []string{"djangoproject" /* <-- known good names | default guess --> */, "Django"}, + }, } for _, test := range tests { From b03e9c68688229ba86cf1795fda4e85408daeb93 Mon Sep 17 00:00:00 2001 From: 5p2O5pe25ouT <245786660@qq.com> Date: Tue, 29 Aug 2023 23:45:20 +0800 Subject: [PATCH 026/106] Add registry certificate verification support (#1734) * add registry certificate verification support * replace stereoscope version * modify go.mod * pull in stereoscope update Signed-off-by: Alex Goodman * rename registry cert options, add docs, and add test Signed-off-by: Alex Goodman * update to account for changes in anchore/stereoscope#195 Signed-off-by: Alex Goodman * fix cli tests Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman Co-authored-by: lishituo <24578666@qq.com> Co-authored-by: Alex Goodman --- .gitignore | 1 + README.md | 18 +++++++- go.mod | 76 +++++++++++++++----------------- go.sum | 4 +- internal/config/registry.go | 34 ++++++++++---- internal/config/registry_test.go | 57 +++++++++++++++++++----- test/cli/packages_cmd_test.go | 17 +++++-- 7 files changed, 140 insertions(+), 67 deletions(-) diff --git a/.gitignore b/.gitignore index d38c4eed3..4cfd0ada7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ go.work go.work.sum +/.syft.yaml /bin /.bin /build diff --git a/README.md b/README.md index f1e6970e0..95d724039 100644 --- a/README.md +++ b/README.md @@ -681,19 +681,35 @@ registry: # SYFT_REGISTRY_INSECURE_USE_HTTP env var insecure-use-http: false + # filepath to a CA certificate (or directory containing *.crt, *.cert, *.pem) used to generate the client certificate + # SYFT_REGISTRY_CA_CERT env var + ca-cert: "" + # credentials for specific registries auth: # the URL to the registry (e.g. "docker.io", "localhost:5000", etc.) # SYFT_REGISTRY_AUTH_AUTHORITY env var - authority: "" + # SYFT_REGISTRY_AUTH_USERNAME env var username: "" + # SYFT_REGISTRY_AUTH_PASSWORD env var password: "" + # note: token and username/password are mutually exclusive # SYFT_REGISTRY_AUTH_TOKEN env var token: "" - # - ... # note, more credentials can be provided via config file only + + # filepath to the client certificate used for TLS authentication to the registry + # SYFT_REGISTRY_AUTH_TLS_CERT env var + tls-cert: "" + + # filepath to the client key used for TLS authentication to the registry + # SYFT_REGISTRY_AUTH_TLS_KEY env var + tls-key: "" + + # - ... # note, more credentials can be provided via config file only (not env vars) # generate an attested SBOM attest: diff --git a/go.mod b/go.mod index ab4219ff0..100b1c7fd 100644 --- a/go.mod +++ b/go.mod @@ -3,22 +3,48 @@ module github.com/anchore/syft go 1.19 require ( + github.com/CycloneDX/cyclonedx-go v0.7.1 + github.com/Masterminds/semver v1.5.0 + github.com/Masterminds/sprig/v3 v3.2.3 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/acobaugh/osrelease v0.1.0 github.com/adrg/xdg v0.4.0 + github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 + github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0 + github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 + github.com/anchore/stereoscope v0.0.0-20230829142608-334c2222e137 + // 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/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 github.com/bmatcuk/doublestar/v4 v4.6.0 + github.com/charmbracelet/bubbletea v0.24.2 + github.com/charmbracelet/lipgloss v0.8.0 + github.com/dave/jennifer v1.7.0 + github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da + github.com/docker/distribution v2.8.2+incompatible + github.com/docker/docker v24.0.5+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 + github.com/github/go-spdx/v2 v2.1.2 + github.com/gkampitakis/go-snaps v0.4.7 + github.com/go-git/go-billy/v5 v5.4.1 + github.com/go-git/go-git/v5 v5.8.1 github.com/go-test/deep v1.1.0 github.com/google/go-cmp v0.5.9 + github.com/google/go-containerregistry v0.16.1 + github.com/google/licensecheck v0.3.1 github.com/google/uuid v1.3.1 github.com/gookit/color v1.5.4 github.com/hashicorp/go-multierror v1.1.1 + github.com/invopop/jsonschema v0.7.0 github.com/jinzhu/copier v0.4.0 + github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 + github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mholt/archiver/v3 v3.5.1 github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5 @@ -26,7 +52,12 @@ require ( github.com/mitchellh/hashstructure/v2 v2.0.2 github.com/mitchellh/mapstructure v1.5.0 github.com/olekukonko/tablewriter v0.0.5 + github.com/opencontainers/go-digest v1.0.0 github.com/pelletier/go-toml v1.9.5 + github.com/pkg/errors v0.9.1 // indirect + github.com/saferwall/pe v1.4.4 + github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d + github.com/sassoftware/go-rpmutils v0.2.0 // pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5 github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e github.com/sergi/go-diff v1.3.1 @@ -37,47 +68,18 @@ require ( github.com/spf13/pflag v1.0.5 github.com/spf13/viper v1.16.0 github.com/stretchr/testify v1.8.4 + github.com/vbatts/go-mtree v0.5.3 github.com/vifraa/gopom v1.0.0 github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 github.com/xeipuuv/gojsonschema v1.2.0 + github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 + golang.org/x/crypto v0.12.0 // indirect + golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b golang.org/x/mod v0.12.0 golang.org/x/net v0.14.0 golang.org/x/term v0.11.0 gopkg.in/yaml.v2 v2.4.0 -) - -require ( - github.com/CycloneDX/cyclonedx-go v0.7.1 - github.com/Masterminds/semver v1.5.0 - github.com/Masterminds/sprig/v3 v3.2.3 - github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 - github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0 - github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe - github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e - github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 - github.com/charmbracelet/bubbletea v0.24.2 - github.com/charmbracelet/lipgloss v0.8.0 - github.com/dave/jennifer v1.7.0 - github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da - github.com/docker/distribution v2.8.2+incompatible - github.com/docker/docker v24.0.5+incompatible - github.com/github/go-spdx/v2 v2.1.2 - github.com/gkampitakis/go-snaps v0.4.7 - github.com/go-git/go-billy/v5 v5.4.1 - github.com/go-git/go-git/v5 v5.8.1 - github.com/google/go-containerregistry v0.16.1 - github.com/google/licensecheck v0.3.1 - github.com/invopop/jsonschema v0.7.0 - github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 - github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b - github.com/opencontainers/go-digest v1.0.0 - github.com/saferwall/pe v1.4.4 - github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d - github.com/sassoftware/go-rpmutils v0.2.0 - github.com/vbatts/go-mtree v0.5.3 - github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 - golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.25.0 ) @@ -201,14 +203,6 @@ require ( modernc.org/token v1.0.1 // indirect ) -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/pkg/errors v0.9.1 // indirect - golang.org/x/crypto v0.12.0 // indirect -) - retract ( v0.53.2 v0.53.1 // Published accidentally with incorrect license in depdencies diff --git a/go.sum b/go.sum index ae40a3b0f..172e79e07 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,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.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e h1:S6IhYpsBCpvphlHA1tN0glSG/kjVvFzC6OJuU2qW5Pc= -github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e/go.mod h1:0LsgHgXO4QFnk2hsYwtqd3fR18PIZXlFLIl2qb9tu3g= +github.com/anchore/stereoscope v0.0.0-20230829142608-334c2222e137 h1:ZuiAV3lYKbGPkZR42UpoKecp/9aFU38CZSjaxjXCMYc= +github.com/anchore/stereoscope v0.0.0-20230829142608-334c2222e137/go.mod h1:jkylrnuv++srUa2rsqCnG0n1ShD+0k2Xa6YtoNoRjAY= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= diff --git a/internal/config/registry.go b/internal/config/registry.go index 7e7e7132b..6b0e5dc9c 100644 --- a/internal/config/registry.go +++ b/internal/config/registry.go @@ -16,30 +16,37 @@ type RegistryCredentials struct { Password string `yaml:"-" json:"-" mapstructure:"password"` // IMPORTANT: do not show the token in any YAML/JSON output (sensitive information) Token string `yaml:"-" json:"-" mapstructure:"token"` + + TLSCert string `yaml:"tls-cert,omitempty" json:"tls-cert,omitempty" mapstructure:"tls-cert"` + TLSKey string `yaml:"tls-key,omitempty" json:"tls-key,omitempty" mapstructure:"tls-key"` } type registry struct { InsecureSkipTLSVerify bool `yaml:"insecure-skip-tls-verify" json:"insecure-skip-tls-verify" mapstructure:"insecure-skip-tls-verify"` InsecureUseHTTP bool `yaml:"insecure-use-http" json:"insecure-use-http" mapstructure:"insecure-use-http"` Auth []RegistryCredentials `yaml:"auth" json:"auth" mapstructure:"auth"` + CACert string `yaml:"ca-cert" json:"ca-cert" mapstructure:"ca-cert"` } func (cfg registry) loadDefaultValues(v *viper.Viper) { v.SetDefault("registry.insecure-skip-tls-verify", false) v.SetDefault("registry.insecure-use-http", false) v.SetDefault("registry.auth", []RegistryCredentials{}) + v.SetDefault("registry.ca-cert", "") } //nolint:unparam func (cfg *registry) parseConfigValues() error { // there may be additional credentials provided by env var that should be appended to the set of credentials - authority, username, password, token := + authority, username, password, token, tlsCert, tlsKey := os.Getenv("SYFT_REGISTRY_AUTH_AUTHORITY"), os.Getenv("SYFT_REGISTRY_AUTH_USERNAME"), os.Getenv("SYFT_REGISTRY_AUTH_PASSWORD"), - os.Getenv("SYFT_REGISTRY_AUTH_TOKEN") + os.Getenv("SYFT_REGISTRY_AUTH_TOKEN"), + os.Getenv("SYFT_REGISTRY_AUTH_TLS_CERT"), + os.Getenv("SYFT_REGISTRY_AUTH_TLS_KEY") - if hasNonEmptyCredentials(username, password, token) { + if hasNonEmptyCredentials(username, password, token, tlsCert, tlsKey) { // note: we prepend the credentials such that the environment variables take precedence over on-disk configuration. cfg.Auth = append([]RegistryCredentials{ { @@ -47,29 +54,38 @@ func (cfg *registry) parseConfigValues() error { Username: username, Password: password, Token: token, + TLSCert: tlsCert, + TLSKey: tlsKey, }, }, cfg.Auth...) } return nil } -func hasNonEmptyCredentials(username, password, token string) bool { - return password != "" && username != "" || token != "" +func hasNonEmptyCredentials(username, password, token, tlsCert, tlsKey string) bool { + hasUserPass := username != "" && password != "" + hasToken := token != "" + hasTLSMaterial := tlsCert != "" && tlsKey != "" + return hasUserPass || hasToken || hasTLSMaterial } func (cfg *registry) ToOptions() *image.RegistryOptions { var auth = make([]image.RegistryCredentials, len(cfg.Auth)) for i, a := range cfg.Auth { auth[i] = image.RegistryCredentials{ - Authority: a.Authority, - Username: a.Username, - Password: a.Password, - Token: a.Token, + Authority: a.Authority, + Username: a.Username, + Password: a.Password, + Token: a.Token, + ClientCert: a.TLSCert, + ClientKey: a.TLSKey, } } + return &image.RegistryOptions{ InsecureSkipTLSVerify: cfg.InsecureSkipTLSVerify, InsecureUseHTTP: cfg.InsecureUseHTTP, Credentials: auth, + CAFileOrDir: cfg.CACert, } } diff --git a/internal/config/registry_test.go b/internal/config/registry_test.go index c98511c1e..034384e61 100644 --- a/internal/config/registry_test.go +++ b/internal/config/registry_test.go @@ -11,48 +11,60 @@ import ( func TestHasNonEmptyCredentials(t *testing.T) { tests := []struct { - username, password, token string - expected bool + username, password, token, cert, key string + expected bool }{ { - "", "", "", + "", "", "", "", "", false, }, { - "user", "", "", + "user", "", "", "", "", false, }, { - "", "pass", "", + "", "pass", "", "", "", false, }, { - "", "pass", "tok", + "", "pass", "tok", "", "", true, }, { - "user", "", "tok", + "user", "", "tok", "", "", true, }, { - "", "", "tok", + "", "", "tok", "", "", true, }, { - "user", "pass", "tok", + "user", "pass", "tok", "", "", true, }, { - "user", "pass", "", + "user", "pass", "", "", "", true, }, + { + "", "", "", "cert", "key", + true, + }, + { + "", "", "", "cert", "", + false, + }, + { + "", "", "", "", "key", + false, + }, } for _, test := range tests { t.Run(fmt.Sprintf("%+v", test), func(t *testing.T) { - assert.Equal(t, test.expected, hasNonEmptyCredentials(test.username, test.password, test.token)) + assert.Equal(t, test.expected, hasNonEmptyCredentials(test.username, test.password, test.token, test.cert, test.key)) }) } } @@ -102,6 +114,29 @@ func Test_registry_ToOptions(t *testing.T) { Credentials: []image.RegistryCredentials{}, }, }, + { + name: "provide all tls configuration", + input: registry{ + CACert: "ca.crt", + InsecureSkipTLSVerify: true, + Auth: []RegistryCredentials{ + { + TLSCert: "client.crt", + TLSKey: "client.key", + }, + }, + }, + expected: image.RegistryOptions{ + CAFileOrDir: "ca.crt", + InsecureSkipTLSVerify: true, + Credentials: []image.RegistryCredentials{ + { + ClientCert: "client.crt", + ClientKey: "client.key", + }, + }, + }, + }, } for _, test := range tests { diff --git a/test/cli/packages_cmd_test.go b/test/cli/packages_cmd_test.go index 41bc2bcc0..e9b87ae48 100644 --- a/test/cli/packages_cmd_test.go +++ b/test/cli/packages_cmd_test.go @@ -258,7 +258,7 @@ func TestPackagesCmdFlags(t *testing.T) { func TestRegistryAuth(t *testing.T) { host := "localhost:17" image := fmt.Sprintf("%s/something:latest", host) - args := []string{"packages", "-vv", fmt.Sprintf("registry:%s", image)} + args := []string{"packages", "-vvv", fmt.Sprintf("registry:%s", image)} tests := []struct { name string @@ -272,7 +272,7 @@ func TestRegistryAuth(t *testing.T) { assertions: []traitAssertion{ assertInOutput("source=OciRegistry"), assertInOutput(image), - assertInOutput("no registry credentials configured, using the default keychain"), + assertInOutput(fmt.Sprintf("no registry credentials configured for %q, using the default keychain", host)), }, }, { @@ -311,7 +311,7 @@ func TestRegistryAuth(t *testing.T) { assertions: []traitAssertion{ assertInOutput("source=OciRegistry"), assertInOutput(image), - assertInOutput(`no registry credentials configured, using the default keychain`), + assertInOutput(fmt.Sprintf(`no registry credentials configured for %q, using the default keychain`, host)), }, }, { @@ -324,6 +324,17 @@ func TestRegistryAuth(t *testing.T) { assertInOutput("insecure-use-http: true"), }, }, + { + name: "use tls configuration", + args: args, + env: map[string]string{ + "SYFT_REGISTRY_AUTH_TLS_CERT": "place.crt", + "SYFT_REGISTRY_AUTH_TLS_KEY": "place.key", + }, + assertions: []traitAssertion{ + assertInOutput("using custom TLS credentials from"), + }, + }, } for _, test := range tests { From 2b7a9d0be32a71508739afd8c4efc38d7e027420 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Tue, 29 Aug 2023 15:52:26 -0400 Subject: [PATCH 027/106] chore: update CLI to CLIO (#2001) Signed-off-by: Keith Zantow Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman --- .goreleaser.yaml | 8 +- cmd/syft/cli/attest.go | 66 --- cmd/syft/cli/attest/attest.go | 261 ---------- cmd/syft/cli/cli.go | 81 ++++ cmd/syft/cli/commands.go | 167 ------- cmd/syft/cli/commands/attest.go | 258 ++++++++++ cmd/syft/cli/commands/convert.go | 95 ++++ cmd/syft/cli/commands/packages.go | 253 ++++++++++ cmd/syft/cli/commands/poweruser.go | 154 ++++++ cmd/syft/cli/commands/root.go | 27 ++ cmd/syft/cli/commands/update.go | 120 +++++ .../syft/cli/commands}/update_test.go | 9 +- cmd/syft/cli/convert.go | 58 --- cmd/syft/cli/convert/convert.go | 85 ---- cmd/syft/cli/eventloop/event_loop.go | 98 ---- cmd/syft/cli/eventloop/event_loop_test.go | 459 ------------------ cmd/syft/cli/eventloop/signals.go | 20 - cmd/syft/cli/eventloop/tasks.go | 53 +- cmd/syft/cli/options/attest.go | 25 +- cmd/syft/cli/options/catalog.go | 168 +++++++ cmd/syft/cli/options/config.go | 6 + cmd/syft/cli/options/file_classification.go | 17 + cmd/syft/cli/options/file_contents.go | 21 + cmd/syft/cli/options/file_metadata.go | 19 + cmd/syft/cli/options/fulcio.go | 49 -- .../config => cmd/syft/cli/options}/golang.go | 12 +- cmd/syft/cli/options/linux_kernel.go | 11 + cmd/syft/cli/options/oidc.go | 49 -- cmd/syft/cli/options/options.go | 11 - cmd/syft/cli/options/output.go | 95 ++++ cmd/syft/cli/options/packages.go | 118 ----- cmd/syft/cli/options/pkg.go | 23 + .../config => cmd/syft/cli/options}/python.go | 10 +- .../syft/cli/options}/registry.go | 39 +- .../syft/cli/options}/registry_test.go | 2 +- cmd/syft/cli/options/rekor.go | 33 -- cmd/syft/cli/options/root.go | 37 -- cmd/syft/cli/options/scope.go | 27 ++ cmd/syft/cli/options/secret.go | 25 + .../syft/cli/options}/secrets.go | 24 +- .../config => cmd/syft/cli/options}/source.go | 12 +- cmd/syft/cli/options/update_check.go | 11 + cmd/syft/cli/options/verbose.go | 18 - cmd/syft/cli/options/version.go | 17 - cmd/syft/cli/options/writer.go | 8 +- cmd/syft/cli/options/writer_test.go | 2 +- cmd/syft/cli/packages.go | 86 ---- cmd/syft/cli/packages/packages.go | 192 -------- cmd/syft/cli/poweruser.go | 51 -- cmd/syft/cli/poweruser/poweruser.go | 144 ------ cmd/syft/cli/version.go | 72 --- cmd/syft/internal/constants.go | 5 + cmd/syft/internal/ui/no_ui.go | 2 - cmd/syft/internal/ui/post_ui_event_writer.go | 10 +- .../internal/ui/post_ui_event_writer_test.go | 15 +- cmd/syft/internal/ui/ui.go | 23 +- cmd/syft/main.go | 32 +- go.mod | 20 +- go.sum | 15 +- internal/bus/helpers.go | 13 +- internal/config/application.go | 337 ------------- internal/config/application_test.go | 129 ----- internal/config/attest.go | 14 - internal/config/cataloger_options.go | 29 -- internal/config/development.go | 13 - internal/config/file_classification.go | 20 - internal/config/file_contents.go | 25 - internal/config/file_metadata.go | 22 - internal/config/linux_kernel.go | 11 - internal/config/logging.go | 34 -- internal/config/pkg.go | 24 - internal/config/test-fixtures/.syft.yaml | 7 - .../config-dir-test/.syft/config.yaml | 5 - .../config-home-test/config-file/.syft.yaml | 5 - .../config-home-test/syft/config.yaml | 5 - .../test-fixtures/config-wd-file/.syft.yaml | 5 - internal/constants.go | 3 - internal/log/log.go | 25 +- internal/redact/redact.go | 36 ++ internal/version/build.go | 54 --- internal/version/update.go | 73 --- syft/event/event.go | 7 +- syft/event/parsers/parsers.go | 26 +- .../formats/common/cyclonedxhelpers/format.go | 3 +- .../common/spdxhelpers/document_namespace.go | 10 +- .../spdxhelpers/document_namespace_test.go | 8 +- .../common/spdxhelpers/to_format_model.go | 4 +- syft/formats/github/encoder.go | 3 +- syft/formats/github/encoder_test.go | 3 + syft/formats/internal/testutils/snapshot.go | 18 +- syft/formats/spdxtagvalue/encoder_test.go | 2 +- syft/pkg/cataloger/rpm/parse_rpm_db.go | 3 +- test/cli/packages_cmd_test.go | 6 +- test/cli/trait_assertions_test.go | 48 +- test/integration/convert_test.go | 14 +- 95 files changed, 1680 insertions(+), 3192 deletions(-) delete mode 100644 cmd/syft/cli/attest.go delete mode 100644 cmd/syft/cli/attest/attest.go create mode 100644 cmd/syft/cli/cli.go delete mode 100644 cmd/syft/cli/commands.go create mode 100644 cmd/syft/cli/commands/attest.go create mode 100644 cmd/syft/cli/commands/convert.go create mode 100644 cmd/syft/cli/commands/packages.go create mode 100644 cmd/syft/cli/commands/poweruser.go create mode 100644 cmd/syft/cli/commands/root.go create mode 100644 cmd/syft/cli/commands/update.go rename {internal/version => cmd/syft/cli/commands}/update_test.go (95%) delete mode 100644 cmd/syft/cli/convert.go delete mode 100644 cmd/syft/cli/convert/convert.go delete mode 100644 cmd/syft/cli/eventloop/event_loop.go delete mode 100644 cmd/syft/cli/eventloop/event_loop_test.go delete mode 100644 cmd/syft/cli/eventloop/signals.go create mode 100644 cmd/syft/cli/options/catalog.go create mode 100644 cmd/syft/cli/options/config.go create mode 100644 cmd/syft/cli/options/file_classification.go create mode 100644 cmd/syft/cli/options/file_contents.go create mode 100644 cmd/syft/cli/options/file_metadata.go delete mode 100644 cmd/syft/cli/options/fulcio.go rename {internal/config => cmd/syft/cli/options}/golang.go (64%) create mode 100644 cmd/syft/cli/options/linux_kernel.go delete mode 100644 cmd/syft/cli/options/oidc.go delete mode 100644 cmd/syft/cli/options/options.go create mode 100644 cmd/syft/cli/options/output.go delete mode 100644 cmd/syft/cli/options/packages.go create mode 100644 cmd/syft/cli/options/pkg.go rename {internal/config => cmd/syft/cli/options}/python.go (50%) rename {internal/config => cmd/syft/cli/options}/registry.go (71%) rename {internal/config => cmd/syft/cli/options}/registry_test.go (99%) delete mode 100644 cmd/syft/cli/options/rekor.go delete mode 100644 cmd/syft/cli/options/root.go create mode 100644 cmd/syft/cli/options/scope.go create mode 100644 cmd/syft/cli/options/secret.go rename {internal/config => cmd/syft/cli/options}/secrets.go (52%) rename {internal/config => cmd/syft/cli/options}/source.go (67%) create mode 100644 cmd/syft/cli/options/update_check.go delete mode 100644 cmd/syft/cli/options/verbose.go delete mode 100644 cmd/syft/cli/options/version.go delete mode 100644 cmd/syft/cli/packages.go delete mode 100644 cmd/syft/cli/packages/packages.go delete mode 100644 cmd/syft/cli/poweruser.go delete mode 100644 cmd/syft/cli/poweruser/poweruser.go delete mode 100644 cmd/syft/cli/version.go create mode 100644 cmd/syft/internal/constants.go delete mode 100644 internal/config/application.go delete mode 100644 internal/config/application_test.go delete mode 100644 internal/config/attest.go delete mode 100644 internal/config/cataloger_options.go delete mode 100644 internal/config/development.go delete mode 100644 internal/config/file_classification.go delete mode 100644 internal/config/file_contents.go delete mode 100644 internal/config/file_metadata.go delete mode 100644 internal/config/linux_kernel.go delete mode 100644 internal/config/logging.go delete mode 100644 internal/config/pkg.go delete mode 100644 internal/config/test-fixtures/.syft.yaml delete mode 100644 internal/config/test-fixtures/config-dir-test/.syft/config.yaml delete mode 100644 internal/config/test-fixtures/config-home-test/config-file/.syft.yaml delete mode 100644 internal/config/test-fixtures/config-home-test/syft/config.yaml delete mode 100644 internal/config/test-fixtures/config-wd-file/.syft.yaml create mode 100644 internal/redact/redact.go delete mode 100644 internal/version/build.go delete mode 100644 internal/version/update.go diff --git a/.goreleaser.yaml b/.goreleaser.yaml index d4e05af8e..f31f769df 100644 --- a/.goreleaser.yaml +++ b/.goreleaser.yaml @@ -24,10 +24,10 @@ builds: -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}} + -X main.version={{.Version}} + -X main.gitCommit={{.Commit}} + -X main.buildDate={{.Date}} + -X main.gitDescription={{.Summary}} - id: darwin-build dir: ./cmd/syft diff --git a/cmd/syft/cli/attest.go b/cmd/syft/cli/attest.go deleted file mode 100644 index 0234057fe..000000000 --- a/cmd/syft/cli/attest.go +++ /dev/null @@ -1,66 +0,0 @@ -package cli - -import ( - "fmt" - "log" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/anchore/syft/cmd/syft/cli/attest" - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/config" -) - -const ( - attestExample = ` {{.appName}} {{.command}} --output [FORMAT] alpine:latest defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry -` - attestSchemeHelp = "\n" + indent + schemeHelpHeader + "\n" + imageSchemeHelp - attestHelp = attestExample + attestSchemeHelp -) - -func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions, po *options.PackagesOptions, ao *options.AttestOptions) *cobra.Command { - cmd := &cobra.Command{ - Use: "attest --output [FORMAT] ", - Short: "Generate an SBOM as an attestation for the given [SOURCE] container image", - Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from a container image as the predicate of an in-toto attestation that will be uploaded to the image registry", - Example: internal.Tprintf(attestHelp, map[string]interface{}{ - "appName": internal.ApplicationName, - "command": "attest", - }), - Args: func(cmd *cobra.Command, args []string) error { - if err := app.LoadAllValues(v, ro.Config); err != nil { - return fmt.Errorf("unable to load configuration: %w", err) - } - - newLogWrapper(app) - logApplicationConfig(app) - return validateArgs(cmd, args) - }, - SilenceUsage: true, - SilenceErrors: true, - RunE: func(cmd *cobra.Command, args []string) error { - if app.CheckForAppUpdate { - checkForApplicationUpdate() - // TODO: this is broke, the bus isn't available yet - } - - return attest.Run(cmd.Context(), app, args) - }, - } - - // syft attest is an enhancement of the packages command, so it should have the same flags - err := po.AddFlags(cmd, v) - if err != nil { - log.Fatal(err) - } - - // syft attest has its own options not included as part of the packages command - err = ao.AddFlags(cmd, v) - if err != nil { - log.Fatal(err) - } - - return cmd -} diff --git a/cmd/syft/cli/attest/attest.go b/cmd/syft/cli/attest/attest.go deleted file mode 100644 index 758af6706..000000000 --- a/cmd/syft/cli/attest/attest.go +++ /dev/null @@ -1,261 +0,0 @@ -package attest - -import ( - "context" - "fmt" - "os" - "os/exec" - "strings" - - "github.com/wagoodman/go-partybus" - "github.com/wagoodman/go-progress" - "golang.org/x/exp/slices" - - "github.com/anchore/stereoscope" - "github.com/anchore/stereoscope/pkg/image" - "github.com/anchore/syft/cmd/syft/cli/eventloop" - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/cmd/syft/cli/packages" - "github.com/anchore/syft/cmd/syft/internal/ui" - "github.com/anchore/syft/internal/bus" - "github.com/anchore/syft/internal/config" - "github.com/anchore/syft/internal/file" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/syft" - "github.com/anchore/syft/syft/event" - "github.com/anchore/syft/syft/event/monitor" - "github.com/anchore/syft/syft/formats/syftjson" - "github.com/anchore/syft/syft/formats/table" - "github.com/anchore/syft/syft/sbom" - "github.com/anchore/syft/syft/source" -) - -func Run(_ context.Context, app *config.Application, args []string) error { - err := ValidateOutputOptions(app) - if err != nil { - return err - } - - // note: must be a container image - userInput := args[0] - - _, err = exec.LookPath("cosign") - if err != nil { - // when cosign is not installed the error will be rendered like so: - // 2023/06/30 08:31:52 error during command execution: 'syft attest' requires cosign to be installed: exec: "cosign": executable file not found in $PATH - return fmt.Errorf("'syft attest' requires cosign to be installed: %w", err) - } - - eventBus := partybus.NewBus() - stereoscope.SetBus(eventBus) - syft.SetBus(eventBus) - subscription := eventBus.Subscribe() - - return eventloop.EventLoop( - execWorker(app, userInput), - eventloop.SetupSignals(), - subscription, - stereoscope.Cleanup, - ui.Select(options.IsVerbose(app), app.Quiet)..., - ) -} - -func buildSBOM(app *config.Application, userInput string, errs chan error) (*sbom.SBOM, error) { - cfg := source.DetectConfig{ - DefaultImageSource: app.DefaultImagePullSource, - } - detection, err := source.Detect(userInput, cfg) - if err != nil { - return nil, fmt.Errorf("could not deteremine source: %w", err) - } - - if detection.IsContainerImage() { - return nil, fmt.Errorf("attestations are only supported for oci images at this time") - } - - var platform *image.Platform - - if app.Platform != "" { - platform, err = image.NewPlatform(app.Platform) - if err != nil { - return nil, fmt.Errorf("invalid platform: %w", err) - } - } - - hashers, err := file.Hashers(app.Source.File.Digests...) - if err != nil { - return nil, fmt.Errorf("invalid hash: %w", err) - } - - src, err := detection.NewSource( - source.DetectionSourceConfig{ - Alias: source.Alias{ - Name: app.Source.Name, - Version: app.Source.Version, - }, - RegistryOptions: app.Registry.ToOptions(), - Platform: platform, - Exclude: source.ExcludeConfig{ - Paths: app.Exclusions, - }, - DigestAlgorithms: hashers, - BasePath: app.BasePath, - }, - ) - - if src != nil { - defer src.Close() - } - if err != nil { - return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err) - } - - s, err := packages.GenerateSBOM(src, errs, app) - if err != nil { - return nil, err - } - - if s == nil { - return nil, fmt.Errorf("no SBOM produced for %q", userInput) - } - - return s, nil -} - -//nolint:funlen -func execWorker(app *config.Application, userInput string) <-chan error { - errs := make(chan error) - go func() { - defer close(errs) - defer bus.Exit() - - s, err := buildSBOM(app, userInput, errs) - if err != nil { - errs <- fmt.Errorf("unable to build SBOM: %w", err) - return - } - - // note: ValidateOutputOptions ensures that there is no more than one output type - o := app.Outputs[0] - - f, err := os.CreateTemp("", o) - if err != nil { - errs <- fmt.Errorf("unable to create temp file: %w", err) - return - } - defer os.Remove(f.Name()) - - writer, err := options.MakeSBOMWriter(app.Outputs, f.Name(), app.OutputTemplatePath) - if err != nil { - errs <- fmt.Errorf("unable to create SBOM writer: %w", err) - return - } - - if err := writer.Write(*s); err != nil { - errs <- fmt.Errorf("unable to write SBOM to temp file: %w", err) - return - } - - // TODO: what other validation here besides binary name? - cmd := "cosign" - if !commandExists(cmd) { - errs <- fmt.Errorf("unable to find cosign in PATH; make sure you have it installed") - return - } - - // Select Cosign predicate type based on defined output type - // As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go - var predicateType string - switch strings.ToLower(o) { - case "cyclonedx-json": - predicateType = "cyclonedx" - case "spdx-tag-value", "spdx-tv": - predicateType = "spdx" - case "spdx-json", "json": - predicateType = "spdxjson" - default: - predicateType = "custom" - } - - args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType} - if app.Attest.Key != "" { - args = append(args, "--key", app.Attest.Key) - } - - execCmd := exec.Command(cmd, args...) - execCmd.Env = os.Environ() - if app.Attest.Key != "" { - execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", app.Attest.Password)) - } else { - // no key provided, use cosign's keyless mode - execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1") - } - - log.WithFields("cmd", strings.Join(execCmd.Args, " ")).Trace("creating attestation") - - // bus adapter for ui to hook into stdout via an os pipe - r, w, err := os.Pipe() - if err != nil { - errs <- fmt.Errorf("unable to create os pipe: %w", err) - return - } - defer w.Close() - - mon := progress.NewManual(-1) - - bus.Publish( - partybus.Event{ - Type: event.AttestationStarted, - Source: monitor.GenericTask{ - Title: monitor.Title{ - Default: "Create attestation", - WhileRunning: "Creating attestation", - OnSuccess: "Created attestation", - }, - Context: "cosign", - }, - Value: &monitor.ShellProgress{ - Reader: r, - Progressable: mon, - }, - }, - ) - - execCmd.Stdout = w - execCmd.Stderr = w - - // attest the SBOM - err = execCmd.Run() - if err != nil { - mon.SetError(err) - errs <- fmt.Errorf("unable to attest SBOM: %w", err) - return - } - - mon.SetCompleted() - }() - return errs -} - -func ValidateOutputOptions(app *config.Application) error { - err := packages.ValidateOutputOptions(app) - if err != nil { - return err - } - - if len(app.Outputs) > 1 { - return fmt.Errorf("multiple SBOM format is not supported for attest at this time") - } - - // cannot use table as default output format when using template output - if slices.Contains(app.Outputs, table.ID.String()) { - app.Outputs = []string{syftjson.ID.String()} - } - - return nil -} - -func commandExists(cmd string) bool { - _, err := exec.LookPath(cmd) - return err == nil -} diff --git a/cmd/syft/cli/cli.go b/cmd/syft/cli/cli.go new file mode 100644 index 000000000..187cf09c4 --- /dev/null +++ b/cmd/syft/cli/cli.go @@ -0,0 +1,81 @@ +package cli + +import ( + "os" + + cranecmd "github.com/google/go-containerregistry/cmd/crane/cmd" + + "github.com/anchore/clio" + "github.com/anchore/stereoscope" + "github.com/anchore/syft/cmd/syft/cli/commands" + handler "github.com/anchore/syft/cmd/syft/cli/ui" + "github.com/anchore/syft/cmd/syft/internal/ui" + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/internal/redact" +) + +// New constructs the `syft packages` command, aliases the root command to `syft packages`, +// and constructs the `syft power-user` command. It is also responsible for +// organizing flag usage and injecting the application config for each command. +// It also constructs the syft attest command and the syft version command. +// `RunE` is the earliest that the complete application configuration can be loaded. +func New(id clio.Identification) clio.Application { + clioCfg := clio.NewSetupConfig(id). + WithGlobalConfigFlag(). // add persistent -c for reading an application config from + WithGlobalLoggingFlags(). // add persistent -v and -q flags tied to the logging config + WithConfigInRootHelp(). // --help on the root command renders the full application config in the help text + WithUIConstructor( + // select a UI based on the logging configuration and state of stdin (if stdin is a tty) + func(cfg clio.Config) ([]clio.UI, error) { + noUI := ui.None(cfg.Log.Quiet) + if !cfg.Log.AllowUI(os.Stdin) { + return []clio.UI{noUI}, nil + } + + h := handler.New(handler.DefaultHandlerConfig()) + + return []clio.UI{ + ui.New(h, false, cfg.Log.Quiet), + noUI, + }, nil + }, + ). + WithInitializers( + func(state *clio.State) error { + // clio is setting up and providing the bus, redact store, and logger to the application. Once loaded, + // we can hoist them into the internal packages for global use. + stereoscope.SetBus(state.Bus) + bus.Set(state.Bus) + redact.Set(state.RedactStore) + log.Set(state.Logger) + stereoscope.SetLogger(state.Logger) + + return nil + }, + ). + WithPostRuns(func(state *clio.State, err error) { + stereoscope.Cleanup() + }) + + app := clio.New(*clioCfg) + + // since root is aliased as the packages cmd we need to construct this command first + // we also need the command to have information about the `root` options because of this alias + packagesCmd := commands.Packages(app) + + // rootCmd is currently an alias for the packages command + rootCmd := commands.Root(app, packagesCmd) + + // add sub-commands + rootCmd.AddCommand( + packagesCmd, + commands.PowerUser(app), + commands.Attest(app), + commands.Convert(app), + clio.VersionCommand(id), + cranecmd.NewCmdAuthLogin(id.Name), // syft login uses the same command as crane + ) + + return app +} diff --git a/cmd/syft/cli/commands.go b/cmd/syft/cli/commands.go deleted file mode 100644 index aa64d5a62..000000000 --- a/cmd/syft/cli/commands.go +++ /dev/null @@ -1,167 +0,0 @@ -package cli - -import ( - "fmt" - "strings" - - cranecmd "github.com/google/go-containerregistry/cmd/crane/cmd" - "github.com/gookit/color" - logrusUpstream "github.com/sirupsen/logrus" - "github.com/spf13/cobra" - "github.com/spf13/viper" - "github.com/wagoodman/go-partybus" - - "github.com/anchore/go-logger/adapter/logrus" - "github.com/anchore/stereoscope" - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/bus" - "github.com/anchore/syft/internal/config" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/internal/version" - "github.com/anchore/syft/syft" - "github.com/anchore/syft/syft/event" -) - -const indent = " " - -// New constructs the `syft packages` command, aliases the root command to `syft packages`, -// and constructs the `syft power-user` command. It is also responsible for -// organizing flag usage and injecting the application config for each command. -// It also constructs the syft attest command and the syft version command. - -// Because of how the `cobra` library behaves, the application's configuration is initialized -// at this level. Values from the config should only be used after `app.LoadAllValues` has been called. -// Cobra does not have knowledge of the user provided flags until the `RunE` block of each command. -// `RunE` is the earliest that the complete application configuration can be loaded. -func New() (*cobra.Command, error) { - app := &config.Application{} - - // allow for nested options to be specified via environment variables - // e.g. pod.context = APPNAME_POD_CONTEXT - v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))) - - // since root is aliased as the packages cmd we need to construct this command first - // we also need the command to have information about the `root` options because of this alias - ro := &options.RootOptions{} - po := &options.PackagesOptions{} - ao := &options.AttestOptions{} - packagesCmd := Packages(v, app, ro, po) - - // root options are also passed to the attestCmd so that a user provided config location can be discovered - poweruserCmd := PowerUser(v, app, ro) - convertCmd := Convert(v, app, ro, po) - attestCmd := Attest(v, app, ro, po, ao) - - // rootCmd is currently an alias for the packages command - rootCmd := &cobra.Command{ - Use: fmt.Sprintf("%s [SOURCE]", internal.ApplicationName), - Short: packagesCmd.Short, - Long: packagesCmd.Long, - Args: packagesCmd.Args, - Example: packagesCmd.Example, - SilenceUsage: true, - SilenceErrors: true, - RunE: packagesCmd.RunE, - Version: version.FromBuild().Version, - } - rootCmd.SetVersionTemplate(fmt.Sprintf("%s {{.Version}}\n", internal.ApplicationName)) - - // start adding flags to all the commands - err := ro.AddFlags(rootCmd, v) - if err != nil { - return nil, err - } - // package flags need to be decorated onto the rootCmd so that rootCmd can function as a packages alias - err = po.AddFlags(rootCmd, v) - if err != nil { - return nil, err - } - - // poweruser also uses the packagesCmd flags since it is a specialized version of the command - err = po.AddFlags(poweruserCmd, v) - if err != nil { - return nil, err - } - - // commands to add to root - cmds := []*cobra.Command{ - packagesCmd, - poweruserCmd, - convertCmd, - attestCmd, - Version(v, app), - cranecmd.NewCmdAuthLogin("syft"), // syft login uses the same command as crane - } - - // Add sub-commands. - for _, cmd := range cmds { - rootCmd.AddCommand(cmd) - } - - return rootCmd, err -} - -func validateArgs(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - // in the case that no arguments are given we want to show the help text and return with a non-0 return code. - if err := cmd.Help(); err != nil { - return fmt.Errorf("unable to display help: %w", err) - } - return fmt.Errorf("an image/directory argument is required") - } - - return cobra.MaximumNArgs(1)(cmd, args) -} - -func checkForApplicationUpdate() { - log.Debugf("checking if a new version of %s is available", internal.ApplicationName) - isAvailable, newVersion, err := version.IsUpdateAvailable() - if err != nil { - // this should never stop the application - log.Errorf(err.Error()) - } - if isAvailable { - log.Infof("new version of %s is available: %s (current version is %s)", internal.ApplicationName, newVersion, version.FromBuild().Version) - - bus.Publish(partybus.Event{ - Type: event.CLIAppUpdateAvailable, - Value: newVersion, - }) - } else { - log.Debugf("no new %s update available", internal.ApplicationName) - } -} - -func logApplicationConfig(app *config.Application) { - versionInfo := version.FromBuild() - log.Infof("%s version: %+v", internal.ApplicationName, versionInfo.Version) - log.Debugf("application config:\n%+v", color.Magenta.Sprint(app.String())) -} - -func newLogWrapper(app *config.Application) { - cfg := logrus.Config{ - EnableConsole: (app.Log.FileLocation == "" || app.Verbosity > 0) && !app.Quiet, - FileLocation: app.Log.FileLocation, - Level: app.Log.Level, - } - - if app.Log.Structured { - cfg.Formatter = &logrusUpstream.JSONFormatter{ - TimestampFormat: "2006-01-02 15:04:05", - DisableTimestamp: false, - DisableHTMLEscape: false, - PrettyPrint: false, - } - } - - logWrapper, err := logrus.New(cfg) - if err != nil { - // this is kinda circular, but we can't return an error... ¯\_(ツ)_/¯ - // I'm going to leave this here in case we one day have a different default logger other than the "discard" logger - log.Error("unable to initialize logger: %+v", err) - return - } - syft.SetLogger(logWrapper) - stereoscope.SetLogger(logWrapper.Nested("from-lib", "stereoscope")) -} diff --git a/cmd/syft/cli/commands/attest.go b/cmd/syft/cli/commands/attest.go new file mode 100644 index 000000000..97ade2437 --- /dev/null +++ b/cmd/syft/cli/commands/attest.go @@ -0,0 +1,258 @@ +package commands + +import ( + "fmt" + "os" + "os/exec" + "strings" + + "github.com/spf13/cobra" + "github.com/wagoodman/go-partybus" + "github.com/wagoodman/go-progress" + + "github.com/anchore/clio" + "github.com/anchore/stereoscope/pkg/image" + "github.com/anchore/syft/cmd/syft/cli/options" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/file" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/event" + "github.com/anchore/syft/syft/event/monitor" + "github.com/anchore/syft/syft/formats" + "github.com/anchore/syft/syft/formats/github" + "github.com/anchore/syft/syft/formats/syftjson" + "github.com/anchore/syft/syft/formats/table" + "github.com/anchore/syft/syft/formats/template" + "github.com/anchore/syft/syft/formats/text" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +const ( + attestExample = ` {{.appName}} {{.command}} --output [FORMAT] alpine:latest defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry +` + attestSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp + attestHelp = attestExample + attestSchemeHelp +) + +type attestOptions struct { + options.Config `yaml:",inline" mapstructure:",squash"` + options.SingleOutput `yaml:",inline" mapstructure:",squash"` + options.UpdateCheck `yaml:",inline" mapstructure:",squash"` + options.Catalog `yaml:",inline" mapstructure:",squash"` + options.Attest `yaml:",inline" mapstructure:",squash"` +} + +func Attest(app clio.Application) *cobra.Command { + id := app.ID() + + var allowableOutputs []string + for _, f := range formats.AllIDs() { + switch f { + case table.ID, text.ID, github.ID, template.ID: + continue + } + allowableOutputs = append(allowableOutputs, f.String()) + } + + opts := &attestOptions{ + UpdateCheck: options.DefaultUpdateCheck(), + SingleOutput: options.SingleOutput{ + AllowableOptions: allowableOutputs, + Output: syftjson.ID.String(), + }, + Catalog: options.DefaultCatalog(), + } + + return app.SetupCommand(&cobra.Command{ + Use: "attest --output [FORMAT] ", + Short: "Generate an SBOM as an attestation for the given [SOURCE] container image", + Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from a container image as the predicate of an in-toto attestation that will be uploaded to the image registry", + Example: internal.Tprintf(attestHelp, map[string]interface{}{ + "appName": id.Name, + "command": "attest", + }), + Args: validatePackagesArgs, + PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), + RunE: func(cmd *cobra.Command, args []string) error { + return runAttest(id, opts, args[0]) + }, + }, opts) +} + +//nolint:funlen +func runAttest(id clio.Identification, opts *attestOptions, userInput string) error { + _, err := exec.LookPath("cosign") + if err != nil { + // when cosign is not installed the error will be rendered like so: + // 2023/06/30 08:31:52 error during command execution: 'syft attest' requires cosign to be installed: exec: "cosign": executable file not found in $PATH + return fmt.Errorf("'syft attest' requires cosign to be installed: %w", err) + } + + s, err := buildSBOM(id, &opts.Catalog, userInput) + if err != nil { + return fmt.Errorf("unable to build SBOM: %w", err) + } + + o := opts.Output + + f, err := os.CreateTemp("", o) + if err != nil { + return fmt.Errorf("unable to create temp file: %w", err) + } + defer os.Remove(f.Name()) + + writer, err := opts.SBOMWriter(f.Name()) + if err != nil { + return fmt.Errorf("unable to create SBOM writer: %w", err) + } + + if err := writer.Write(*s); err != nil { + return fmt.Errorf("unable to write SBOM to temp file: %w", err) + } + + // TODO: what other validation here besides binary name? + cmd := "cosign" + if !commandExists(cmd) { + return fmt.Errorf("unable to find cosign in PATH; make sure you have it installed") + } + + // Select Cosign predicate type based on defined output type + // As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go + var predicateType string + switch strings.ToLower(o) { + case "cyclonedx-json": + predicateType = "cyclonedx" + case "spdx-tag-value", "spdx-tv": + predicateType = "spdx" + case "spdx-json", "json": + predicateType = "spdxjson" + default: + predicateType = "custom" + } + + args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType} + if opts.Attest.Key != "" { + args = append(args, "--key", opts.Attest.Key.String()) + } + + execCmd := exec.Command(cmd, args...) + execCmd.Env = os.Environ() + if opts.Attest.Key != "" { + execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", opts.Attest.Password)) + } else { + // no key provided, use cosign's keyless mode + execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1") + } + + log.WithFields("cmd", strings.Join(execCmd.Args, " ")).Trace("creating attestation") + + // bus adapter for ui to hook into stdout via an os pipe + r, w, err := os.Pipe() + if err != nil { + return fmt.Errorf("unable to create os pipe: %w", err) + } + defer w.Close() + + mon := progress.NewManual(-1) + + bus.Publish( + partybus.Event{ + Type: event.AttestationStarted, + Source: monitor.GenericTask{ + Title: monitor.Title{ + Default: "Create attestation", + WhileRunning: "Creating attestation", + OnSuccess: "Created attestation", + }, + Context: "cosign", + }, + Value: &monitor.ShellProgress{ + Reader: r, + Progressable: mon, + }, + }, + ) + + execCmd.Stdout = w + execCmd.Stderr = w + + // attest the SBOM + err = execCmd.Run() + if err != nil { + mon.SetError(err) + return fmt.Errorf("unable to attest SBOM: %w", err) + } + + mon.SetCompleted() + + return nil +} + +func buildSBOM(id clio.Identification, opts *options.Catalog, userInput string) (*sbom.SBOM, error) { + cfg := source.DetectConfig{ + DefaultImageSource: opts.DefaultImagePullSource, + } + detection, err := source.Detect(userInput, cfg) + if err != nil { + return nil, fmt.Errorf("could not deteremine source: %w", err) + } + + if detection.IsContainerImage() { + return nil, fmt.Errorf("attestations are only supported for oci images at this time") + } + + var platform *image.Platform + + if opts.Platform != "" { + platform, err = image.NewPlatform(opts.Platform) + if err != nil { + return nil, fmt.Errorf("invalid platform: %w", err) + } + } + + hashers, err := file.Hashers(opts.Source.File.Digests...) + if err != nil { + return nil, fmt.Errorf("invalid hash: %w", err) + } + + src, err := detection.NewSource( + source.DetectionSourceConfig{ + Alias: source.Alias{ + Name: opts.Source.Name, + Version: opts.Source.Version, + }, + RegistryOptions: opts.Registry.ToOptions(), + Platform: platform, + Exclude: source.ExcludeConfig{ + Paths: opts.Exclusions, + }, + DigestAlgorithms: hashers, + BasePath: opts.BasePath, + }, + ) + + if src != nil { + defer src.Close() + } + if err != nil { + return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err) + } + + s, err := generateSBOM(id, src, opts) + if err != nil { + return nil, err + } + + if s == nil { + return nil, fmt.Errorf("no SBOM produced for %q", userInput) + } + + return s, nil +} + +func commandExists(cmd string) bool { + _, err := exec.LookPath(cmd) + return err == nil +} diff --git a/cmd/syft/cli/commands/convert.go b/cmd/syft/cli/commands/convert.go new file mode 100644 index 000000000..f42b7da62 --- /dev/null +++ b/cmd/syft/cli/commands/convert.go @@ -0,0 +1,95 @@ +package commands + +import ( + "fmt" + "io" + "os" + + "github.com/spf13/cobra" + + "github.com/anchore/clio" + "github.com/anchore/syft/cmd/syft/cli/options" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/formats" +) + +const ( + convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout + {{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output is written to the file "img.cdx.json"" + {{.appName}} {{.command}} - -o spdx-json convert an SBOM from STDIN to spdx-json +` +) + +type ConvertOptions struct { + options.Config `yaml:",inline" mapstructure:",squash"` + options.MultiOutput `yaml:",inline" mapstructure:",squash"` + options.UpdateCheck `yaml:",inline" mapstructure:",squash"` +} + +//nolint:dupl +func Convert(app clio.Application) *cobra.Command { + id := app.ID() + + opts := &ConvertOptions{ + UpdateCheck: options.DefaultUpdateCheck(), + } + + return app.SetupCommand(&cobra.Command{ + Use: "convert [SOURCE-SBOM] -o [FORMAT]", + Short: "Convert between SBOM formats", + Long: "[Experimental] Convert SBOM files to, and from, SPDX, CycloneDX and Syft's format. For more info about data loss between formats see https://github.com/anchore/syft#format-conversion-experimental", + Example: internal.Tprintf(convertExample, map[string]interface{}{ + "appName": id.Name, + "command": "convert", + }), + Args: validateConvertArgs, + PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), + RunE: func(cmd *cobra.Command, args []string) error { + return RunConvert(opts, args[0]) + }, + }, opts) +} + +func validateConvertArgs(cmd *cobra.Command, args []string) error { + return validateArgs(cmd, args, "an SBOM argument is required") +} + +func RunConvert(opts *ConvertOptions, userInput string) error { + log.Warn("convert is an experimental feature, run `syft convert -h` for help") + + writer, err := opts.SBOMWriter() + if err != nil { + return err + } + + var reader io.ReadCloser + + if userInput == "-" { + reader = os.Stdin + } else { + f, err := os.Open(userInput) + if err != nil { + return fmt.Errorf("failed to open SBOM file: %w", err) + } + defer func() { + _ = f.Close() + }() + reader = f + } + + s, _, err := formats.Decode(reader) + if err != nil { + return fmt.Errorf("failed to decode SBOM: %w", err) + } + + if s == nil { + return fmt.Errorf("no SBOM produced") + } + + if err := writer.Write(*s); err != nil { + return fmt.Errorf("failed to write SBOM: %w", err) + } + + return nil +} diff --git a/cmd/syft/cli/commands/packages.go b/cmd/syft/cli/commands/packages.go new file mode 100644 index 000000000..c3a67992f --- /dev/null +++ b/cmd/syft/cli/commands/packages.go @@ -0,0 +1,253 @@ +package commands + +import ( + "fmt" + + "github.com/hashicorp/go-multierror" + "github.com/spf13/cobra" + + "github.com/anchore/clio" + "github.com/anchore/stereoscope/pkg/image" + "github.com/anchore/syft/cmd/syft/cli/eventloop" + "github.com/anchore/syft/cmd/syft/cli/options" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/file" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/formats/template" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +const ( + 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 + {{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.3 Tag-Value formatted SBOM + {{.appName}} {{.command}} alpine:latest -o spdx@2.2 show a SPDX 2.2 Tag-Value formatted SBOM + {{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.3 JSON formatted SBOM + {{.appName}} {{.command}} alpine:latest -o spdx-json@2.2 show a SPDX 2.2 JSON formatted SBOM + {{.appName}} {{.command}} alpine:latest -vv show verbose debug information + {{.appName}} {{.command}} alpine:latest -o template -t my_format.tmpl show a SBOM formatted according to given template file + + Supports the following image sources: + {{.appName}} {{.command}} yourrepo/yourimage:tag defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry. + {{.appName}} {{.command}} path/to/a/file/or/dir a Docker tar, OCI tar, OCI directory, SIF container, or generic filesystem directory +` + + 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) + {{.appName}} {{.command}} oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise) + {{.appName}} {{.command}} singularity:path/to/yourimage.sif read directly from a Singularity Image Format (SIF) container on disk +` + nonImageSchemeHelp = ` {{.appName}} {{.command}} dir:path/to/yourproject read directly from a path on disk (any directory) + {{.appName}} {{.command}} file:path/to/yourproject/file read directly from a path on disk (any single file) +` + packagesSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp + nonImageSchemeHelp + + packagesHelp = packagesExample + packagesSchemeHelp +) + +type packagesOptions struct { + options.Config `yaml:",inline" mapstructure:",squash"` + options.MultiOutput `yaml:",inline" mapstructure:",squash"` + options.UpdateCheck `yaml:",inline" mapstructure:",squash"` + options.Catalog `yaml:",inline" mapstructure:",squash"` +} + +func defaultPackagesOptions() *packagesOptions { + return &packagesOptions{ + MultiOutput: options.DefaultOutput(), + UpdateCheck: options.DefaultUpdateCheck(), + Catalog: options.DefaultCatalog(), + } +} + +//nolint:dupl +func Packages(app clio.Application) *cobra.Command { + id := app.ID() + + opts := defaultPackagesOptions() + + return app.SetupCommand(&cobra.Command{ + Use: "packages [SOURCE]", + Short: "Generate a package SBOM", + Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from container images and filesystems", + Example: internal.Tprintf(packagesHelp, map[string]interface{}{ + "appName": id.Name, + "command": "packages", + }), + Args: validatePackagesArgs, + PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), + RunE: func(cmd *cobra.Command, args []string) error { + return runPackages(id, opts, args[0]) + }, + }, opts) +} + +func validatePackagesArgs(cmd *cobra.Command, args []string) error { + return validateArgs(cmd, args, "an image/directory argument is required") +} + +func validateArgs(cmd *cobra.Command, args []string, error string) error { + if len(args) == 0 { + // in the case that no arguments are given we want to show the help text and return with a non-0 return code. + if err := cmd.Help(); err != nil { + return fmt.Errorf("unable to display help: %w", err) + } + return fmt.Errorf(error) + } + + return cobra.MaximumNArgs(1)(cmd, args) +} + +// nolint:funlen +func runPackages(id clio.Identification, opts *packagesOptions, userInput string) error { + err := validatePackageOutputOptions(&opts.MultiOutput) + if err != nil { + return err + } + + writer, err := opts.SBOMWriter() + if err != nil { + return err + } + + detection, err := source.Detect( + userInput, + source.DetectConfig{ + DefaultImageSource: opts.DefaultImagePullSource, + }, + ) + if err != nil { + return fmt.Errorf("could not deteremine source: %w", err) + } + + var platform *image.Platform + + if opts.Platform != "" { + platform, err = image.NewPlatform(opts.Platform) + if err != nil { + return fmt.Errorf("invalid platform: %w", err) + } + } + + hashers, err := file.Hashers(opts.Source.File.Digests...) + if err != nil { + return fmt.Errorf("invalid hash: %w", err) + } + + src, err := detection.NewSource( + source.DetectionSourceConfig{ + Alias: source.Alias{ + Name: opts.Source.Name, + Version: opts.Source.Version, + }, + RegistryOptions: opts.Registry.ToOptions(), + Platform: platform, + Exclude: source.ExcludeConfig{ + Paths: opts.Exclusions, + }, + DigestAlgorithms: hashers, + BasePath: opts.BasePath, + }, + ) + + if err != nil { + return fmt.Errorf("failed to construct source from user input %q: %w", userInput, err) + } + + defer func() { + if src != nil { + if err := src.Close(); err != nil { + log.Tracef("unable to close source: %+v", err) + } + } + }() + + s, err := generateSBOM(id, src, &opts.Catalog) + if err != nil { + return err + } + + if s == nil { + return fmt.Errorf("no SBOM produced for %q", userInput) + } + + if err := writer.Write(*s); err != nil { + return fmt.Errorf("failed to write SBOM: %w", err) + } + + return nil +} + +func generateSBOM(id clio.Identification, src source.Source, opts *options.Catalog) (*sbom.SBOM, error) { + tasks, err := eventloop.Tasks(opts) + if err != nil { + return nil, err + } + + s := sbom.SBOM{ + Source: src.Describe(), + Descriptor: sbom.Descriptor{ + Name: id.Name, + Version: id.Version, + Configuration: opts, + }, + } + + err = buildRelationships(&s, src, tasks) + + return &s, err +} + +func buildRelationships(s *sbom.SBOM, src source.Source, tasks []eventloop.Task) error { + var errs error + + var relationships []<-chan artifact.Relationship + for _, task := range tasks { + c := make(chan artifact.Relationship) + relationships = append(relationships, c) + go func(task eventloop.Task) { + err := eventloop.RunTask(task, &s.Artifacts, src, c) + if err != nil { + errs = multierror.Append(errs, err) + } + }(task) + } + + s.Relationships = append(s.Relationships, mergeRelationships(relationships...)...) + + 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 validatePackageOutputOptions(cfg *options.MultiOutput) error { + var usesTemplateOutput bool + for _, o := range cfg.Outputs { + if o == template.ID.String() { + usesTemplateOutput = true + break + } + } + + if usesTemplateOutput && cfg.OutputTemplatePath == "" { + return fmt.Errorf(`must specify path to template file when using "template" output format`) + } + + return nil +} diff --git a/cmd/syft/cli/commands/poweruser.go b/cmd/syft/cli/commands/poweruser.go new file mode 100644 index 000000000..2c9ff185a --- /dev/null +++ b/cmd/syft/cli/commands/poweruser.go @@ -0,0 +1,154 @@ +package commands + +import ( + "fmt" + "os" + + "github.com/gookit/color" + "github.com/hashicorp/go-multierror" + "github.com/spf13/cobra" + + "github.com/anchore/clio" + "github.com/anchore/stereoscope/pkg/image" + "github.com/anchore/syft/cmd/syft/cli/eventloop" + "github.com/anchore/syft/cmd/syft/cli/options" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/formats/syftjson" + "github.com/anchore/syft/syft/sbom" + "github.com/anchore/syft/syft/source" +) + +const powerUserExample = ` {{.appName}} {{.command}} + DEPRECATED - THIS COMMAND WILL BE REMOVED in v1.0.0 + Only image sources are supported (e.g. docker: , podman: , docker-archive: , oci: , etc.), the directory source (dir:) is not supported, template outputs are not supported. + All behavior is controlled via application configuration and environment variables (see https://github.com/anchore/syft#configuration) +` + +type powerUserOptions struct { + options.Config `yaml:",inline" mapstructure:",squash"` + options.OutputFile `yaml:",inline" mapstructure:",squash"` + options.UpdateCheck `yaml:",inline" mapstructure:",squash"` + options.Catalog `yaml:",inline" mapstructure:",squash"` +} + +func PowerUser(app clio.Application) *cobra.Command { + id := app.ID() + + pkgs := options.DefaultCatalog() + pkgs.Secrets.Cataloger.Enabled = true + pkgs.FileMetadata.Cataloger.Enabled = true + pkgs.FileContents.Cataloger.Enabled = true + pkgs.FileClassification.Cataloger.Enabled = true + opts := &powerUserOptions{ + Catalog: pkgs, + } + + return app.SetupCommand(&cobra.Command{ + Use: "power-user [IMAGE]", + Short: "Run bulk operations on container images", + Example: internal.Tprintf(powerUserExample, map[string]interface{}{ + "appName": id.Name, + "command": "power-user", + }), + Args: validatePackagesArgs, + Hidden: true, + PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), + RunE: func(cmd *cobra.Command, args []string) error { + return runPowerUser(id, opts, args[0]) + }, + }, opts) +} + +//nolint:funlen +func runPowerUser(id clio.Identification, opts *powerUserOptions, userInput string) error { + writer, err := opts.SBOMWriter(syftjson.Format()) + if err != nil { + return err + } + defer func() { + // inform user at end of run that command will be removed + deprecated := color.Style{color.Red, color.OpBold}.Sprint("DEPRECATED: This command will be removed in v1.0.0") + fmt.Fprintln(os.Stderr, deprecated) + }() + + tasks, err := eventloop.Tasks(&opts.Catalog) + if err != nil { + return err + } + + detection, err := source.Detect( + userInput, + source.DetectConfig{ + DefaultImageSource: opts.DefaultImagePullSource, + }, + ) + if err != nil { + return fmt.Errorf("could not deteremine source: %w", err) + } + + var platform *image.Platform + + if opts.Platform != "" { + platform, err = image.NewPlatform(opts.Platform) + if err != nil { + return fmt.Errorf("invalid platform: %w", err) + } + } + + src, err := detection.NewSource( + source.DetectionSourceConfig{ + Alias: source.Alias{ + Name: opts.Source.Name, + Version: opts.Source.Version, + }, + RegistryOptions: opts.Registry.ToOptions(), + Platform: platform, + Exclude: source.ExcludeConfig{ + Paths: opts.Exclusions, + }, + DigestAlgorithms: nil, + BasePath: opts.BasePath, + }, + ) + + if src != nil { + defer src.Close() + } + if err != nil { + return fmt.Errorf("failed to construct source from user input %q: %w", userInput, err) + } + + s := sbom.SBOM{ + Source: src.Describe(), + Descriptor: sbom.Descriptor{ + Name: id.Name, + Version: id.Version, + Configuration: opts, + }, + } + + var errs error + var relationships []<-chan artifact.Relationship + for _, task := range tasks { + c := make(chan artifact.Relationship) + relationships = append(relationships, c) + + go func(task eventloop.Task) { + err := eventloop.RunTask(task, &s.Artifacts, src, c) + errs = multierror.Append(errs, err) + }(task) + } + + if errs != nil { + return errs + } + + s.Relationships = append(s.Relationships, mergeRelationships(relationships...)...) + + if err := writer.Write(s); err != nil { + return fmt.Errorf("failed to write sbom: %w", err) + } + + return nil +} diff --git a/cmd/syft/cli/commands/root.go b/cmd/syft/cli/commands/root.go new file mode 100644 index 000000000..fb3234b50 --- /dev/null +++ b/cmd/syft/cli/commands/root.go @@ -0,0 +1,27 @@ +package commands + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/anchore/clio" +) + +func Root(app clio.Application, packagesCmd *cobra.Command) *cobra.Command { + id := app.ID() + + opts := defaultPackagesOptions() + + return app.SetupRootCommand(&cobra.Command{ + Use: fmt.Sprintf("%s [SOURCE]", app.ID().Name), + Short: packagesCmd.Short, + Long: packagesCmd.Long, + Args: packagesCmd.Args, + Example: packagesCmd.Example, + PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck), + RunE: func(cmd *cobra.Command, args []string) error { + return runPackages(id, opts, args[0]) + }, + }, opts) +} diff --git a/cmd/syft/cli/commands/update.go b/cmd/syft/cli/commands/update.go new file mode 100644 index 000000000..3ef1812e1 --- /dev/null +++ b/cmd/syft/cli/commands/update.go @@ -0,0 +1,120 @@ +package commands + +import ( + "fmt" + "io" + "net/http" + "strings" + + "github.com/spf13/cobra" + "github.com/wagoodman/go-partybus" + + "github.com/anchore/clio" + hashiVersion "github.com/anchore/go-version" + "github.com/anchore/syft/cmd/syft/cli/options" + "github.com/anchore/syft/cmd/syft/internal" + "github.com/anchore/syft/internal/bus" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/event" + "github.com/anchore/syft/syft/event/parsers" +) + +var latestAppVersionURL = struct { + host string + path string +}{ + host: "https://toolbox-data.anchore.io", + path: "/syft/releases/latest/VERSION", +} + +func applicationUpdateCheck(id clio.Identification, check *options.UpdateCheck) func(cmd *cobra.Command, args []string) error { + return func(cmd *cobra.Command, args []string) error { + if check.CheckForAppUpdate { + checkForApplicationUpdate(id) + } + return nil + } +} + +func checkForApplicationUpdate(id clio.Identification) { + log.Debugf("checking if a new version of %s is available", id.Name) + isAvailable, newVersion, err := isUpdateAvailable(id.Version) + if err != nil { + // this should never stop the application + log.Errorf(err.Error()) + } + if isAvailable { + log.Infof("new version of %s is available: %s (current version is %s)", id.Name, newVersion, id.Version) + + bus.Publish(partybus.Event{ + Type: event.CLIAppUpdateAvailable, + Value: parsers.UpdateCheck{ + New: newVersion, + Current: id.Version, + }, + }) + } else { + log.Debugf("no new %s update available", id.Name) + } +} + +// isUpdateAvailable indicates if there is a newer application version available, and if so, what the new version is. +func isUpdateAvailable(version string) (bool, string, error) { + if !isProductionBuild(version) { + // don't allow for non-production builds to check for a version. + return false, "", nil + } + + currentVersion, err := hashiVersion.NewVersion(version) + if err != nil { + return false, "", fmt.Errorf("failed to parse current application version: %w", err) + } + + latestVersion, err := fetchLatestApplicationVersion() + if err != nil { + return false, "", err + } + + if latestVersion.GreaterThan(currentVersion) { + return true, latestVersion.String(), nil + } + + return false, "", nil +} + +func isProductionBuild(version string) bool { + if strings.Contains(version, "SNAPSHOT") || strings.Contains(version, internal.NotProvided) { + return false + } + return true +} + +func fetchLatestApplicationVersion() (*hashiVersion.Version, error) { + req, err := http.NewRequest(http.MethodGet, latestAppVersionURL.host+latestAppVersionURL.path, nil) + if err != nil { + return nil, fmt.Errorf("failed to create request for latest version: %w", err) + } + + client := http.Client{} + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("failed to fetch latest version: %w", err) + } + defer func() { _ = resp.Body.Close() }() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("HTTP %d on fetching latest version: %s", resp.StatusCode, resp.Status) + } + + versionBytes, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("failed to read latest version: %w", err) + } + + versionStr := strings.TrimSuffix(string(versionBytes), "\n") + if len(versionStr) > 50 { + return nil, fmt.Errorf("version too long: %q", versionStr[:50]) + } + + return hashiVersion.NewVersion(versionStr) +} diff --git a/internal/version/update_test.go b/cmd/syft/cli/commands/update_test.go similarity index 95% rename from internal/version/update_test.go rename to cmd/syft/cli/commands/update_test.go index c79750540..96cd7de34 100644 --- a/internal/version/update_test.go +++ b/cmd/syft/cli/commands/update_test.go @@ -1,4 +1,4 @@ -package version +package commands import ( "net/http" @@ -6,6 +6,7 @@ import ( "testing" hashiVersion "github.com/anchore/go-version" + "github.com/anchore/syft/cmd/syft/internal" ) func TestIsUpdateAvailable(t *testing.T) { @@ -74,7 +75,7 @@ func TestIsUpdateAvailable(t *testing.T) { }, { name: "NoBuildVersion", - buildVersion: valueNotProvided, + buildVersion: internal.NotProvided, latestVersion: "1.0.0", code: 200, isAvailable: false, @@ -105,7 +106,7 @@ func TestIsUpdateAvailable(t *testing.T) { t.Run(test.name, func(t *testing.T) { // setup mocks // local... - version = test.buildVersion + version := test.buildVersion // remote... handler := http.NewServeMux() handler.HandleFunc(latestAppVersionURL.path, func(w http.ResponseWriter, r *http.Request) { @@ -116,7 +117,7 @@ func TestIsUpdateAvailable(t *testing.T) { latestAppVersionURL.host = mockSrv.URL defer mockSrv.Close() - isAvailable, newVersion, err := IsUpdateAvailable() + isAvailable, newVersion, err := isUpdateAvailable(version) if err != nil && !test.err { t.Fatalf("got error but expected none: %+v", err) } else if err == nil && test.err { diff --git a/cmd/syft/cli/convert.go b/cmd/syft/cli/convert.go deleted file mode 100644 index 16c24cac5..000000000 --- a/cmd/syft/cli/convert.go +++ /dev/null @@ -1,58 +0,0 @@ -package cli - -import ( - "fmt" - "log" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/anchore/syft/cmd/syft/cli/convert" - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/config" -) - -const ( - convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout - {{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output is written to the file "img.cdx.json"" - {{.appName}} {{.command}} - -o spdx-json convert an SBOM from STDIN to spdx-json -` -) - -//nolint:dupl -func Convert(v *viper.Viper, app *config.Application, ro *options.RootOptions, po *options.PackagesOptions) *cobra.Command { - cmd := &cobra.Command{ - Use: "convert [SOURCE-SBOM] -o [FORMAT]", - Short: "Convert between SBOM formats", - Long: "[Experimental] Convert SBOM files to, and from, SPDX, CycloneDX and Syft's format. For more info about data loss between formats see https://github.com/anchore/syft#format-conversion-experimental", - Example: internal.Tprintf(convertExample, map[string]interface{}{ - "appName": internal.ApplicationName, - "command": "convert", - }), - Args: func(cmd *cobra.Command, args []string) error { - if err := app.LoadAllValues(v, ro.Config); err != nil { - return fmt.Errorf("invalid application config: %w", err) - } - newLogWrapper(app) - logApplicationConfig(app) - return validateArgs(cmd, args) - }, - SilenceUsage: true, - SilenceErrors: true, - RunE: func(cmd *cobra.Command, args []string) error { - if app.CheckForAppUpdate { - checkForApplicationUpdate() - // TODO: this is broke, the bus isn't available yet - } - return convert.Run(cmd.Context(), app, args) - }, - } - - err := po.AddFlags(cmd, v) - if err != nil { - log.Fatal(err) - } - - return cmd -} diff --git a/cmd/syft/cli/convert/convert.go b/cmd/syft/cli/convert/convert.go deleted file mode 100644 index 2f0dbcedb..000000000 --- a/cmd/syft/cli/convert/convert.go +++ /dev/null @@ -1,85 +0,0 @@ -package convert - -import ( - "context" - "fmt" - "io" - "os" - - "github.com/wagoodman/go-partybus" - - "github.com/anchore/stereoscope" - "github.com/anchore/syft/cmd/syft/cli/eventloop" - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/cmd/syft/internal/ui" - "github.com/anchore/syft/internal/bus" - "github.com/anchore/syft/internal/config" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/syft" - "github.com/anchore/syft/syft/formats" - "github.com/anchore/syft/syft/sbom" -) - -func Run(_ context.Context, app *config.Application, args []string) error { - log.Warn("convert is an experimental feature, run `syft convert -h` for help") - - writer, err := options.MakeSBOMWriter(app.Outputs, app.File, app.OutputTemplatePath) - if err != nil { - return err - } - - // could be an image or a directory, with or without a scheme - userInput := args[0] - - var reader io.ReadCloser - - if userInput == "-" { - reader = os.Stdin - } else { - f, err := os.Open(userInput) - if err != nil { - return fmt.Errorf("failed to open SBOM file: %w", err) - } - defer func() { - _ = f.Close() - }() - reader = f - } - - eventBus := partybus.NewBus() - stereoscope.SetBus(eventBus) - syft.SetBus(eventBus) - subscription := eventBus.Subscribe() - - return eventloop.EventLoop( - execWorker(reader, writer), - eventloop.SetupSignals(), - subscription, - stereoscope.Cleanup, - ui.Select(options.IsVerbose(app), app.Quiet)..., - ) -} - -func execWorker(reader io.Reader, writer sbom.Writer) <-chan error { - errs := make(chan error) - go func() { - defer close(errs) - defer bus.Exit() - - s, _, err := formats.Decode(reader) - if err != nil { - errs <- fmt.Errorf("failed to decode SBOM: %w", err) - return - } - - if s == nil { - errs <- fmt.Errorf("no SBOM produced") - return - } - - if err := writer.Write(*s); err != nil { - errs <- fmt.Errorf("failed to write SBOM: %w", err) - } - }() - return errs -} diff --git a/cmd/syft/cli/eventloop/event_loop.go b/cmd/syft/cli/eventloop/event_loop.go deleted file mode 100644 index e7d008e71..000000000 --- a/cmd/syft/cli/eventloop/event_loop.go +++ /dev/null @@ -1,98 +0,0 @@ -package eventloop - -import ( - "errors" - "fmt" - "os" - - "github.com/hashicorp/go-multierror" - "github.com/wagoodman/go-partybus" - - "github.com/anchore/clio" - "github.com/anchore/syft/internal/log" -) - -// EventLoop listens to worker errors (from execution path), worker events (from a partybus subscription), and -// signal interrupts. Is responsible for handling each event relative to a given UI an to coordinate eventing until -// an eventual graceful exit. -func EventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *partybus.Subscription, cleanupFn func(), uxs ...clio.UI) error { - defer cleanupFn() - events := subscription.Events() - var err error - var ux clio.UI - - if ux, err = setupUI(subscription, uxs...); err != nil { - return err - } - - var retErr error - var forceTeardown bool - - for { - if workerErrs == nil && events == nil { - break - } - select { - case err, isOpen := <-workerErrs: - if !isOpen { - workerErrs = nil - continue - } - if err != nil { - // capture the error from the worker and unsubscribe to complete a graceful shutdown - retErr = multierror.Append(retErr, err) - _ = subscription.Unsubscribe() - // the worker has exited, we may have been mid-handling events for the UI which should now be - // ignored, in which case forcing a teardown of the UI irregardless of the state is required. - forceTeardown = true - } - case e, isOpen := <-events: - if !isOpen { - events = nil - continue - } - - if err := ux.Handle(e); err != nil { - if errors.Is(err, partybus.ErrUnsubscribe) { - events = nil - } else { - retErr = multierror.Append(retErr, err) - // TODO: should we unsubscribe? should we try to halt execution? or continue? - } - } - case <-signals: - // ignore further results from any event source and exit ASAP, but ensure that all cache is cleaned up. - // we ignore further errors since cleaning up the tmp directories will affect running catalogers that are - // reading/writing from/to their nested temp dirs. This is acceptable since we are bailing without result. - - // TODO: potential future improvement would be to pass context into workers with a cancel function that is - // to the event loop. In this way we can have a more controlled shutdown even at the most nested levels - // of processing. - events = nil - workerErrs = nil - forceTeardown = true - } - } - - if err := ux.Teardown(forceTeardown); err != nil { - retErr = multierror.Append(retErr, err) - } - - return retErr -} - -// setupUI takes one or more UIs that responds to events and takes a event bus unsubscribe function for use -// during teardown. With the given UIs, the first UI which the ui.Setup() function does not return an error -// will be utilized in execution. Providing a set of UIs allows for the caller to provide graceful fallbacks -// when there are environmental problem (e.g. unable to setup a TUI with the current TTY). -func setupUI(subscription *partybus.Subscription, uis ...clio.UI) (clio.UI, error) { - for _, ux := range uis { - if err := ux.Setup(subscription); err != nil { - log.Warnf("unable to setup given UI, falling back to alternative UI: %+v", err) - continue - } - - return ux, nil - } - return nil, fmt.Errorf("unable to setup any UI") -} diff --git a/cmd/syft/cli/eventloop/event_loop_test.go b/cmd/syft/cli/eventloop/event_loop_test.go deleted file mode 100644 index 495af90b8..000000000 --- a/cmd/syft/cli/eventloop/event_loop_test.go +++ /dev/null @@ -1,459 +0,0 @@ -package eventloop - -import ( - "fmt" - "os" - "syscall" - "testing" - "time" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" - "github.com/wagoodman/go-partybus" - - "github.com/anchore/clio" - "github.com/anchore/syft/syft/event" -) - -var _ clio.UI = (*uiMock)(nil) - -type uiMock struct { - t *testing.T - finalEvent partybus.Event - subscription partybus.Unsubscribable - mock.Mock -} - -func (u *uiMock) Setup(unsubscribe partybus.Unsubscribable) error { - u.t.Helper() - u.t.Logf("UI Setup called") - u.subscription = unsubscribe - return u.Called(unsubscribe.Unsubscribe).Error(0) -} - -func (u *uiMock) Handle(event partybus.Event) error { - u.t.Helper() - u.t.Logf("UI Handle called: %+v", event.Type) - if event == u.finalEvent { - assert.NoError(u.t, u.subscription.Unsubscribe()) - } - return u.Called(event).Error(0) -} - -func (u *uiMock) Teardown(_ bool) error { - u.t.Helper() - u.t.Logf("UI Teardown called") - return u.Called().Error(0) -} - -func Test_EventLoop_gracefulExit(t *testing.T) { - test := func(t *testing.T) { - - testBus := partybus.NewBus() - subscription := testBus.Subscribe() - t.Cleanup(testBus.Close) - - finalEvent := partybus.Event{ - Type: event.CLIExit, - } - - worker := func() <-chan error { - ret := make(chan error) - go func() { - t.Log("worker running") - // send an empty item (which is ignored) ensuring we've entered the select statement, - // then close (a partial shutdown). - ret <- nil - t.Log("worker sent nothing") - close(ret) - t.Log("worker closed") - // do the other half of the shutdown - testBus.Publish(finalEvent) - t.Log("worker published final event") - }() - return ret - } - - signaler := func() <-chan os.Signal { - return nil - } - - ux := &uiMock{ - t: t, - finalEvent: finalEvent, - } - - // ensure the mock sees at least the final event - ux.On("Handle", finalEvent).Return(nil) - // ensure the mock sees basic setup/teardown events - ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil) - ux.On("Teardown").Return(nil) - - var cleanupCalled bool - cleanupFn := func() { - t.Log("cleanup called") - cleanupCalled = true - } - - assert.NoError(t, - EventLoop( - worker(), - signaler(), - subscription, - cleanupFn, - ux, - ), - ) - - assert.True(t, cleanupCalled, "cleanup function not called") - ux.AssertExpectations(t) - } - - // if there is a bug, then there is a risk of the event loop never returning - testWithTimeout(t, 5*time.Second, test) -} - -func Test_EventLoop_workerError(t *testing.T) { - test := func(t *testing.T) { - - testBus := partybus.NewBus() - subscription := testBus.Subscribe() - t.Cleanup(testBus.Close) - - workerErr := fmt.Errorf("worker error") - - worker := func() <-chan error { - ret := make(chan error) - go func() { - t.Log("worker running") - // send an empty item (which is ignored) ensuring we've entered the select statement, - // then close (a partial shutdown). - ret <- nil - t.Log("worker sent nothing") - ret <- workerErr - t.Log("worker sent error") - close(ret) - t.Log("worker closed") - // note: NO final event is fired - }() - return ret - } - - signaler := func() <-chan os.Signal { - return nil - } - - ux := &uiMock{ - t: t, - } - - // ensure the mock sees basic setup/teardown events - ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil) - ux.On("Teardown").Return(nil) - - var cleanupCalled bool - cleanupFn := func() { - t.Log("cleanup called") - cleanupCalled = true - } - - // ensure we see an error returned - assert.ErrorIs(t, - EventLoop( - worker(), - signaler(), - subscription, - cleanupFn, - ux, - ), - workerErr, - "should have seen a worker error, but did not", - ) - - assert.True(t, cleanupCalled, "cleanup function not called") - ux.AssertExpectations(t) - } - - // if there is a bug, then there is a risk of the event loop never returning - testWithTimeout(t, 5*time.Second, test) -} - -func Test_EventLoop_unsubscribeError(t *testing.T) { - test := func(t *testing.T) { - - testBus := partybus.NewBus() - subscription := testBus.Subscribe() - t.Cleanup(testBus.Close) - - finalEvent := partybus.Event{ - Type: event.CLIExit, - } - - worker := func() <-chan error { - ret := make(chan error) - go func() { - t.Log("worker running") - // send an empty item (which is ignored) ensuring we've entered the select statement, - // then close (a partial shutdown). - ret <- nil - t.Log("worker sent nothing") - close(ret) - t.Log("worker closed") - // do the other half of the shutdown - testBus.Publish(finalEvent) - t.Log("worker published final event") - }() - return ret - } - - signaler := func() <-chan os.Signal { - return nil - } - - ux := &uiMock{ - t: t, - finalEvent: finalEvent, - } - - // ensure the mock sees at least the final event... note the unsubscribe error here - ux.On("Handle", finalEvent).Return(partybus.ErrUnsubscribe) - // ensure the mock sees basic setup/teardown events - ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil) - ux.On("Teardown").Return(nil) - - var cleanupCalled bool - cleanupFn := func() { - t.Log("cleanup called") - cleanupCalled = true - } - - // unsubscribe errors should be handled and ignored, not propagated. We are additionally asserting that - // this case is handled as a controlled shutdown (this test should not timeout) - assert.NoError(t, - EventLoop( - worker(), - signaler(), - subscription, - cleanupFn, - ux, - ), - ) - - assert.True(t, cleanupCalled, "cleanup function not called") - ux.AssertExpectations(t) - } - - // if there is a bug, then there is a risk of the event loop never returning - testWithTimeout(t, 5*time.Second, test) -} - -func Test_EventLoop_handlerError(t *testing.T) { - test := func(t *testing.T) { - - testBus := partybus.NewBus() - subscription := testBus.Subscribe() - t.Cleanup(testBus.Close) - - finalEvent := partybus.Event{ - Type: event.CLIExit, - Error: fmt.Errorf("an exit error occured"), - } - - worker := func() <-chan error { - ret := make(chan error) - go func() { - t.Log("worker running") - // send an empty item (which is ignored) ensuring we've entered the select statement, - // then close (a partial shutdown). - ret <- nil - t.Log("worker sent nothing") - close(ret) - t.Log("worker closed") - // do the other half of the shutdown - testBus.Publish(finalEvent) - t.Log("worker published final event") - }() - return ret - } - - signaler := func() <-chan os.Signal { - return nil - } - - ux := &uiMock{ - t: t, - finalEvent: finalEvent, - } - - // ensure the mock sees at least the final event... note the event error is propagated - ux.On("Handle", finalEvent).Return(finalEvent.Error) - // ensure the mock sees basic setup/teardown events - ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil) - ux.On("Teardown").Return(nil) - - var cleanupCalled bool - cleanupFn := func() { - t.Log("cleanup called") - cleanupCalled = true - } - - // handle errors SHOULD propagate the event loop. We are additionally asserting that this case is - // handled as a controlled shutdown (this test should not timeout) - assert.ErrorIs(t, - EventLoop( - worker(), - signaler(), - subscription, - cleanupFn, - ux, - ), - finalEvent.Error, - "should have seen a event error, but did not", - ) - - assert.True(t, cleanupCalled, "cleanup function not called") - ux.AssertExpectations(t) - } - - // if there is a bug, then there is a risk of the event loop never returning - testWithTimeout(t, 5*time.Second, test) -} - -func Test_EventLoop_signalsStopExecution(t *testing.T) { - test := func(t *testing.T) { - - testBus := partybus.NewBus() - subscription := testBus.Subscribe() - t.Cleanup(testBus.Close) - - worker := func() <-chan error { - // the worker will never return work and the event loop will always be waiting... - return make(chan error) - } - - signaler := func() <-chan os.Signal { - ret := make(chan os.Signal) - go func() { - ret <- syscall.SIGINT - // note: we do NOT close the channel to ensure the event loop does not depend on that behavior to exit - }() - return ret - } - - ux := &uiMock{ - t: t, - } - - // ensure the mock sees basic setup/teardown events - ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil) - ux.On("Teardown").Return(nil) - - var cleanupCalled bool - cleanupFn := func() { - t.Log("cleanup called") - cleanupCalled = true - } - - assert.NoError(t, - EventLoop( - worker(), - signaler(), - subscription, - cleanupFn, - ux, - ), - ) - - assert.True(t, cleanupCalled, "cleanup function not called") - ux.AssertExpectations(t) - } - - // if there is a bug, then there is a risk of the event loop never returning - testWithTimeout(t, 5*time.Second, test) -} - -func Test_EventLoop_uiTeardownError(t *testing.T) { - test := func(t *testing.T) { - - testBus := partybus.NewBus() - subscription := testBus.Subscribe() - t.Cleanup(testBus.Close) - - finalEvent := partybus.Event{ - Type: event.CLIExit, - } - - worker := func() <-chan error { - ret := make(chan error) - go func() { - t.Log("worker running") - // send an empty item (which is ignored) ensuring we've entered the select statement, - // then close (a partial shutdown). - ret <- nil - t.Log("worker sent nothing") - close(ret) - t.Log("worker closed") - // do the other half of the shutdown - testBus.Publish(finalEvent) - t.Log("worker published final event") - }() - return ret - } - - signaler := func() <-chan os.Signal { - return nil - } - - ux := &uiMock{ - t: t, - finalEvent: finalEvent, - } - - teardownError := fmt.Errorf("sorry, dave, the UI doesn't want to be torn down") - - // ensure the mock sees at least the final event... note the event error is propagated - ux.On("Handle", finalEvent).Return(nil) - // ensure the mock sees basic setup/teardown events - ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil) - ux.On("Teardown").Return(teardownError) - - var cleanupCalled bool - cleanupFn := func() { - t.Log("cleanup called") - cleanupCalled = true - } - - // ensure we see an error returned - assert.ErrorIs(t, - EventLoop( - worker(), - signaler(), - subscription, - cleanupFn, - ux, - ), - teardownError, - "should have seen a UI teardown error, but did not", - ) - - assert.True(t, cleanupCalled, "cleanup function not called") - ux.AssertExpectations(t) - } - - // if there is a bug, then there is a risk of the event loop never returning - testWithTimeout(t, 5*time.Second, test) -} - -func testWithTimeout(t *testing.T, timeout time.Duration, test func(*testing.T)) { - done := make(chan bool) - go func() { - test(t) - done <- true - }() - - select { - case <-time.After(timeout): - t.Fatal("test timed out") - case <-done: - } -} diff --git a/cmd/syft/cli/eventloop/signals.go b/cmd/syft/cli/eventloop/signals.go deleted file mode 100644 index 72a97711e..000000000 --- a/cmd/syft/cli/eventloop/signals.go +++ /dev/null @@ -1,20 +0,0 @@ -package eventloop - -import ( - "os" - "os/signal" - "syscall" -) - -func SetupSignals() <-chan os.Signal { - c := make(chan os.Signal, 1) // Note: A buffered channel is recommended for this; see https://golang.org/pkg/os/signal/#Notify - - interruptions := []os.Signal{ - syscall.SIGINT, - syscall.SIGTERM, - } - - signal.Notify(c, interruptions...) - - return c -} diff --git a/cmd/syft/cli/eventloop/tasks.go b/cmd/syft/cli/eventloop/tasks.go index b6121d0da..feabca573 100644 --- a/cmd/syft/cli/eventloop/tasks.go +++ b/cmd/syft/cli/eventloop/tasks.go @@ -1,7 +1,7 @@ package eventloop import ( - "github.com/anchore/syft/internal/config" + "github.com/anchore/syft/cmd/syft/cli/options" "github.com/anchore/syft/internal/file" "github.com/anchore/syft/syft" "github.com/anchore/syft/syft/artifact" @@ -15,10 +15,10 @@ import ( type Task func(*sbom.Artifacts, source.Source) ([]artifact.Relationship, error) -func Tasks(app *config.Application) ([]Task, error) { +func Tasks(opts *options.Catalog) ([]Task, error) { var tasks []Task - generators := []func(app *config.Application) (Task, error){ + generators := []func(opts *options.Catalog) (Task, error){ generateCatalogPackagesTask, generateCatalogFileMetadataTask, generateCatalogFileDigestsTask, @@ -27,7 +27,7 @@ func Tasks(app *config.Application) ([]Task, error) { } for _, generator := range generators { - task, err := generator(app) + task, err := generator(opts) if err != nil { return nil, err } @@ -40,13 +40,13 @@ func Tasks(app *config.Application) ([]Task, error) { return tasks, nil } -func generateCatalogPackagesTask(app *config.Application) (Task, error) { - if !app.Package.Cataloger.Enabled { +func generateCatalogPackagesTask(opts *options.Catalog) (Task, error) { + if !opts.Package.Cataloger.Enabled { return nil, nil } task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) { - packageCatalog, relationships, theDistro, err := syft.CatalogPackages(src, app.ToCatalogerConfig()) + packageCatalog, relationships, theDistro, err := syft.CatalogPackages(src, opts.ToCatalogerConfig()) results.Packages = packageCatalog results.LinuxDistribution = theDistro @@ -57,15 +57,15 @@ func generateCatalogPackagesTask(app *config.Application) (Task, error) { return task, nil } -func generateCatalogFileMetadataTask(app *config.Application) (Task, error) { - if !app.FileMetadata.Cataloger.Enabled { +func generateCatalogFileMetadataTask(opts *options.Catalog) (Task, error) { + if !opts.FileMetadata.Cataloger.Enabled { return nil, nil } metadataCataloger := filemetadata.NewCataloger() task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) { - resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt) + resolver, err := src.FileResolver(opts.FileMetadata.Cataloger.GetScope()) if err != nil { return nil, err } @@ -81,12 +81,12 @@ func generateCatalogFileMetadataTask(app *config.Application) (Task, error) { return task, nil } -func generateCatalogFileDigestsTask(app *config.Application) (Task, error) { - if !app.FileMetadata.Cataloger.Enabled { +func generateCatalogFileDigestsTask(opts *options.Catalog) (Task, error) { + if !opts.FileMetadata.Cataloger.Enabled { return nil, nil } - hashes, err := file.Hashers(app.FileMetadata.Digests...) + hashes, err := file.Hashers(opts.FileMetadata.Digests...) if err != nil { return nil, err } @@ -94,7 +94,7 @@ func generateCatalogFileDigestsTask(app *config.Application) (Task, error) { digestsCataloger := filedigest.NewCataloger(hashes) task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) { - resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt) + resolver, err := src.FileResolver(opts.FileMetadata.Cataloger.GetScope()) if err != nil { return nil, err } @@ -110,23 +110,23 @@ func generateCatalogFileDigestsTask(app *config.Application) (Task, error) { return task, nil } -func generateCatalogSecretsTask(app *config.Application) (Task, error) { - if !app.Secrets.Cataloger.Enabled { +func generateCatalogSecretsTask(opts *options.Catalog) (Task, error) { + if !opts.Secrets.Cataloger.Enabled { return nil, nil } - patterns, err := secrets.GenerateSearchPatterns(secrets.DefaultSecretsPatterns, app.Secrets.AdditionalPatterns, app.Secrets.ExcludePatternNames) + patterns, err := secrets.GenerateSearchPatterns(secrets.DefaultSecretsPatterns, opts.Secrets.AdditionalPatterns, opts.Secrets.ExcludePatternNames) if err != nil { return nil, err } - secretsCataloger, err := secrets.NewCataloger(patterns, app.Secrets.RevealValues, app.Secrets.SkipFilesAboveSize) //nolint:staticcheck + secretsCataloger, err := secrets.NewCataloger(patterns, opts.Secrets.RevealValues, opts.Secrets.SkipFilesAboveSize) //nolint:staticcheck if err != nil { return nil, err } task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) { - resolver, err := src.FileResolver(app.Secrets.Cataloger.ScopeOpt) + resolver, err := src.FileResolver(opts.Secrets.Cataloger.GetScope()) if err != nil { return nil, err } @@ -142,18 +142,18 @@ func generateCatalogSecretsTask(app *config.Application) (Task, error) { return task, nil } -func generateCatalogContentsTask(app *config.Application) (Task, error) { - if !app.FileContents.Cataloger.Enabled { +func generateCatalogContentsTask(opts *options.Catalog) (Task, error) { + if !opts.FileContents.Cataloger.Enabled { return nil, nil } - contentsCataloger, err := filecontent.NewCataloger(app.FileContents.Globs, app.FileContents.SkipFilesAboveSize) //nolint:staticcheck + contentsCataloger, err := filecontent.NewCataloger(opts.FileContents.Globs, opts.FileContents.SkipFilesAboveSize) //nolint:staticcheck if err != nil { return nil, err } task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) { - resolver, err := src.FileResolver(app.FileContents.Cataloger.ScopeOpt) + resolver, err := src.FileResolver(opts.FileContents.Cataloger.GetScope()) if err != nil { return nil, err } @@ -169,16 +169,17 @@ func generateCatalogContentsTask(app *config.Application) (Task, error) { return task, nil } -func RunTask(t Task, a *sbom.Artifacts, src source.Source, c chan<- artifact.Relationship, errs chan<- error) { +func RunTask(t Task, a *sbom.Artifacts, src source.Source, c chan<- artifact.Relationship) error { defer close(c) relationships, err := t(a, src) if err != nil { - errs <- err - return + return err } for _, relationship := range relationships { c <- relationship } + + return nil } diff --git a/cmd/syft/cli/options/attest.go b/cmd/syft/cli/options/attest.go index 60d72191a..8c0420386 100644 --- a/cmd/syft/cli/options/attest.go +++ b/cmd/syft/cli/options/attest.go @@ -1,26 +1,17 @@ package options import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" + "github.com/anchore/clio" ) -type AttestOptions struct { - Key string +type Attest struct { + // IMPORTANT: do not show the attestation key/password in any YAML/JSON output (sensitive information) + Key secret `yaml:"key" json:"key" mapstructure:"key"` + Password secret `yaml:"password" json:"password" mapstructure:"password"` } -var _ Interface = (*AttestOptions)(nil) +var _ clio.FlagAdder = (*Attest)(nil) -func (o AttestOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error { - cmd.Flags().StringVarP(&o.Key, "key", "k", "", "the key to use for the attestation") - return bindAttestConfigOptions(cmd.Flags(), v) -} - -//nolint:revive -func bindAttestConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error { - if err := v.BindPFlag("attest.key", flags.Lookup("key")); err != nil { - return err - } - return nil +func (o Attest) AddFlags(flags clio.FlagSet) { + flags.StringVarP((*string)(&o.Key), "key", "k", "the key to use for the attestation") } diff --git a/cmd/syft/cli/options/catalog.go b/cmd/syft/cli/options/catalog.go new file mode 100644 index 000000000..50f4142d5 --- /dev/null +++ b/cmd/syft/cli/options/catalog.go @@ -0,0 +1,168 @@ +package options + +import ( + "fmt" + "sort" + "strings" + + "github.com/iancoleman/strcase" + "github.com/mitchellh/go-homedir" + + "github.com/anchore/clio" + "github.com/anchore/fangs" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/pkg/cataloger" + golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang" + "github.com/anchore/syft/syft/pkg/cataloger/kernel" + pythonCataloger "github.com/anchore/syft/syft/pkg/cataloger/python" + "github.com/anchore/syft/syft/source" +) + +type Catalog struct { + Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"` + Package pkg `yaml:"package" json:"package" mapstructure:"package"` + Golang golang `yaml:"golang" json:"golang" mapstructure:"golang"` + LinuxKernel linuxKernel `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"` + Python python `yaml:"python" json:"python" mapstructure:"python"` + 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"` + Platform string `yaml:"platform" json:"platform" mapstructure:"platform"` + Name string `yaml:"name" json:"name" mapstructure:"name"` + Source sourceCfg `yaml:"source" json:"source" mapstructure:"source"` + Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel + DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` // specify default image pull source + BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths + ExcludeBinaryOverlapByOwnership bool `yaml:"exclude-binary-overlap-by-ownership" json:"exclude-binary-overlap-by-ownership" mapstructure:"exclude-binary-overlap-by-ownership"` // exclude synthetic binary packages owned by os package files +} + +var _ interface { + clio.FlagAdder + clio.PostLoader +} = (*Catalog)(nil) + +func DefaultCatalog() Catalog { + return Catalog{ + Package: defaultPkg(), + LinuxKernel: defaultLinuxKernel(), + FileMetadata: defaultFileMetadata(), + FileClassification: defaultFileClassification(), + FileContents: defaultFileContents(), + Secrets: defaultSecrets(), + Source: defaultSourceCfg(), + Parallelism: 1, + ExcludeBinaryOverlapByOwnership: true, + } +} + +func (cfg *Catalog) AddFlags(flags clio.FlagSet) { + var validScopeValues []string + for _, scope := range source.AllScopes { + validScopeValues = append(validScopeValues, strcase.ToDelimited(string(scope), '-')) + } + flags.StringVarP(&cfg.Package.Cataloger.Scope, "scope", "s", + fmt.Sprintf("selection of layers to catalog, options=%v", validScopeValues)) + + flags.StringVarP(&cfg.Platform, "platform", "", + "an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')") + + flags.StringArrayVarP(&cfg.Exclusions, "exclude", "", + "exclude paths from being scanned using a glob expression") + + flags.StringArrayVarP(&cfg.Catalogers, "catalogers", "", + "enable one or more package catalogers") + + flags.StringVarP(&cfg.Source.Name, "name", "", + "set the name of the target being analyzed") + + if pfp, ok := flags.(fangs.PFlagSetProvider); ok { + flagSet := pfp.PFlagSet() + flagSet.Lookup("name").Deprecated = "use: source-name" + } + + flags.StringVarP(&cfg.Source.Name, "source-name", "", + "set the name of the target being analyzed") + + flags.StringVarP(&cfg.Source.Version, "source-version", "", + "set the name of the target being analyzed") + + flags.StringVarP(&cfg.BasePath, "base-path", "", + "base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory") +} + +func (cfg *Catalog) PostLoad() error { + // parse options on this struct + var catalogers []string + for _, c := range cfg.Catalogers { + for _, f := range strings.Split(c, ",") { + catalogers = append(catalogers, strings.TrimSpace(f)) + } + } + sort.Strings(catalogers) + cfg.Catalogers = catalogers + + if err := checkDefaultSourceValues(cfg.DefaultImagePullSource); err != nil { + return err + } + + if cfg.Name != "" { + log.Warnf("name parameter is deprecated. please use: source-name. name will be removed in a future version") + if cfg.Source.Name == "" { + cfg.Source.Name = cfg.Name + } + } + + return nil +} + +func (cfg Catalog) ToCatalogerConfig() cataloger.Config { + return cataloger.Config{ + Search: cataloger.SearchConfig{ + IncludeIndexedArchives: cfg.Package.SearchIndexedArchives, + IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives, + Scope: cfg.Package.Cataloger.GetScope(), + }, + Catalogers: cfg.Catalogers, + Parallelism: cfg.Parallelism, + Golang: golangCataloger.NewGoCatalogerOpts(). + WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses). + WithLocalModCacheDir(cfg.Golang.LocalModCacheDir). + WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses). + WithProxy(cfg.Golang.Proxy). + WithNoProxy(cfg.Golang.NoProxy), + LinuxKernel: kernel.LinuxCatalogerConfig{ + CatalogModules: cfg.LinuxKernel.CatalogModules, + }, + Python: pythonCataloger.CatalogerConfig{ + GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements, + }, + ExcludeBinaryOverlapByOwnership: cfg.ExcludeBinaryOverlapByOwnership, + } +} + +var validDefaultSourceValues = []string{"registry", "docker", "podman", ""} + +func checkDefaultSourceValues(source string) error { + validValues := internal.NewStringSet(validDefaultSourceValues...) + if !validValues.Contains(source) { + validValuesString := strings.Join(validDefaultSourceValues, ", ") + return fmt.Errorf("%s is not a valid default source; please use one of the following: %s''", source, validValuesString) + } + + return nil +} + +func expandFilePath(file string) (string, error) { + if file != "" { + expandedPath, err := homedir.Expand(file) + if err != nil { + return "", fmt.Errorf("unable to expand file path=%q: %w", file, err) + } + file = expandedPath + } + return file, nil +} diff --git a/cmd/syft/cli/options/config.go b/cmd/syft/cli/options/config.go new file mode 100644 index 000000000..85aeb69dd --- /dev/null +++ b/cmd/syft/cli/options/config.go @@ -0,0 +1,6 @@ +package options + +// Config holds a reference to the specific config file that was used to load application configuration +type Config struct { + ConfigFile string `yaml:"config" json:"config" mapstructure:"config"` +} diff --git a/cmd/syft/cli/options/file_classification.go b/cmd/syft/cli/options/file_classification.go new file mode 100644 index 000000000..9f1abcdfb --- /dev/null +++ b/cmd/syft/cli/options/file_classification.go @@ -0,0 +1,17 @@ +package options + +import ( + "github.com/anchore/syft/syft/source" +) + +type fileClassification struct { + Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"` +} + +func defaultFileClassification() fileClassification { + return fileClassification{ + Cataloger: scope{ + Scope: source.SquashedScope.String(), + }, + } +} diff --git a/cmd/syft/cli/options/file_contents.go b/cmd/syft/cli/options/file_contents.go new file mode 100644 index 000000000..6dba465f5 --- /dev/null +++ b/cmd/syft/cli/options/file_contents.go @@ -0,0 +1,21 @@ +package options + +import ( + "github.com/anchore/syft/internal/file" + "github.com/anchore/syft/syft/source" +) + +type fileContents struct { + Cataloger scope `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"` +} + +func defaultFileContents() fileContents { + return fileContents{ + Cataloger: scope{ + Scope: source.SquashedScope.String(), + }, + SkipFilesAboveSize: 1 * file.MB, + } +} diff --git a/cmd/syft/cli/options/file_metadata.go b/cmd/syft/cli/options/file_metadata.go new file mode 100644 index 000000000..eb2335a24 --- /dev/null +++ b/cmd/syft/cli/options/file_metadata.go @@ -0,0 +1,19 @@ +package options + +import ( + "github.com/anchore/syft/syft/source" +) + +type fileMetadata struct { + Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"` + Digests []string `yaml:"digests" json:"digests" mapstructure:"digests"` +} + +func defaultFileMetadata() fileMetadata { + return fileMetadata{ + Cataloger: scope{ + Scope: source.SquashedScope.String(), + }, + Digests: []string{"sha256"}, + } +} diff --git a/cmd/syft/cli/options/fulcio.go b/cmd/syft/cli/options/fulcio.go deleted file mode 100644 index b1dec9eb6..000000000 --- a/cmd/syft/cli/options/fulcio.go +++ /dev/null @@ -1,49 +0,0 @@ -package options - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" -) - -const defaultFulcioURL = "https://fulcio.sigstore.dev" - -// FulcioOptions is the wrapper for Fulcio related options. -type FulcioOptions struct { - URL string - IdentityToken string - InsecureSkipFulcioVerify bool -} - -var _ Interface = (*FulcioOptions)(nil) - -// AddFlags implements Interface -func (o *FulcioOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error { - // TODO: change this back to api.SigstorePublicServerURL after the v1 migration is complete. - cmd.Flags().StringVar(&o.URL, "fulcio-url", defaultFulcioURL, - "address of sigstore PKI server") - - cmd.Flags().StringVar(&o.IdentityToken, "identity-token", "", - "identity token to use for certificate from fulcio") - - cmd.Flags().BoolVar(&o.InsecureSkipFulcioVerify, "insecure-skip-verify", false, - "skip verifying fulcio certificat and the SCT (Signed Certificate Timestamp) (this should only be used for testing).") - return bindFulcioConfigOptions(cmd.Flags(), v) -} - -//nolint:revive -func bindFulcioConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error { - if err := v.BindPFlag("attest.fulcio-url", flags.Lookup("fulcio-url")); err != nil { - return err - } - - if err := v.BindPFlag("attest.fulcio-identity-token", flags.Lookup("identity-token")); err != nil { - return err - } - - if err := v.BindPFlag("attest.insecure-skip-verify", flags.Lookup("insecure-skip-verify")); err != nil { - return err - } - - return nil -} diff --git a/internal/config/golang.go b/cmd/syft/cli/options/golang.go similarity index 64% rename from internal/config/golang.go rename to cmd/syft/cli/options/golang.go index 56ebbed08..ff99f414f 100644 --- a/internal/config/golang.go +++ b/cmd/syft/cli/options/golang.go @@ -1,6 +1,4 @@ -package config - -import "github.com/spf13/viper" +package options type golang struct { SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"` @@ -9,11 +7,3 @@ type golang struct { Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"` NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"` } - -func (cfg golang) loadDefaultValues(v *viper.Viper) { - v.SetDefault("golang.search-local-mod-cache-licenses", false) - v.SetDefault("golang.local-mod-cache-dir", "") - v.SetDefault("golang.search-remote-licenses", false) - v.SetDefault("golang.proxy", "") - v.SetDefault("golang.no-proxy", "") -} diff --git a/cmd/syft/cli/options/linux_kernel.go b/cmd/syft/cli/options/linux_kernel.go new file mode 100644 index 000000000..c56466abf --- /dev/null +++ b/cmd/syft/cli/options/linux_kernel.go @@ -0,0 +1,11 @@ +package options + +type linuxKernel struct { + CatalogModules bool `json:"catalog-modules" yaml:"catalog-modules" mapstructure:"catalog-modules"` +} + +func defaultLinuxKernel() linuxKernel { + return linuxKernel{ + CatalogModules: true, + } +} diff --git a/cmd/syft/cli/options/oidc.go b/cmd/syft/cli/options/oidc.go deleted file mode 100644 index 580ecc176..000000000 --- a/cmd/syft/cli/options/oidc.go +++ /dev/null @@ -1,49 +0,0 @@ -package options - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" -) - -const DefaultOIDCIssuerURL = "https://oauth2.sigstore.dev/auth" - -// OIDCOptions is the wrapper for OIDC related options. -type OIDCOptions struct { - Issuer string - ClientID string - RedirectURL string -} - -var _ Interface = (*OIDCOptions)(nil) - -// AddFlags implements Interface -func (o *OIDCOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error { - cmd.Flags().StringVar(&o.Issuer, "oidc-issuer", DefaultOIDCIssuerURL, - "OIDC provider to be used to issue ID token") - - cmd.Flags().StringVar(&o.ClientID, "oidc-client-id", "sigstore", - "OIDC client ID for application") - - cmd.Flags().StringVar(&o.RedirectURL, "oidc-redirect-url", "", - "OIDC redirect URL (Optional)") - - return bindOIDCConfigOptions(cmd.Flags(), v) -} - -//nolint:revive -func bindOIDCConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error { - if err := v.BindPFlag("attest.oidc-issuer", flags.Lookup("oidc-issuer")); err != nil { - return err - } - - if err := v.BindPFlag("attest.oidc-client-id", flags.Lookup("oidc-client-id")); err != nil { - return err - } - - if err := v.BindPFlag("attest.oidc-redirect-url", flags.Lookup("oidc-redirect-url")); err != nil { - return err - } - - return nil -} diff --git a/cmd/syft/cli/options/options.go b/cmd/syft/cli/options/options.go deleted file mode 100644 index f8646bbb9..000000000 --- a/cmd/syft/cli/options/options.go +++ /dev/null @@ -1,11 +0,0 @@ -package options - -import ( - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -type Interface interface { - // AddFlags adds this options' flags to the cobra command. - AddFlags(cmd *cobra.Command, v *viper.Viper) error -} diff --git a/cmd/syft/cli/options/output.go b/cmd/syft/cli/options/output.go new file mode 100644 index 000000000..1c321fd47 --- /dev/null +++ b/cmd/syft/cli/options/output.go @@ -0,0 +1,95 @@ +package options + +import ( + "fmt" + + "golang.org/x/exp/slices" + + "github.com/anchore/clio" + "github.com/anchore/syft/syft/formats" + "github.com/anchore/syft/syft/formats/table" + "github.com/anchore/syft/syft/formats/template" + "github.com/anchore/syft/syft/sbom" +) + +// MultiOutput has the standard output options syft accepts: multiple -o, --file, --template +type MultiOutput struct { + Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output + OutputFile `yaml:",inline" json:"" mapstructure:",squash"` + OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output +} + +var _ interface { + clio.FlagAdder +} = (*MultiOutput)(nil) + +func DefaultOutput() MultiOutput { + return MultiOutput{ + Outputs: []string{string(table.ID)}, + } +} + +func (o *MultiOutput) AddFlags(flags clio.FlagSet) { + flags.StringArrayVarP(&o.Outputs, "output", "o", + fmt.Sprintf("report output format, options=%v", formats.AllIDs())) + + flags.StringVarP(&o.OutputTemplatePath, "template", "t", + "specify the path to a Go template file") +} + +func (o *MultiOutput) SBOMWriter() (sbom.Writer, error) { + return makeSBOMWriter(o.Outputs, o.File, o.OutputTemplatePath) +} + +// SingleOutput allows only 1 output to be specified, with a user able to set what options are allowed by setting AllowableOptions +type SingleOutput struct { + AllowableOptions []string `yaml:"-" json:"-" mapstructure:"-"` + Output string `yaml:"output" json:"output" mapstructure:"output"` + OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output +} + +var _ clio.FlagAdder = (*SingleOutput)(nil) + +func (o *SingleOutput) AddFlags(flags clio.FlagSet) { + flags.StringVarP(&o.Output, "output", "o", + fmt.Sprintf("report output format, options=%v", o.AllowableOptions)) + + if slices.Contains(o.AllowableOptions, template.ID.String()) { + flags.StringVarP(&o.OutputTemplatePath, "template", "t", + "specify the path to a Go template file") + } +} + +func (o *SingleOutput) SBOMWriter(file string) (sbom.Writer, error) { + return makeSBOMWriter([]string{o.Output}, file, o.OutputTemplatePath) +} + +// OutputFile is only the --file argument +type OutputFile struct { + File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to +} + +var _ interface { + clio.FlagAdder + clio.PostLoader +} = (*OutputFile)(nil) + +func (o *OutputFile) AddFlags(flags clio.FlagSet) { + flags.StringVarP(&o.File, "file", "", + "file to write the default report output to (default is STDOUT)") +} + +func (o *OutputFile) PostLoad() error { + if o.File != "" { + file, err := expandFilePath(o.File) + if err != nil { + return err + } + o.File = file + } + return nil +} + +func (o *OutputFile) SBOMWriter(f sbom.Format) (sbom.Writer, error) { + return makeSBOMWriterForFormat(f, o.File) +} diff --git a/cmd/syft/cli/options/packages.go b/cmd/syft/cli/options/packages.go deleted file mode 100644 index 259a78781..000000000 --- a/cmd/syft/cli/options/packages.go +++ /dev/null @@ -1,118 +0,0 @@ -package options - -import ( - "fmt" - - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" - - "github.com/anchore/syft/syft/formats" - "github.com/anchore/syft/syft/formats/table" - "github.com/anchore/syft/syft/pkg/cataloger" - "github.com/anchore/syft/syft/source" -) - -type PackagesOptions struct { - Scope string - Output []string - OutputTemplatePath string - File string - Platform string - Exclude []string - Catalogers []string - SourceName string - SourceVersion string - BasePath string -} - -var _ Interface = (*PackagesOptions)(nil) - -func (o *PackagesOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error { - cmd.Flags().StringVarP(&o.Scope, "scope", "s", cataloger.DefaultSearchConfig().Scope.String(), - fmt.Sprintf("selection of layers to catalog, options=%v", source.AllScopes)) - - cmd.Flags().StringArrayVarP(&o.Output, "output", "o", []string{string(table.ID)}, - fmt.Sprintf("report output format, options=%v", formats.AllIDs())) - - cmd.Flags().StringVarP(&o.File, "file", "", "", - "file to write the default report output to (default is STDOUT)") - - cmd.Flags().StringVarP(&o.OutputTemplatePath, "template", "t", "", - "specify the path to a Go template file") - - cmd.Flags().StringVarP(&o.Platform, "platform", "", "", - "an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')") - - cmd.Flags().StringArrayVarP(&o.Exclude, "exclude", "", nil, - "exclude paths from being scanned using a glob expression") - - cmd.Flags().StringArrayVarP(&o.Catalogers, "catalogers", "", nil, - "enable one or more package catalogers") - - cmd.Flags().StringVarP(&o.SourceName, "name", "", "", - "set the name of the target being analyzed") - cmd.Flags().Lookup("name").Deprecated = "use: source-name" - - cmd.Flags().StringVarP(&o.SourceName, "source-name", "", "", - "set the name of the target being analyzed") - - cmd.Flags().StringVarP(&o.SourceVersion, "source-version", "", "", - "set the name of the target being analyzed") - - cmd.Flags().StringVarP(&o.BasePath, "base-path", "", "", - "base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory") - - return bindPackageConfigOptions(cmd.Flags(), v) -} - -//nolint:revive -func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error { - // Formatting & Input options ////////////////////////////////////////////// - - if err := v.BindPFlag("package.cataloger.scope", flags.Lookup("scope")); err != nil { - return err - } - - if err := v.BindPFlag("file", flags.Lookup("file")); err != nil { - return err - } - - if err := v.BindPFlag("exclude", flags.Lookup("exclude")); err != nil { - return err - } - - if err := v.BindPFlag("catalogers", flags.Lookup("catalogers")); err != nil { - return err - } - - if err := v.BindPFlag("name", flags.Lookup("name")); err != nil { - return err - } - - if err := v.BindPFlag("source.name", flags.Lookup("source-name")); err != nil { - return err - } - - if err := v.BindPFlag("source.version", flags.Lookup("source-version")); err != nil { - return err - } - - if err := v.BindPFlag("output", flags.Lookup("output")); err != nil { - return err - } - - if err := v.BindPFlag("output-template-path", flags.Lookup("template")); err != nil { - return err - } - - if err := v.BindPFlag("platform", flags.Lookup("platform")); err != nil { - return err - } - - if err := v.BindPFlag("base-path", flags.Lookup("base-path")); err != nil { - return err - } - - return nil -} diff --git a/cmd/syft/cli/options/pkg.go b/cmd/syft/cli/options/pkg.go new file mode 100644 index 000000000..329dad9ed --- /dev/null +++ b/cmd/syft/cli/options/pkg.go @@ -0,0 +1,23 @@ +package options + +import ( + "github.com/anchore/syft/syft/pkg/cataloger" +) + +type pkg struct { + Cataloger scope `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"` +} + +func defaultPkg() pkg { + c := cataloger.DefaultSearchConfig() + return pkg{ + SearchIndexedArchives: c.IncludeIndexedArchives, + SearchUnindexedArchives: c.IncludeUnindexedArchives, + Cataloger: scope{ + Enabled: true, + Scope: c.Scope.String(), + }, + } +} diff --git a/internal/config/python.go b/cmd/syft/cli/options/python.go similarity index 50% rename from internal/config/python.go rename to cmd/syft/cli/options/python.go index d86da39be..0efab8713 100644 --- a/internal/config/python.go +++ b/cmd/syft/cli/options/python.go @@ -1,13 +1,5 @@ -package config - -import ( - "github.com/spf13/viper" -) +package options type python struct { GuessUnpinnedRequirements bool `json:"guess-unpinned-requirements" yaml:"guess-unpinned-requirements" mapstructure:"guess-unpinned-requirements"` } - -func (cfg python) loadDefaultValues(v *viper.Viper) { - v.SetDefault("python.guess-unpinned-requirements", false) -} diff --git a/internal/config/registry.go b/cmd/syft/cli/options/registry.go similarity index 71% rename from internal/config/registry.go rename to cmd/syft/cli/options/registry.go index 6b0e5dc9c..455ba93d5 100644 --- a/internal/config/registry.go +++ b/cmd/syft/cli/options/registry.go @@ -1,21 +1,18 @@ -package config +package options import ( "os" - "github.com/spf13/viper" - + "github.com/anchore/clio" "github.com/anchore/stereoscope/pkg/image" ) type RegistryCredentials struct { Authority string `yaml:"authority" json:"authority" mapstructure:"authority"` - // IMPORTANT: do not show the username in any YAML/JSON output (sensitive information) - Username string `yaml:"-" json:"-" mapstructure:"username"` - // IMPORTANT: do not show the password in any YAML/JSON output (sensitive information) - Password string `yaml:"-" json:"-" mapstructure:"password"` - // IMPORTANT: do not show the token in any YAML/JSON output (sensitive information) - Token string `yaml:"-" json:"-" mapstructure:"token"` + // IMPORTANT: do not show any credential information, use secret type to automatically redact the values + Username secret `yaml:"username" json:"username" mapstructure:"username"` + Password secret `yaml:"password" json:"password" mapstructure:"password"` + Token secret `yaml:"token" json:"token" mapstructure:"token"` TLSCert string `yaml:"tls-cert,omitempty" json:"tls-cert,omitempty" mapstructure:"tls-cert"` TLSKey string `yaml:"tls-key,omitempty" json:"tls-key,omitempty" mapstructure:"tls-key"` @@ -28,15 +25,9 @@ type registry struct { CACert string `yaml:"ca-cert" json:"ca-cert" mapstructure:"ca-cert"` } -func (cfg registry) loadDefaultValues(v *viper.Viper) { - v.SetDefault("registry.insecure-skip-tls-verify", false) - v.SetDefault("registry.insecure-use-http", false) - v.SetDefault("registry.auth", []RegistryCredentials{}) - v.SetDefault("registry.ca-cert", "") -} +var _ clio.PostLoader = (*registry)(nil) -//nolint:unparam -func (cfg *registry) parseConfigValues() error { +func (cfg *registry) PostLoad() error { // there may be additional credentials provided by env var that should be appended to the set of credentials authority, username, password, token, tlsCert, tlsKey := os.Getenv("SYFT_REGISTRY_AUTH_AUTHORITY"), @@ -48,12 +39,14 @@ func (cfg *registry) parseConfigValues() error { if hasNonEmptyCredentials(username, password, token, tlsCert, tlsKey) { // note: we prepend the credentials such that the environment variables take precedence over on-disk configuration. + // since this PostLoad is called before the PostLoad on the Auth credentials list, + // all appropriate redactions will be added cfg.Auth = append([]RegistryCredentials{ { Authority: authority, - Username: username, - Password: password, - Token: token, + Username: secret(username), + Password: secret(password), + Token: secret(token), TLSCert: tlsCert, TLSKey: tlsKey, }, @@ -74,9 +67,9 @@ func (cfg *registry) ToOptions() *image.RegistryOptions { for i, a := range cfg.Auth { auth[i] = image.RegistryCredentials{ Authority: a.Authority, - Username: a.Username, - Password: a.Password, - Token: a.Token, + Username: a.Username.String(), + Password: a.Password.String(), + Token: a.Token.String(), ClientCert: a.TLSCert, ClientKey: a.TLSKey, } diff --git a/internal/config/registry_test.go b/cmd/syft/cli/options/registry_test.go similarity index 99% rename from internal/config/registry_test.go rename to cmd/syft/cli/options/registry_test.go index 034384e61..4979fcbc2 100644 --- a/internal/config/registry_test.go +++ b/cmd/syft/cli/options/registry_test.go @@ -1,4 +1,4 @@ -package config +package options import ( "fmt" diff --git a/cmd/syft/cli/options/rekor.go b/cmd/syft/cli/options/rekor.go deleted file mode 100644 index 49539539c..000000000 --- a/cmd/syft/cli/options/rekor.go +++ /dev/null @@ -1,33 +0,0 @@ -package options - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" -) - -const DefaultRekorURL = "https://rekor.sigstore.dev" - -// RekorOptions is the wrapper for Rekor related options. -type RekorOptions struct { - URL string -} - -var _ Interface = (*RekorOptions)(nil) - -// AddFlags implements Interface -func (o *RekorOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error { - cmd.Flags().StringVar(&o.URL, "rekor-url", DefaultRekorURL, - "address of rekor STL server") - return bindRekorConfigOptions(cmd.Flags(), v) -} - -//nolint:revive -func bindRekorConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error { - // TODO: config re-design - if err := v.BindPFlag("attest.rekor-url", flags.Lookup("rekor-url")); err != nil { - return err - } - - return nil -} diff --git a/cmd/syft/cli/options/root.go b/cmd/syft/cli/options/root.go deleted file mode 100644 index 316fbff17..000000000 --- a/cmd/syft/cli/options/root.go +++ /dev/null @@ -1,37 +0,0 @@ -package options - -import ( - "github.com/spf13/cobra" - "github.com/spf13/pflag" - "github.com/spf13/viper" -) - -type RootOptions struct { - Config string - Quiet bool - Verbose int -} - -var _ Interface = (*RootOptions)(nil) - -func (o *RootOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error { - cmd.PersistentFlags().StringVarP(&o.Config, "config", "c", "", "application config file") - cmd.PersistentFlags().CountVarP(&o.Verbose, "verbose", "v", "increase verbosity (-v = info, -vv = debug)") - cmd.PersistentFlags().BoolVarP(&o.Quiet, "quiet", "q", false, "suppress all logging output") - - return bindRootConfigOptions(cmd.PersistentFlags(), v) -} - -//nolint:revive -func bindRootConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error { - if err := v.BindPFlag("config", flags.Lookup("config")); err != nil { - return err - } - if err := v.BindPFlag("verbosity", flags.Lookup("verbose")); err != nil { - return err - } - if err := v.BindPFlag("quiet", flags.Lookup("quiet")); err != nil { - return err - } - return nil -} diff --git a/cmd/syft/cli/options/scope.go b/cmd/syft/cli/options/scope.go new file mode 100644 index 000000000..ae6efcffe --- /dev/null +++ b/cmd/syft/cli/options/scope.go @@ -0,0 +1,27 @@ +package options + +import ( + "fmt" + + "github.com/anchore/clio" + "github.com/anchore/syft/syft/source" +) + +type scope struct { + Enabled bool `yaml:"enabled" json:"enabled" mapstructure:"enabled"` + Scope string `yaml:"scope" json:"scope" mapstructure:"scope"` +} + +var _ clio.PostLoader = (*scope)(nil) + +func (opt *scope) PostLoad() error { + s := opt.GetScope() + if s == source.UnknownScope { + return fmt.Errorf("bad scope value %v", opt.Scope) + } + return nil +} + +func (opt scope) GetScope() source.Scope { + return source.ParseScope(opt.Scope) +} diff --git a/cmd/syft/cli/options/secret.go b/cmd/syft/cli/options/secret.go new file mode 100644 index 000000000..3224dd803 --- /dev/null +++ b/cmd/syft/cli/options/secret.go @@ -0,0 +1,25 @@ +package options + +import ( + "fmt" + + "github.com/anchore/clio" + "github.com/anchore/syft/internal/redact" +) + +type secret string + +var _ interface { + fmt.Stringer + clio.PostLoader +} = (*secret)(nil) + +// PostLoad needs to use a pointer receiver, even if it's not modifying the value +func (r *secret) PostLoad() error { + redact.Add(string(*r)) + return nil +} + +func (r secret) String() string { + return string(r) +} diff --git a/internal/config/secrets.go b/cmd/syft/cli/options/secrets.go similarity index 52% rename from internal/config/secrets.go rename to cmd/syft/cli/options/secrets.go index 0d0efc4f1..58693f6e6 100644 --- a/internal/config/secrets.go +++ b/cmd/syft/cli/options/secrets.go @@ -1,29 +1,23 @@ -package config +package options import ( - "github.com/spf13/viper" - "github.com/anchore/syft/internal/file" "github.com/anchore/syft/syft/source" ) type secrets struct { - Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"` + Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"` 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"` } -func (cfg secrets) loadDefaultValues(v *viper.Viper) { - v.SetDefault("secrets.cataloger.enabled", catalogerEnabledDefault) - v.SetDefault("secrets.cataloger.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 defaultSecrets() secrets { + return secrets{ + Cataloger: scope{ + Scope: source.AllLayersScope.String(), + }, + SkipFilesAboveSize: 1 * file.MB, + } } diff --git a/internal/config/source.go b/cmd/syft/cli/options/source.go similarity index 67% rename from internal/config/source.go rename to cmd/syft/cli/options/source.go index 5346f994f..41e301996 100644 --- a/internal/config/source.go +++ b/cmd/syft/cli/options/source.go @@ -1,6 +1,4 @@ -package config - -import "github.com/spf13/viper" +package options type sourceCfg struct { Name string `json:"name" yaml:"name" mapstructure:"name"` @@ -12,6 +10,10 @@ type fileSource struct { Digests []string `json:"digests" yaml:"digests" mapstructure:"digests"` } -func (cfg sourceCfg) loadDefaultValues(v *viper.Viper) { - v.SetDefault("source.file.digests", []string{"sha256"}) +func defaultSourceCfg() sourceCfg { + return sourceCfg{ + File: fileSource{ + Digests: []string{"sha256"}, + }, + } } diff --git a/cmd/syft/cli/options/update_check.go b/cmd/syft/cli/options/update_check.go new file mode 100644 index 000000000..a05b52420 --- /dev/null +++ b/cmd/syft/cli/options/update_check.go @@ -0,0 +1,11 @@ +package options + +type UpdateCheck struct { + 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 +} + +func DefaultUpdateCheck() UpdateCheck { + return UpdateCheck{ + CheckForAppUpdate: true, + } +} diff --git a/cmd/syft/cli/options/verbose.go b/cmd/syft/cli/options/verbose.go deleted file mode 100644 index aa53bb40e..000000000 --- a/cmd/syft/cli/options/verbose.go +++ /dev/null @@ -1,18 +0,0 @@ -package options - -import ( - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/config" - "github.com/anchore/syft/internal/log" -) - -func IsVerbose(app *config.Application) (result bool) { - isPipedInput, err := internal.IsPipedInput() - if err != nil { - // since we can't tell if there was piped input we assume that there could be to disable the ETUI - log.Warnf("unable to determine if there is piped input: %+v", err) - return true - } - // verbosity should consider if there is piped input (in which case we should not show the ETUI) - return app.Verbosity > 0 || isPipedInput -} diff --git a/cmd/syft/cli/options/version.go b/cmd/syft/cli/options/version.go deleted file mode 100644 index a3ac49cf6..000000000 --- a/cmd/syft/cli/options/version.go +++ /dev/null @@ -1,17 +0,0 @@ -package options - -import ( - "github.com/spf13/cobra" - "github.com/spf13/viper" -) - -type VersionOptions struct { - Output string -} - -var _ Interface = (*VersionOptions)(nil) - -func (o *VersionOptions) AddFlags(cmd *cobra.Command, _ *viper.Viper) error { - cmd.Flags().StringVarP(&o.Output, "output", "o", "text", "format to show version information (available=[text, json])") - return nil -} diff --git a/cmd/syft/cli/options/writer.go b/cmd/syft/cli/options/writer.go index 1ea4ff1a2..047245169 100644 --- a/cmd/syft/cli/options/writer.go +++ b/cmd/syft/cli/options/writer.go @@ -26,9 +26,9 @@ var _ interface { sbom.Writer } = (*sbomStreamWriter)(nil) -// MakeSBOMWriter creates a sbom.Writer for output or returns an error. this will either return a valid writer +// makeSBOMWriter creates a sbom.Writer for output or returns an error. this will either return a valid writer // or an error but neither both and if there is no error, sbom.Writer.Close() should be called -func MakeSBOMWriter(outputs []string, defaultFile, templateFilePath string) (sbom.Writer, error) { +func makeSBOMWriter(outputs []string, defaultFile, templateFilePath string) (sbom.Writer, error) { outputOptions, err := parseSBOMOutputFlags(outputs, defaultFile, templateFilePath) if err != nil { return nil, err @@ -42,8 +42,8 @@ func MakeSBOMWriter(outputs []string, defaultFile, templateFilePath string) (sbo return writer, nil } -// MakeSBOMWriterForFormat creates a sbom.Writer for for the given format or returns an error. -func MakeSBOMWriterForFormat(format sbom.Format, path string) (sbom.Writer, error) { +// makeSBOMWriterForFormat creates a sbom.Writer for for the given format or returns an error. +func makeSBOMWriterForFormat(format sbom.Format, path string) (sbom.Writer, error) { writer, err := newSBOMMultiWriter(newSBOMWriterDescription(format, path)) if err != nil { return nil, err diff --git a/cmd/syft/cli/options/writer_test.go b/cmd/syft/cli/options/writer_test.go index 643d251cd..f71d2b9d3 100644 --- a/cmd/syft/cli/options/writer_test.go +++ b/cmd/syft/cli/options/writer_test.go @@ -34,7 +34,7 @@ func Test_MakeSBOMWriter(t *testing.T) { } for _, tt := range tests { - _, err := MakeSBOMWriter(tt.outputs, "", "") + _, err := makeSBOMWriter(tt.outputs, "", "") tt.wantErr(t, err) } } diff --git a/cmd/syft/cli/packages.go b/cmd/syft/cli/packages.go deleted file mode 100644 index 88154b199..000000000 --- a/cmd/syft/cli/packages.go +++ /dev/null @@ -1,86 +0,0 @@ -package cli - -import ( - "fmt" - "log" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/cmd/syft/cli/packages" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/config" -) - -const ( - 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 - {{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.3 Tag-Value formatted SBOM - {{.appName}} {{.command}} alpine:latest -o spdx@2.2 show a SPDX 2.2 Tag-Value formatted SBOM - {{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.3 JSON formatted SBOM - {{.appName}} {{.command}} alpine:latest -o spdx-json@2.2 show a SPDX 2.2 JSON formatted SBOM - {{.appName}} {{.command}} alpine:latest -vv show verbose debug information - {{.appName}} {{.command}} alpine:latest -o template -t my_format.tmpl show a SBOM formatted according to given template file - - Supports the following image sources: - {{.appName}} {{.command}} yourrepo/yourimage:tag defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry. - {{.appName}} {{.command}} path/to/a/file/or/dir a Docker tar, OCI tar, OCI directory, SIF container, or generic filesystem directory -` - - 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) - {{.appName}} {{.command}} oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise) - {{.appName}} {{.command}} singularity:path/to/yourimage.sif read directly from a Singularity Image Format (SIF) container on disk -` - nonImageSchemeHelp = ` {{.appName}} {{.command}} dir:path/to/yourproject read directly from a path on disk (any directory) - {{.appName}} {{.command}} file:path/to/yourproject/file read directly from a path on disk (any single file) -` - packagesSchemeHelp = "\n" + indent + schemeHelpHeader + "\n" + imageSchemeHelp + nonImageSchemeHelp - - packagesHelp = packagesExample + packagesSchemeHelp -) - -//nolint:dupl -func Packages(v *viper.Viper, app *config.Application, ro *options.RootOptions, po *options.PackagesOptions) *cobra.Command { - cmd := &cobra.Command{ - Use: "packages [SOURCE]", - Short: "Generate a package SBOM", - Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from container images and filesystems", - Example: internal.Tprintf(packagesHelp, map[string]interface{}{ - "appName": internal.ApplicationName, - "command": "packages", - }), - Args: func(cmd *cobra.Command, args []string) error { - if err := app.LoadAllValues(v, ro.Config); err != nil { - return fmt.Errorf("invalid application config: %w", err) - } - // configure logging for command - newLogWrapper(app) - logApplicationConfig(app) - return validateArgs(cmd, args) - }, - SilenceUsage: true, - SilenceErrors: true, - RunE: func(cmd *cobra.Command, args []string) error { - if app.CheckForAppUpdate { - // TODO: this is broke, the bus isn't available yet - checkForApplicationUpdate() - } - return packages.Run(cmd.Context(), app, args) - }, - } - - err := po.AddFlags(cmd, v) - if err != nil { - log.Fatal(err) - } - - return cmd -} diff --git a/cmd/syft/cli/packages/packages.go b/cmd/syft/cli/packages/packages.go deleted file mode 100644 index 0f87720fb..000000000 --- a/cmd/syft/cli/packages/packages.go +++ /dev/null @@ -1,192 +0,0 @@ -package packages - -import ( - "context" - "fmt" - - "github.com/wagoodman/go-partybus" - - "github.com/anchore/stereoscope" - "github.com/anchore/stereoscope/pkg/image" - "github.com/anchore/syft/cmd/syft/cli/eventloop" - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/cmd/syft/internal/ui" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/bus" - "github.com/anchore/syft/internal/config" - "github.com/anchore/syft/internal/file" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/internal/version" - "github.com/anchore/syft/syft" - "github.com/anchore/syft/syft/artifact" - "github.com/anchore/syft/syft/formats/template" - "github.com/anchore/syft/syft/sbom" - "github.com/anchore/syft/syft/source" -) - -func Run(_ context.Context, app *config.Application, args []string) error { - err := ValidateOutputOptions(app) - if err != nil { - return err - } - - writer, err := options.MakeSBOMWriter(app.Outputs, app.File, app.OutputTemplatePath) - if err != nil { - return err - } - - // could be an image or a directory, with or without a scheme - userInput := args[0] - - eventBus := partybus.NewBus() - stereoscope.SetBus(eventBus) - syft.SetBus(eventBus) - subscription := eventBus.Subscribe() - - return eventloop.EventLoop( - execWorker(app, userInput, writer), - eventloop.SetupSignals(), - subscription, - stereoscope.Cleanup, - ui.Select(options.IsVerbose(app), app.Quiet)..., - ) -} - -// nolint:funlen -func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error { - errs := make(chan error) - go func() { - defer close(errs) - defer bus.Exit() - - detection, err := source.Detect( - userInput, - source.DetectConfig{ - DefaultImageSource: app.DefaultImagePullSource, - }, - ) - if err != nil { - errs <- fmt.Errorf("could not deteremine source: %w", err) - return - } - - var platform *image.Platform - - if app.Platform != "" { - platform, err = image.NewPlatform(app.Platform) - if err != nil { - errs <- fmt.Errorf("invalid platform: %w", err) - return - } - } - - hashers, err := file.Hashers(app.Source.File.Digests...) - if err != nil { - errs <- fmt.Errorf("invalid hash: %w", err) - return - } - - src, err := detection.NewSource( - source.DetectionSourceConfig{ - Alias: source.Alias{ - Name: app.Source.Name, - Version: app.Source.Version, - }, - RegistryOptions: app.Registry.ToOptions(), - Platform: platform, - Exclude: source.ExcludeConfig{ - Paths: app.Exclusions, - }, - DigestAlgorithms: hashers, - BasePath: app.BasePath, - }, - ) - - if err != nil { - errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err) - return - } - - defer func() { - if src != nil { - if err := src.Close(); err != nil { - log.Tracef("unable to close source: %+v", err) - } - } - }() - - s, err := GenerateSBOM(src, errs, app) - if err != nil { - errs <- err - return - } - - if s == nil { - errs <- fmt.Errorf("no SBOM produced for %q", userInput) - return - } - - if err := writer.Write(*s); err != nil { - errs <- fmt.Errorf("failed to write SBOM: %w", err) - return - } - }() - return errs -} - -func GenerateSBOM(src source.Source, errs chan error, app *config.Application) (*sbom.SBOM, error) { - tasks, err := eventloop.Tasks(app) - if err != nil { - return nil, err - } - - s := sbom.SBOM{ - Source: src.Describe(), - Descriptor: sbom.Descriptor{ - Name: internal.ApplicationName, - Version: version.FromBuild().Version, - Configuration: app, - }, - } - - buildRelationships(&s, src, tasks, errs) - - return &s, nil -} - -func buildRelationships(s *sbom.SBOM, src source.Source, tasks []eventloop.Task, errs chan error) { - var relationships []<-chan artifact.Relationship - for _, task := range tasks { - c := make(chan artifact.Relationship) - relationships = append(relationships, c) - go eventloop.RunTask(task, &s.Artifacts, src, c, errs) - } - - s.Relationships = append(s.Relationships, MergeRelationships(relationships...)...) -} - -func MergeRelationships(cs ...<-chan artifact.Relationship) (relationships []artifact.Relationship) { - for _, c := range cs { - for n := range c { - relationships = append(relationships, n) - } - } - - return relationships -} - -func ValidateOutputOptions(app *config.Application) error { - var usesTemplateOutput bool - for _, o := range app.Outputs { - if o == template.ID.String() { - usesTemplateOutput = true - break - } - } - - if usesTemplateOutput && app.OutputTemplatePath == "" { - return fmt.Errorf(`must specify path to template file when using "template" output format`) - } - - return nil -} diff --git a/cmd/syft/cli/poweruser.go b/cmd/syft/cli/poweruser.go deleted file mode 100644 index e3c935d9e..000000000 --- a/cmd/syft/cli/poweruser.go +++ /dev/null @@ -1,51 +0,0 @@ -package cli - -import ( - "fmt" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/cmd/syft/cli/poweruser" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/config" -) - -const powerUserExample = ` {{.appName}} {{.command}} - DEPRECATED - THIS COMMAND WILL BE REMOVED in v1.0.0 - Only image sources are supported (e.g. docker: , podman: , docker-archive: , oci: , etc.), the directory source (dir:) is not supported, template outputs are not supported. - All behavior is controlled via application configuration and environment variables (see https://github.com/anchore/syft#configuration) -` - -func PowerUser(v *viper.Viper, app *config.Application, ro *options.RootOptions) *cobra.Command { - cmd := &cobra.Command{ - Use: "power-user [IMAGE]", - Short: "Run bulk operations on container images", - Example: internal.Tprintf(powerUserExample, map[string]interface{}{ - "appName": internal.ApplicationName, - "command": "power-user", - }), - Args: func(cmd *cobra.Command, args []string) error { - if err := app.LoadAllValues(v, ro.Config); err != nil { - return fmt.Errorf("invalid application config: %w", err) - } - // configure logging for command - newLogWrapper(app) - logApplicationConfig(app) - return validateArgs(cmd, args) - }, - Hidden: true, - SilenceUsage: true, - SilenceErrors: true, - RunE: func(cmd *cobra.Command, args []string) error { - if app.CheckForAppUpdate { - checkForApplicationUpdate() - // TODO: this is broke, the bus isn't available yet - } - return poweruser.Run(cmd.Context(), app, args) - }, - } - - return cmd -} diff --git a/cmd/syft/cli/poweruser/poweruser.go b/cmd/syft/cli/poweruser/poweruser.go deleted file mode 100644 index cfc10e1bc..000000000 --- a/cmd/syft/cli/poweruser/poweruser.go +++ /dev/null @@ -1,144 +0,0 @@ -package poweruser - -import ( - "context" - "fmt" - "os" - - "github.com/gookit/color" - "github.com/wagoodman/go-partybus" - - "github.com/anchore/stereoscope" - "github.com/anchore/stereoscope/pkg/image" - "github.com/anchore/syft/cmd/syft/cli/eventloop" - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/cmd/syft/cli/packages" - "github.com/anchore/syft/cmd/syft/internal/ui" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/bus" - "github.com/anchore/syft/internal/config" - "github.com/anchore/syft/internal/version" - "github.com/anchore/syft/syft" - "github.com/anchore/syft/syft/artifact" - "github.com/anchore/syft/syft/formats/syftjson" - "github.com/anchore/syft/syft/sbom" - "github.com/anchore/syft/syft/source" -) - -func Run(_ context.Context, app *config.Application, args []string) error { - f := syftjson.Format() - writer, err := options.MakeSBOMWriterForFormat(f, app.File) - if err != nil { - return err - } - defer func() { - // inform user at end of run that command will be removed - deprecated := color.Style{color.Red, color.OpBold}.Sprint("DEPRECATED: This command will be removed in v1.0.0") - fmt.Fprintln(os.Stderr, deprecated) - }() - - userInput := args[0] - - eventBus := partybus.NewBus() - stereoscope.SetBus(eventBus) - syft.SetBus(eventBus) - subscription := eventBus.Subscribe() - - return eventloop.EventLoop( - execWorker(app, userInput, writer), - eventloop.SetupSignals(), - subscription, - stereoscope.Cleanup, - ui.Select(options.IsVerbose(app), app.Quiet)..., - ) -} - -//nolint:funlen -func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error { - errs := make(chan error) - go func() { - defer close(errs) - defer bus.Exit() - - app.Secrets.Cataloger.Enabled = true - app.FileMetadata.Cataloger.Enabled = true - app.FileContents.Cataloger.Enabled = true - app.FileClassification.Cataloger.Enabled = true - tasks, err := eventloop.Tasks(app) - if err != nil { - errs <- err - return - } - - detection, err := source.Detect( - userInput, - source.DetectConfig{ - DefaultImageSource: app.DefaultImagePullSource, - }, - ) - if err != nil { - errs <- fmt.Errorf("could not deteremine source: %w", err) - return - } - - var platform *image.Platform - - if app.Platform != "" { - platform, err = image.NewPlatform(app.Platform) - if err != nil { - errs <- fmt.Errorf("invalid platform: %w", err) - return - } - } - - src, err := detection.NewSource( - source.DetectionSourceConfig{ - Alias: source.Alias{ - Name: app.Source.Name, - Version: app.Source.Version, - }, - RegistryOptions: app.Registry.ToOptions(), - Platform: platform, - Exclude: source.ExcludeConfig{ - Paths: app.Exclusions, - }, - DigestAlgorithms: nil, - BasePath: app.BasePath, - }, - ) - - if src != nil { - defer src.Close() - } - if err != nil { - errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err) - return - } - - s := sbom.SBOM{ - Source: src.Describe(), - Descriptor: sbom.Descriptor{ - Name: internal.ApplicationName, - Version: version.FromBuild().Version, - Configuration: app, - }, - } - - var relationships []<-chan artifact.Relationship - for _, task := range tasks { - c := make(chan artifact.Relationship) - relationships = append(relationships, c) - - go eventloop.RunTask(task, &s.Artifacts, src, c, errs) - } - - s.Relationships = append(s.Relationships, packages.MergeRelationships(relationships...)...) - - if err := writer.Write(s); err != nil { - errs <- fmt.Errorf("failed to write sbom: %w", err) - return - } - }() - - return errs -} diff --git a/cmd/syft/cli/version.go b/cmd/syft/cli/version.go deleted file mode 100644 index 3235a813b..000000000 --- a/cmd/syft/cli/version.go +++ /dev/null @@ -1,72 +0,0 @@ -package cli - -import ( - "encoding/json" - "fmt" - "log" - "os" - - "github.com/spf13/cobra" - "github.com/spf13/viper" - - "github.com/anchore/syft/cmd/syft/cli/options" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/config" - "github.com/anchore/syft/internal/version" -) - -func Version(v *viper.Viper, _ *config.Application) *cobra.Command { - o := &options.VersionOptions{} - cmd := &cobra.Command{ - Use: "version", - Short: "show the version", - RunE: func(cmd *cobra.Command, args []string) error { - return printVersion(o.Output) - }, - } - - err := o.AddFlags(cmd, v) - if err != nil { - log.Fatal(err) - } - - return cmd -} - -func printVersion(output string) error { - versionInfo := version.FromBuild() - - switch output { - case "text": - 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) - enc.SetEscapeHTML(false) - enc.SetIndent("", " ") - err := enc.Encode(&struct { - version.Version - Application string `json:"application"` - }{ - Version: versionInfo, - Application: internal.ApplicationName, - }) - if err != nil { - fmt.Printf("failed to show version information: %+v\n", err) - os.Exit(1) - } - default: - fmt.Printf("unsupported output format: %s\n", output) - os.Exit(1) - } - - return nil -} diff --git a/cmd/syft/internal/constants.go b/cmd/syft/internal/constants.go new file mode 100644 index 000000000..eedbdb0ee --- /dev/null +++ b/cmd/syft/internal/constants.go @@ -0,0 +1,5 @@ +package internal + +const ( + NotProvided = "[not provided]" +) diff --git a/cmd/syft/internal/ui/no_ui.go b/cmd/syft/internal/ui/no_ui.go index 015ae8218..e8ad13245 100644 --- a/cmd/syft/internal/ui/no_ui.go +++ b/cmd/syft/internal/ui/no_ui.go @@ -33,8 +33,6 @@ func (n *NoUI) Handle(e partybus.Event) error { case event.CLIReport, event.CLINotification: // keep these for when the UI is terminated to show to the screen (or perform other events) n.finalizeEvents = append(n.finalizeEvents, e) - case event.CLIExit: - return n.subscription.Unsubscribe() } return nil } diff --git a/cmd/syft/internal/ui/post_ui_event_writer.go b/cmd/syft/internal/ui/post_ui_event_writer.go index 22287d8ff..dcef5cb6c 100644 --- a/cmd/syft/internal/ui/post_ui_event_writer.go +++ b/cmd/syft/internal/ui/post_ui_event_writer.go @@ -10,7 +10,6 @@ import ( "github.com/wagoodman/go-partybus" "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/internal/version" "github.com/anchore/syft/syft/event" "github.com/anchore/syft/syft/event/parsers" ) @@ -119,13 +118,18 @@ func writeAppUpdate(writer io.Writer, events ...partybus.Event) error { style := lipgloss.NewStyle().Foreground(lipgloss.Color("13")).Italic(true) for _, e := range events { - newVersion, err := parsers.ParseCLIAppUpdateAvailable(e) + updateCheck, err := parsers.ParseCLIAppUpdateAvailable(e) if err != nil { log.WithFields("error", err).Warn("failed to parse app update notification") continue } - notice := fmt.Sprintf("A newer version of syft is available for download: %s (installed version is %s)", newVersion, version.FromBuild().Version) + if updateCheck.Current == updateCheck.New { + log.Tracef("update check event with identical versions: %s", updateCheck.Current) + continue + } + + notice := fmt.Sprintf("A newer version of syft is available for download: %s (installed version is %s)", updateCheck.New, updateCheck.Current) if _, err := fmt.Fprintln(writer, style.Render(notice)); err != nil { // don't let this be fatal diff --git a/cmd/syft/internal/ui/post_ui_event_writer_test.go b/cmd/syft/internal/ui/post_ui_event_writer_test.go index 83af4d2c5..45372e570 100644 --- a/cmd/syft/internal/ui/post_ui_event_writer_test.go +++ b/cmd/syft/internal/ui/post_ui_event_writer_test.go @@ -9,6 +9,7 @@ import ( "github.com/wagoodman/go-partybus" "github.com/anchore/syft/syft/event" + "github.com/anchore/syft/syft/event/parsers" ) func Test_postUIEventWriter_write(t *testing.T) { @@ -34,8 +35,11 @@ func Test_postUIEventWriter_write(t *testing.T) { Value: "", }, { - Type: event.CLIAppUpdateAvailable, - Value: "v0.33.0", + Type: event.CLIAppUpdateAvailable, + Value: parsers.UpdateCheck{ + New: "v0.33.0", + Current: "[not provided]", + }, }, { Type: event.CLINotification, @@ -61,8 +65,11 @@ func Test_postUIEventWriter_write(t *testing.T) { Value: "", }, { - Type: event.CLIAppUpdateAvailable, - Value: "", + Type: event.CLIAppUpdateAvailable, + Value: parsers.UpdateCheck{ + New: "", + Current: "", + }, }, { Type: event.CLIReport, diff --git a/cmd/syft/internal/ui/ui.go b/cmd/syft/internal/ui/ui.go index 441470b7c..f66454f23 100644 --- a/cmd/syft/internal/ui/ui.go +++ b/cmd/syft/internal/ui/ui.go @@ -49,31 +49,23 @@ func (m *UI) Setup(subscription partybus.Unsubscribable) error { } m.subscription = subscription - m.program = tea.NewProgram(m, tea.WithOutput(os.Stderr), tea.WithInput(os.Stdin)) + m.program = tea.NewProgram(m, tea.WithOutput(os.Stderr), tea.WithInput(os.Stdin), tea.WithoutSignalHandler()) m.running.Add(1) go func() { defer m.running.Done() if _, err := m.program.Run(); err != nil { log.Errorf("unable to start UI: %+v", err) - m.exit() + bus.ExitWithInterrupt() } }() return nil } -func (m *UI) exit() { - // stop the event loop - bus.Exit() -} - func (m *UI) Handle(e partybus.Event) error { if m.program != nil { m.program.Send(e) - if e.Type == event.CLIExit { - return m.subscription.Unsubscribe() - } } return nil } @@ -88,7 +80,9 @@ func (m *UI) Teardown(force bool) error { // string from the worker (outside of the UI after teardown). m.running.Wait() } else { - m.program.Kill() + // it may be tempting to use Kill() however it has been found that this can cause the terminal to be left in + // a bad state (where Ctrl+C and other control characters no longer works for future processes in that terminal). + m.program.Quit() } // TODO: allow for writing out the full log output to the screen (only a partial log is shown currently) @@ -107,7 +101,6 @@ func (m UI) RespondsTo() []partybus.EventType { return append([]partybus.EventType{ event.CLIReport, event.CLINotification, - event.CLIExit, event.CLIAppUpdateAvailable, }, m.handler.RespondsTo()...) } @@ -126,8 +119,10 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { + // today we treat esc and ctrl+c the same, but in the future when the syft worker has a graceful way to + // cancel in-flight work via a context, we can wire up esc to this path with bus.Exit() case "esc", "ctrl+c": - m.exit() + bus.ExitWithInterrupt() return m, tea.Quit } @@ -135,7 +130,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { log.WithFields("component", "ui").Tracef("event: %q", msg.Type) switch msg.Type { - case event.CLIReport, event.CLINotification, event.CLIExit, event.CLIAppUpdateAvailable: + case event.CLIReport, event.CLINotification, event.CLIAppUpdateAvailable: // keep these for when the UI is terminated to show to the screen (or perform other events) m.finalizeEvents = append(m.finalizeEvents, msg) diff --git a/cmd/syft/main.go b/cmd/syft/main.go index 1a4b9fb7c..c9a13881a 100644 --- a/cmd/syft/main.go +++ b/cmd/syft/main.go @@ -1,20 +1,34 @@ package main import ( - "log" - _ "modernc.org/sqlite" + "github.com/anchore/clio" "github.com/anchore/syft/cmd/syft/cli" + "github.com/anchore/syft/cmd/syft/internal" +) + +// applicationName is the non-capitalized name of the application (do not change this) +const applicationName = "syft" + +// all variables here are provided as build-time arguments, with clear default values +var ( + version = internal.NotProvided + buildDate = internal.NotProvided + gitCommit = internal.NotProvided + gitDescription = internal.NotProvided ) func main() { - cli, err := cli.New() - if err != nil { - log.Fatalf("error during command construction: %v", err) - } + app := cli.New( + clio.Identification{ + Name: applicationName, + Version: version, + BuildDate: buildDate, + GitCommit: gitCommit, + GitDescription: gitDescription, + }, + ) - if err := cli.Execute(); err != nil { - log.Fatalf("error during command execution: %v", err) - } + app.Run() } diff --git a/go.mod b/go.mod index 100b1c7fd..cb60f1ffd 100644 --- a/go.mod +++ b/go.mod @@ -8,10 +8,10 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/acobaugh/osrelease v0.1.0 - github.com/adrg/xdg v0.4.0 + github.com/adrg/xdg v0.4.0 // indirect github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 - github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0 - github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe + github.com/anchore/clio v0.0.0-20230823172630-c42d666061af + github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b @@ -61,12 +61,12 @@ require ( // pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5 github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e github.com/sergi/go-diff v1.3.1 - github.com/sirupsen/logrus v1.9.3 + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spdx/tools-golang v0.5.3 github.com/spf13/afero v1.9.5 github.com/spf13/cobra v1.7.0 - github.com/spf13/pflag v1.0.5 - github.com/spf13/viper v1.16.0 + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.16.0 // indirect github.com/stretchr/testify v1.8.4 github.com/vbatts/go-mtree v0.5.3 github.com/vifraa/gopom v1.0.0 @@ -79,7 +79,11 @@ require ( golang.org/x/mod v0.12.0 golang.org/x/net v0.14.0 golang.org/x/term v0.11.0 - gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe + github.com/iancoleman/strcase v0.3.0 gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.25.0 ) @@ -93,7 +97,6 @@ require ( github.com/Microsoft/go-winio v0.6.1 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect - github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect @@ -165,7 +168,6 @@ require ( github.com/skeema/knownhosts v1.2.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/sylabs/sif/v2 v2.11.5 // indirect github.com/sylabs/squashfs v0.6.1 // indirect diff --git a/go.sum b/go.sum index 172e79e07..341d07475 100644 --- a/go.sum +++ b/go.sum @@ -90,12 +90,12 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 h1:xGu4/uMWucwWV0YV3fpFIQZ6KVfS/Wfhmma8t0s0vRo= github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461/go.mod h1:Ger02eh5NpPm2IqkPAy396HU1KlK3BhOeCljDYXySSk= -github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0 h1:g0UqRW60JDrf5fb40RUyIwwcfQ3nAJqGj4aUCVTwFE4= -github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0/go.mod h1:0IQVIROfgRX4WZFMfgsbNZmMgLKqW/KgByyJDYvWiDE= -github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba h1:tJ186HK8e0Lf+hhNWX4fJrq14yj3mw8JQkkLhA0nFhE= -github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba/go.mod h1:E3zNHEz7mizIFGJhuX+Ga7AbCmEN5TfzVDxmOfj7XZw= -github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe h1:Df867YMmymdMG6z5IW8pR0/2CRpLIjYnaTXLp6j+s0k= -github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe/go.mod h1:ubLFmlsv8/DFUQrZwY5syT5/8Er3ugSr4rDFwHsE3hg= +github.com/anchore/clio v0.0.0-20230823172630-c42d666061af h1:dBVKZyMZeA0oZK0+aCCRoqxhxUvx/7xy/VEaLMMMnb0= +github.com/anchore/clio v0.0.0-20230823172630-c42d666061af/go.mod h1:XryJ3CIF1T7SbacQV+OPykfKKIbfXnBssYfpjy2peUg= +github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe h1:pVpLCGWdNeskAw7vGNdCAcGMezrNljHIqOc9HaOja5M= +github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe/go.mod h1:82EGoxZTfBXSW0/zollEP+Qs3wkiKmip5yBT5j+eZpY= +github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a h1:nJ2G8zWKASyVClGVgG7sfM5mwoZlZ2zYpIzN2OhjWkw= +github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a/go.mod h1:ubLFmlsv8/DFUQrZwY5syT5/8Er3ugSr4rDFwHsE3hg= github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU= github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb/go.mod h1:DmTY2Mfcv38hsHbG78xMiTDdxFtkHpgYNVDPsF2TgHk= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= @@ -406,6 +406,8 @@ github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk= github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA= github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI= +github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= @@ -1226,7 +1228,6 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/bus/helpers.go b/internal/bus/helpers.go index 5efacfb35..b2c883ebd 100644 --- a/internal/bus/helpers.go +++ b/internal/bus/helpers.go @@ -3,21 +3,24 @@ package bus import ( "github.com/wagoodman/go-partybus" - "github.com/anchore/syft/internal/log" + "github.com/anchore/clio" + "github.com/anchore/syft/internal/redact" "github.com/anchore/syft/syft/event" ) func Exit() { - Publish(partybus.Event{ - Type: event.CLIExit, - }) + Publish(clio.ExitEvent(false)) +} + +func ExitWithInterrupt() { + Publish(clio.ExitEvent(true)) } func Report(report string) { if len(report) == 0 { return } - report = log.Redactor.RedactString(report) + report = redact.Apply(report) Publish(partybus.Event{ Type: event.CLIReport, Value: report, diff --git a/internal/config/application.go b/internal/config/application.go deleted file mode 100644 index e7d6539fe..000000000 --- a/internal/config/application.go +++ /dev/null @@ -1,337 +0,0 @@ -package config - -import ( - "errors" - "fmt" - "os" - "path" - "reflect" - "sort" - "strings" - - "github.com/adrg/xdg" - "github.com/mitchellh/go-homedir" - "github.com/spf13/viper" - "gopkg.in/yaml.v2" - - "github.com/anchore/go-logger" - "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/log" - "github.com/anchore/syft/syft/pkg/cataloger" - golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang" - "github.com/anchore/syft/syft/pkg/cataloger/kernel" - pythonCataloger "github.com/anchore/syft/syft/pkg/cataloger/python" -) - -var ( - ErrApplicationConfigNotFound = fmt.Errorf("application config not found") - catalogerEnabledDefault = false -) - -type defaultValueLoader interface { - loadDefaultValues(*viper.Viper) -} - -type parser interface { - parseConfigValues() error -} - -// Application is the main syft application configuration. -type Application struct { - // the location where the application config was read from (either from -c or discovered while loading); default .syft.yaml - ConfigPath string `yaml:"configPath,omitempty" json:"configPath" mapstructure:"config"` - Verbosity uint `yaml:"verbosity,omitempty" json:"verbosity" mapstructure:"verbosity"` - // -q, indicates to not show any status output to stderr (ETUI or logging UI) - Quiet bool `yaml:"quiet" json:"quiet" mapstructure:"quiet"` - Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output - OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output - File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to - 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 - Dev development `yaml:"dev" json:"dev" mapstructure:"dev"` - Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options - Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"` - Package pkg `yaml:"package" json:"package" mapstructure:"package"` - Golang golang `yaml:"golang" json:"golang" mapstructure:"golang"` - LinuxKernel linuxKernel `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"` - Python python `yaml:"python" json:"python" mapstructure:"python"` - Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"` - 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"` - Platform string `yaml:"platform" json:"platform" mapstructure:"platform"` - Name string `yaml:"name" json:"name" mapstructure:"name"` - Source sourceCfg `yaml:"source" json:"source" mapstructure:"source"` - Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel - DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` // specify default image pull source - BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths - ExcludeBinaryOverlapByOwnership bool `yaml:"exclude-binary-overlap-by-ownership" json:"exclude-binary-overlap-by-ownership" mapstructure:"exclude-binary-overlap-by-ownership"` // exclude synthetic binary packages owned by os package files -} - -func (cfg Application) ToCatalogerConfig() cataloger.Config { - return cataloger.Config{ - Search: cataloger.SearchConfig{ - IncludeIndexedArchives: cfg.Package.SearchIndexedArchives, - IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives, - Scope: cfg.Package.Cataloger.ScopeOpt, - }, - Catalogers: cfg.Catalogers, - Parallelism: cfg.Parallelism, - ExcludeBinaryOverlapByOwnership: cfg.ExcludeBinaryOverlapByOwnership, - Golang: golangCataloger.NewGoCatalogerOpts(). - WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses). - WithLocalModCacheDir(cfg.Golang.LocalModCacheDir). - WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses). - WithProxy(cfg.Golang.Proxy). - WithNoProxy(cfg.Golang.NoProxy), - LinuxKernel: kernel.LinuxCatalogerConfig{ - CatalogModules: cfg.LinuxKernel.CatalogModules, - }, - Python: pythonCataloger.CatalogerConfig{ - GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements, - }, - } -} - -func (cfg *Application) LoadAllValues(v *viper.Viper, configPath string) error { - // priority order: viper.Set, flag, env, config, kv, defaults - // flags have already been loaded into viper by command construction - - // check if user specified config; otherwise read all possible paths - if err := loadConfig(v, configPath); err != nil { - var notFound *viper.ConfigFileNotFoundError - if errors.As(err, ¬Found) { - log.Debugf("no config file found, using defaults") - } else { - return fmt.Errorf("unable to load config: %w", err) - } - } - - // load default config values into viper - loadDefaultValues(v) - - // load environment variables - v.SetEnvPrefix(internal.ApplicationName) - v.AllowEmptyEnv(true) - v.AutomaticEnv() - - // unmarshal fully populated viper object onto config - err := v.Unmarshal(cfg) - if err != nil { - return err - } - - // Convert all populated config options to their internal application values ex: scope string => scopeOpt source.Scope - return cfg.parseConfigValues() -} - -func (cfg *Application) parseConfigValues() error { - // parse options on this struct - var catalogers []string - for _, c := range cfg.Catalogers { - for _, f := range strings.Split(c, ",") { - catalogers = append(catalogers, strings.TrimSpace(f)) - } - } - sort.Strings(catalogers) - cfg.Catalogers = catalogers - - // parse application config options - for _, optionFn := range []func() error{ - cfg.parseLogLevelOption, - cfg.parseFile, - } { - if err := optionFn(); err != nil { - return err - } - } - - if err := checkDefaultSourceValues(cfg.DefaultImagePullSource); err != nil { - return err - } - - if cfg.Name != "" { - log.Warnf("name parameter is deprecated. please use: source-name. name will be removed in a future version") - if cfg.Source.Name == "" { - cfg.Source.Name = cfg.Name - } - } - - // check for valid default source options - // parse nested config options - // for each field in the configuration struct, see if the field implements the parser interface - // note: the app config is a pointer, so we need to grab the elements explicitly (to traverse the address) - value := reflect.ValueOf(cfg).Elem() - for i := 0; i < value.NumField(); i++ { - // note: since the interface method of parser is a pointer receiver we need to get the value of the field as a pointer. - if parsable, ok := value.Field(i).Addr().Interface().(parser); ok { - // the field implements parser, call it - if err := parsable.parseConfigValues(); err != nil { - return err - } - } - } - return nil -} - -func (cfg *Application) parseLogLevelOption() error { - switch { - case cfg.Quiet: - // TODO: this is bad: quiet option trumps all other logging options (such as to a file on disk) - // we should be able to quiet the console logging and leave file logging alone... - // ... this will be an enhancement for later - cfg.Log.Level = logger.DisabledLevel - - case cfg.Verbosity > 0: - cfg.Log.Level = logger.LevelFromVerbosity(int(cfg.Verbosity), logger.WarnLevel, logger.InfoLevel, logger.DebugLevel, logger.TraceLevel) - - case cfg.Log.Level != "": - var err error - cfg.Log.Level, err = logger.LevelFromString(string(cfg.Log.Level)) - if err != nil { - return err - } - - if logger.IsVerbose(cfg.Log.Level) { - cfg.Verbosity = 1 - } - default: - cfg.Log.Level = logger.WarnLevel - } - - 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 -} - -// init loads the default configuration values into the viper instance (before the config values are read and parsed). -func loadDefaultValues(v *viper.Viper) { - // set the default values for primitive fields in this struct - v.SetDefault("quiet", false) - v.SetDefault("check-for-app-update", true) - v.SetDefault("catalogers", nil) - v.SetDefault("parallelism", 1) - v.SetDefault("default-image-pull-source", "") - v.SetDefault("exclude-binary-overlap-by-ownership", true) - - // for each field in the configuration struct, see if the field implements the defaultValueLoader interface and invoke it if it does - value := reflect.ValueOf(Application{}) - for i := 0; i < value.NumField(); i++ { - // note: the defaultValueLoader method receiver is NOT a pointer receiver. - if loadable, ok := value.Field(i).Interface().(defaultValueLoader); ok { - // the field implements defaultValueLoader, call it - loadable.loadDefaultValues(v) - } - } -} - -func (cfg Application) String() string { - // yaml is pretty human friendly (at least when compared to json) - appaStr, err := yaml.Marshal(&cfg) - - if err != nil { - return err.Error() - } - - return string(appaStr) -} - -// nolint:funlen -func loadConfig(v *viper.Viper, configPath string) error { - var err error - // use explicitly the given user config - if configPath != "" { - v.SetConfigFile(configPath) - if err := v.ReadInConfig(); err != nil { - return fmt.Errorf("unable to read application config=%q : %w", configPath, err) - } - v.Set("config", v.ConfigFileUsed()) - // don't fall through to other options if the config path was explicitly provided - return nil - } - - // start searching for valid configs in order... - // 1. look for ..yaml (in the current directory) - confFilePath := "." + internal.ApplicationName - - // TODO: Remove this before v1.0.0 - // See syft #1634 - v.AddConfigPath(".") - v.SetConfigName(confFilePath) - - // check if config.yaml exists in the current directory - // DEPRECATED: this will be removed in v1.0.0 - if _, err := os.Stat("config.yaml"); err == nil { - log.Warn("DEPRECATED: ./config.yaml as a configuration file is deprecated and will be removed as an option in v1.0.0, please rename to .syft.yaml") - } - - if _, err := os.Stat(confFilePath + ".yaml"); err == nil { - if err = v.ReadInConfig(); err == nil { - v.Set("config", v.ConfigFileUsed()) - return nil - } else if !errors.As(err, &viper.ConfigFileNotFoundError{}) { - return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err) - } - } - - // 2. look for ./config.yaml (in the current directory) - v.AddConfigPath("." + internal.ApplicationName) - v.SetConfigName("config") - if err = v.ReadInConfig(); err == nil { - v.Set("config", v.ConfigFileUsed()) - return nil - } else if !errors.As(err, &viper.ConfigFileNotFoundError{}) { - return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err) - } - - // 3. look for ~/..yaml - home, err := homedir.Dir() - if err == nil { - v.AddConfigPath(home) - v.SetConfigName("." + internal.ApplicationName) - if err = v.ReadInConfig(); err == nil { - v.Set("config", v.ConfigFileUsed()) - return nil - } else if !errors.As(err, &viper.ConfigFileNotFoundError{}) { - return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err) - } - } - - // 4. look for /config.yaml in xdg locations (starting with xdg home config dir, then moving upwards) - v.SetConfigName("config") - configPath = path.Join(xdg.ConfigHome, internal.ApplicationName) - v.AddConfigPath(configPath) - for _, dir := range xdg.ConfigDirs { - v.AddConfigPath(path.Join(dir, internal.ApplicationName)) - } - if err = v.ReadInConfig(); err == nil { - v.Set("config", v.ConfigFileUsed()) - return nil - } else if !errors.As(err, &viper.ConfigFileNotFoundError{}) { - return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err) - } - return nil -} - -var validDefaultSourceValues = []string{"registry", "docker", "podman", ""} - -func checkDefaultSourceValues(source string) error { - validValues := internal.NewStringSet(validDefaultSourceValues...) - if !validValues.Contains(source) { - validValuesString := strings.Join(validDefaultSourceValues, ", ") - return fmt.Errorf("%s is not a valid default source; please use one of the following: %s''", source, validValuesString) - } - - return nil -} diff --git a/internal/config/application_test.go b/internal/config/application_test.go deleted file mode 100644 index eac0280de..000000000 --- a/internal/config/application_test.go +++ /dev/null @@ -1,129 +0,0 @@ -package config - -import ( - "fmt" - "os" - "path" - "testing" - - "github.com/adrg/xdg" - "github.com/mitchellh/go-homedir" - "github.com/spf13/viper" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -// TODO: set negative case when config.yaml is no longer a valid option -func TestApplicationConfig(t *testing.T) { - // disable homedir package cache for testing - originalCacheOpt := homedir.DisableCache - homedir.DisableCache = true - t.Cleanup(func() { - homedir.DisableCache = originalCacheOpt - }) - - // ensure we have no side effects for xdg package for future tests - originalXDG := os.Getenv("XDG_CONFIG_HOME") - t.Cleanup(func() { - // note: we're not using t.Setenv since the effect we're trying to eliminate is within the xdg package - require.NoError(t, os.Setenv("XDG_CONFIG_HOME", originalXDG)) - xdg.Reload() - }) - - // config is picked up at desired configuration paths - // VALID: .syft.yaml, .syft/config.yaml, ~/.syft.yaml, /syft/config.yaml - // DEPRECATED: config.yaml is currently supported by - tests := []struct { - name string - setup func(t *testing.T) string - assertions func(t *testing.T, app *Application) - cleanup func() - }{ - { - name: "explicit config", - setup: func(t *testing.T) string { - return "./test-fixtures/.syft.yaml" - }, // no-op for explicit config - assertions: func(t *testing.T, app *Application) { - assert.Equal(t, "test-explicit-config", app.File) - }, - }, - { - name: "current working directory named config", - setup: func(t *testing.T) string { - err := os.Chdir("./test-fixtures/config-wd-file") // change application cwd to test-fixtures - require.NoError(t, err) - return "" - }, - assertions: func(t *testing.T, app *Application) { - assert.Equal(t, "test-wd-named-config", app.File) - }, - }, - { - name: "current working directory syft dir config", - setup: func(t *testing.T) string { - err := os.Chdir("./test-fixtures/config-dir-test") // change application cwd to test-fixtures - require.NoError(t, err) - return "" - }, - assertions: func(t *testing.T, app *Application) { - assert.Equal(t, "test-dir-config", app.File) - }, - }, - { - name: "home directory file config", - setup: func(t *testing.T) string { - // Because Setenv affects the whole process, it cannot be used in parallel tests or - // tests with parallel ancestors: see separate XDG test for consequence of this - t.Setenv("HOME", "./test-fixtures/config-home-test/config-file") - return "" - }, - assertions: func(t *testing.T, app *Application) { - assert.Equal(t, "test-home-config", app.File) - }, - }, - { - name: "XDG file config", - setup: func(t *testing.T) string { - wd, err := os.Getwd() - require.NoError(t, err) - configDir := path.Join(wd, "./test-fixtures/config-home-test") // set HOME to testdata - // note: this explicitly has multiple XDG paths, make certain we use the first VALID one (not the first one) - t.Setenv("XDG_CONFIG_DIRS", fmt.Sprintf("/another/foo/bar:%s", configDir)) - xdg.Reload() - return "" - }, - assertions: func(t *testing.T, app *Application) { - assert.Equal(t, "test-home-XDG-config", app.File) - }, - cleanup: func() { - require.NoError(t, os.Unsetenv("XDG_CONFIG_DIRS")) - xdg.Reload() - }, - }, - } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - if test.cleanup != nil { - t.Cleanup(test.cleanup) - } - wd, err := os.Getwd() - require.NoError(t, err) - - defer os.Chdir(wd) // reset working directory after test - application := &Application{} - viperInstance := viper.New() - - // this will override home in case you are running this test locally and DO have a syft config - // in your home directory... now it will be ignored. Same for XDG_CONFIG_DIRS. - t.Setenv("HOME", "/foo/bar") - t.Setenv("XDG_CONFIG_DIRS", "/foo/bar") - xdg.Reload() - - configPath := test.setup(t) - err = application.LoadAllValues(viperInstance, configPath) - require.NoError(t, err) - test.assertions(t, application) - }) - } -} diff --git a/internal/config/attest.go b/internal/config/attest.go deleted file mode 100644 index 659c7d3ed..000000000 --- a/internal/config/attest.go +++ /dev/null @@ -1,14 +0,0 @@ -package config - -import "github.com/spf13/viper" - -type attest struct { - // IMPORTANT: do not show the attestation key/password in any YAML/JSON output (sensitive information) - Key string `yaml:"-" json:"-" mapstructure:"key"` - Password string `yaml:"-" json:"-" mapstructure:"password"` -} - -func (cfg attest) loadDefaultValues(v *viper.Viper) { - v.SetDefault("attest.key", "") - v.SetDefault("attest.password", "") -} diff --git a/internal/config/cataloger_options.go b/internal/config/cataloger_options.go deleted file mode 100644 index 1fb2992b4..000000000 --- a/internal/config/cataloger_options.go +++ /dev/null @@ -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 -} diff --git a/internal/config/development.go b/internal/config/development.go deleted file mode 100644 index 4e1e8b01a..000000000 --- a/internal/config/development.go +++ /dev/null @@ -1,13 +0,0 @@ -package config - -import "github.com/spf13/viper" - -type development struct { - ProfileCPU bool `yaml:"profile-cpu" json:"profile-cpu" mapstructure:"profile-cpu"` - ProfileMem bool `yaml:"profile-mem" json:"profile-mem" mapstructure:"profile-mem"` -} - -func (cfg development) loadDefaultValues(v *viper.Viper) { - v.SetDefault("dev.profile-cpu", false) - v.SetDefault("dev.profile-mem", false) -} diff --git a/internal/config/file_classification.go b/internal/config/file_classification.go deleted file mode 100644 index d78812705..000000000 --- a/internal/config/file_classification.go +++ /dev/null @@ -1,20 +0,0 @@ -package config - -import ( - "github.com/spf13/viper" - - "github.com/anchore/syft/syft/source" -) - -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() -} diff --git a/internal/config/file_contents.go b/internal/config/file_contents.go deleted file mode 100644 index 18a404f5b..000000000 --- a/internal/config/file_contents.go +++ /dev/null @@ -1,25 +0,0 @@ -package config - -import ( - "github.com/spf13/viper" - - "github.com/anchore/syft/internal/file" - "github.com/anchore/syft/syft/source" -) - -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"` -} - -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() -} diff --git a/internal/config/file_metadata.go b/internal/config/file_metadata.go deleted file mode 100644 index 5339f29d7..000000000 --- a/internal/config/file_metadata.go +++ /dev/null @@ -1,22 +0,0 @@ -package config - -import ( - "github.com/spf13/viper" - - "github.com/anchore/syft/syft/source" -) - -type FileMetadata struct { - Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"` - 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) - v.SetDefault("file-metadata.digests", []string{"sha256"}) -} - -func (cfg *FileMetadata) parseConfigValues() error { - return cfg.Cataloger.parseConfigValues() -} diff --git a/internal/config/linux_kernel.go b/internal/config/linux_kernel.go deleted file mode 100644 index 1d0b38e96..000000000 --- a/internal/config/linux_kernel.go +++ /dev/null @@ -1,11 +0,0 @@ -package config - -import "github.com/spf13/viper" - -type linuxKernel struct { - CatalogModules bool `json:"catalog-modules" yaml:"catalog-modules" mapstructure:"catalog-modules"` -} - -func (cfg linuxKernel) loadDefaultValues(v *viper.Viper) { - v.SetDefault("linux-kernel.catalog-modules", true) -} diff --git a/internal/config/logging.go b/internal/config/logging.go deleted file mode 100644 index 8c0b43c93..000000000 --- a/internal/config/logging.go +++ /dev/null @@ -1,34 +0,0 @@ -package config - -import ( - "fmt" - - "github.com/mitchellh/go-homedir" - "github.com/spf13/viper" - - "github.com/anchore/go-logger" -) - -// logging contains all logging-related configuration options available to the user via the application config. -type logging struct { - Structured bool `yaml:"structured" json:"structured" mapstructure:"structured"` // show all log entries as JSON formatted strings - Level logger.Level `yaml:"level" json:"level" mapstructure:"level"` // the log level string hint - 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", "") - v.SetDefault("log.level", string(logger.WarnLevel)) -} diff --git a/internal/config/pkg.go b/internal/config/pkg.go deleted file mode 100644 index 21e26a59b..000000000 --- a/internal/config/pkg.go +++ /dev/null @@ -1,24 +0,0 @@ -package config - -import ( - "github.com/spf13/viper" - - "github.com/anchore/syft/syft/pkg/cataloger" -) - -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"` -} - -func (cfg pkg) loadDefaultValues(v *viper.Viper) { - cfg.Cataloger.loadDefaultValues(v) - c := cataloger.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() -} diff --git a/internal/config/test-fixtures/.syft.yaml b/internal/config/test-fixtures/.syft.yaml deleted file mode 100644 index 9da17f95b..000000000 --- a/internal/config/test-fixtures/.syft.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# same as --file; write output report to a file (default is to write to stdout) -file: "test-explicit-config" -package: - cataloger: - scope: "squashed" - - # same as --scope; limit the scope of the cataloger to only the specified types \ No newline at end of file diff --git a/internal/config/test-fixtures/config-dir-test/.syft/config.yaml b/internal/config/test-fixtures/config-dir-test/.syft/config.yaml deleted file mode 100644 index 0122d78dc..000000000 --- a/internal/config/test-fixtures/config-dir-test/.syft/config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# same as --file; write output report to a file (default is to write to stdout) -file: "test-dir-config" -package: - cataloger: - scope: "squashed" \ No newline at end of file diff --git a/internal/config/test-fixtures/config-home-test/config-file/.syft.yaml b/internal/config/test-fixtures/config-home-test/config-file/.syft.yaml deleted file mode 100644 index 04b886ba5..000000000 --- a/internal/config/test-fixtures/config-home-test/config-file/.syft.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# same as --file; write output report to a file (default is to write to stdout) -file: "test-home-config" -package: - cataloger: - scope: "squashed" \ No newline at end of file diff --git a/internal/config/test-fixtures/config-home-test/syft/config.yaml b/internal/config/test-fixtures/config-home-test/syft/config.yaml deleted file mode 100644 index 3c64be119..000000000 --- a/internal/config/test-fixtures/config-home-test/syft/config.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# same as --file; write output report to a file (default is to write to stdout) -file: "test-home-XDG-config" -package: - cataloger: - scope: "squashed" \ No newline at end of file diff --git a/internal/config/test-fixtures/config-wd-file/.syft.yaml b/internal/config/test-fixtures/config-wd-file/.syft.yaml deleted file mode 100644 index 6971cbee5..000000000 --- a/internal/config/test-fixtures/config-wd-file/.syft.yaml +++ /dev/null @@ -1,5 +0,0 @@ -# same as --file; write output report to a file (default is to write to stdout) -file: "test-wd-named-config" -package: - cataloger: - scope: "squashed" \ No newline at end of file diff --git a/internal/constants.go b/internal/constants.go index 259b2d77c..6903ef00d 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -1,9 +1,6 @@ package internal const ( - // ApplicationName is the non-capitalized name of the application (do not change this) - ApplicationName = "syft" - // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. JSONSchemaVersion = "10.0.1" diff --git a/internal/log/log.go b/internal/log/log.go index 242c5f0b7..5c557ee0d 100644 --- a/internal/log/log.go +++ b/internal/log/log.go @@ -7,29 +7,28 @@ import ( "github.com/anchore/go-logger" "github.com/anchore/go-logger/adapter/discard" "github.com/anchore/go-logger/adapter/redact" + red "github.com/anchore/syft/internal/redact" ) -var ( - // log is the singleton used to facilitate logging internally within - log = discard.New() - - store = redact.NewStore() - - Redactor = store.(redact.Redactor) -) +// log is the singleton used to facilitate logging internally within +var log = discard.New() func Set(l logger.Logger) { - log = redact.New(l, store) + // though the application will automatically have a redaction logger, library consumers may not be doing this. + // for this reason we additionally ensure there is a redaction logger configured for any logger passed. The + // source of truth for redaction values is still in the internal redact package. If the passed logger is already + // redacted, then this is a no-op. + store := red.Get() + if store != nil { + l = redact.New(l, store) + } + log = l } func Get() logger.Logger { return log } -func Redact(values ...string) { - store.Add(values...) -} - // Errorf takes a formatted template string and template arguments for the error logging level. func Errorf(format string, args ...interface{}) { log.Errorf(format, args...) diff --git a/internal/redact/redact.go b/internal/redact/redact.go new file mode 100644 index 000000000..3bb76e269 --- /dev/null +++ b/internal/redact/redact.go @@ -0,0 +1,36 @@ +package redact + +import "github.com/anchore/go-logger/adapter/redact" + +var store redact.Store + +func Set(s redact.Store) { + if store != nil { + // if someone is trying to set a redaction store and we already have one then something is wrong. The store + // that we're replacing might already have values in it, so we should never replace it. + panic("replace existing redaction store (probably unintentional)") + } + store = s +} + +func Get() redact.Store { + return store +} + +func Add(vs ...string) { + if store == nil { + // if someone is trying to add values that should never be output and we don't have a store then something is wrong. + // we should never accidentally output values that should be redacted, thus we panic here. + panic("cannot add redactions without a store") + } + store.Add(vs...) +} + +func Apply(value string) string { + if store == nil { + // if someone is trying to add values that should never be output and we don't have a store then something is wrong. + // we should never accidentally output values that should be redacted, thus we panic here. + panic("cannot apply redactions without a store") + } + return store.RedactString(value) +} diff --git a/internal/version/build.go b/internal/version/build.go deleted file mode 100644 index a0d5fc1d2..000000000 --- a/internal/version/build.go +++ /dev/null @@ -1,54 +0,0 @@ -/* -Package version contains all build time metadata (version, build time, git commit, etc). -*/ -package version - -import ( - "fmt" - "runtime" - "strings" - - "github.com/anchore/syft/internal" -) - -const valueNotProvided = "[not provided]" - -// all variables here are provided as build-time arguments, with clear default values -var version = valueNotProvided -var gitCommit = valueNotProvided -var gitDescription = valueNotProvided -var buildDate = valueNotProvided -var platform = fmt.Sprintf("%s/%s", runtime.GOOS, runtime.GOARCH) - -// Version defines the application version details (generally from build information) -type Version struct { - Version string `json:"version"` // application semantic version - JSONSchemaVersion string `json:"jsonSchemaVersion"` // application semantic JSON schema version - GitCommit string `json:"gitCommit"` // git SHA at build-time - GitDescription string `json:"gitDescription"` // output of 'git describe --dirty --always --tags' - BuildDate string `json:"buildDate"` // date of the build - GoVersion string `json:"goVersion"` // go runtime version at build-time - Compiler string `json:"compiler"` // compiler used at build-time - Platform string `json:"platform"` // GOOS and GOARCH at build-time -} - -func (v Version) IsProductionBuild() bool { - if strings.Contains(v.Version, "SNAPSHOT") || strings.Contains(v.Version, valueNotProvided) { - return false - } - return true -} - -// FromBuild provides all version details -func FromBuild() Version { - return Version{ - Version: version, - JSONSchemaVersion: internal.JSONSchemaVersion, - GitCommit: gitCommit, - GitDescription: gitDescription, - BuildDate: buildDate, - GoVersion: runtime.Version(), - Compiler: runtime.Compiler, - Platform: platform, - } -} diff --git a/internal/version/update.go b/internal/version/update.go deleted file mode 100644 index 00141d142..000000000 --- a/internal/version/update.go +++ /dev/null @@ -1,73 +0,0 @@ -package version - -import ( - "fmt" - "io" - "net/http" - "strings" - - hashiVersion "github.com/anchore/go-version" - "github.com/anchore/syft/internal" -) - -var latestAppVersionURL = struct { - host string - path string -}{ - host: "https://toolbox-data.anchore.io", - path: fmt.Sprintf("/%s/releases/latest/VERSION", internal.ApplicationName), -} - -// IsUpdateAvailable indicates if there is a newer application version available, and if so, what the new version is. -func IsUpdateAvailable() (bool, string, error) { - currentBuildInfo := FromBuild() - if !currentBuildInfo.IsProductionBuild() { - // don't allow for non-production builds to check for a version. - return false, "", nil - } - currentVersion, err := hashiVersion.NewVersion(currentBuildInfo.Version) - if err != nil { - return false, "", fmt.Errorf("failed to parse current application version: %w", err) - } - - latestVersion, err := fetchLatestApplicationVersion() - if err != nil { - return false, "", err - } - - if latestVersion.GreaterThan(currentVersion) { - return true, latestVersion.String(), nil - } - - return false, "", nil -} - -func fetchLatestApplicationVersion() (*hashiVersion.Version, error) { - req, err := http.NewRequest(http.MethodGet, latestAppVersionURL.host+latestAppVersionURL.path, nil) - if err != nil { - return nil, fmt.Errorf("failed to create request for latest version: %w", err) - } - - client := http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("failed to fetch latest version: %w", err) - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("HTTP %d on fetching latest version: %s", resp.StatusCode, resp.Status) - } - - versionBytes, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("failed to read latest version: %w", err) - } - - versionStr := strings.TrimSuffix(string(versionBytes), "\n") - if len(versionStr) > 50 { - return nil, fmt.Errorf("version too long: %q", versionStr[:50]) - } - - return hashiVersion.NewVersion(versionStr) -} diff --git a/syft/event/event.go b/syft/event/event.go index 4c2a29296..c095e9eef 100644 --- a/syft/event/event.go +++ b/syft/event/event.go @@ -6,12 +6,10 @@ package event import ( "github.com/wagoodman/go-partybus" - - "github.com/anchore/syft/internal" ) const ( - typePrefix = internal.ApplicationName + typePrefix = "syft" cliTypePrefix = typePrefix + "-cli" // Events from the syft library @@ -48,7 +46,4 @@ const ( // CLINotification is a partybus event that occurs when auxiliary information is ready for presentation to stderr CLINotification partybus.EventType = cliTypePrefix + "-notification" - - // CLIExit is a partybus event that occurs when an analysis result is ready for final presentation - CLIExit partybus.EventType = cliTypePrefix + "-exit-event" ) diff --git a/syft/event/parsers/parsers.go b/syft/event/parsers/parsers.go index 2e6cbeca9..c2a56e17c 100644 --- a/syft/event/parsers/parsers.go +++ b/syft/event/parsers/parsers.go @@ -144,30 +144,22 @@ func ParseAttestationStartedEvent(e partybus.Event) (io.Reader, progress.Progres // CLI event types -func ParseCLIExit(e partybus.Event) (func() error, error) { - if err := checkEventType(e.Type, event.CLIExit); err != nil { +type UpdateCheck struct { + New string + Current string +} + +func ParseCLIAppUpdateAvailable(e partybus.Event) (*UpdateCheck, error) { + if err := checkEventType(e.Type, event.CLIAppUpdateAvailable); err != nil { return nil, err } - fn, ok := e.Value.(func() error) + updateCheck, ok := e.Value.(UpdateCheck) if !ok { return nil, newPayloadErr(e.Type, "Value", e.Value) } - return fn, nil -} - -func ParseCLIAppUpdateAvailable(e partybus.Event) (string, error) { - if err := checkEventType(e.Type, event.CLIAppUpdateAvailable); err != nil { - return "", err - } - - newVersion, ok := e.Value.(string) - if !ok { - return "", newPayloadErr(e.Type, "Value", e.Value) - } - - return newVersion, nil + return &updateCheck, nil } func ParseCLIReport(e partybus.Event) (string, string, error) { diff --git a/syft/formats/common/cyclonedxhelpers/format.go b/syft/formats/common/cyclonedxhelpers/format.go index e36149e12..1712e0f49 100644 --- a/syft/formats/common/cyclonedxhelpers/format.go +++ b/syft/formats/common/cyclonedxhelpers/format.go @@ -7,7 +7,6 @@ import ( "github.com/google/uuid" "golang.org/x/exp/slices" - "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" @@ -24,7 +23,7 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM { // https://github.com/CycloneDX/specification/blob/master/schema/bom-1.3-strict.schema.json#L36 // "pattern": "^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" cdxBOM.SerialNumber = uuid.New().URN() - cdxBOM.Metadata = toBomDescriptor(internal.ApplicationName, s.Descriptor.Version, s.Source) + cdxBOM.Metadata = toBomDescriptor(s.Descriptor.Name, s.Descriptor.Version, s.Source) packages := s.Artifacts.Packages.Sorted() components := make([]cyclonedx.Component, len(packages)) diff --git a/syft/formats/common/spdxhelpers/document_namespace.go b/syft/formats/common/spdxhelpers/document_namespace.go index 3b6d30b69..74801653d 100644 --- a/syft/formats/common/spdxhelpers/document_namespace.go +++ b/syft/formats/common/spdxhelpers/document_namespace.go @@ -8,7 +8,7 @@ import ( "github.com/google/uuid" - "github.com/anchore/syft/internal" + "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" ) @@ -18,12 +18,12 @@ const ( inputFile = "file" ) -func DocumentNameAndNamespace(src source.Description) (string, string) { +func DocumentNameAndNamespace(src source.Description, desc sbom.Descriptor) (string, string) { name := DocumentName(src) - return name, DocumentNamespace(name, src) + return name, DocumentNamespace(name, src, desc) } -func DocumentNamespace(name string, src source.Description) string { +func DocumentNamespace(name string, src source.Description, desc sbom.Descriptor) string { name = cleanName(name) input := "unknown-source-type" switch src.Metadata.(type) { @@ -44,7 +44,7 @@ func DocumentNamespace(name string, src source.Description) string { u := url.URL{ Scheme: "https", Host: "anchore.com", - Path: path.Join(internal.ApplicationName, identifier), + Path: path.Join(desc.Name, identifier), } return u.String() diff --git a/syft/formats/common/spdxhelpers/document_namespace_test.go b/syft/formats/common/spdxhelpers/document_namespace_test.go index 00bed3536..9fa2ccb33 100644 --- a/syft/formats/common/spdxhelpers/document_namespace_test.go +++ b/syft/formats/common/spdxhelpers/document_namespace_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/anchore/syft/syft/internal/sourcemetadata" + "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" ) @@ -55,9 +56,12 @@ func Test_documentNamespace(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - actual := DocumentNamespace(test.inputName, test.src) + actual := DocumentNamespace(test.inputName, test.src, sbom.Descriptor{ + Name: "syft", + }) + // note: since the namespace ends with a UUID we check the prefix - assert.True(t, strings.HasPrefix(actual, test.expected), fmt.Sprintf("actual namespace %q", actual)) + assert.True(t, strings.HasPrefix(actual, test.expected), fmt.Sprintf("expected prefix: '%s' got: '%s'", test.expected, actual)) // track each scheme tested (passed or not) tracker.Tested(t, test.src.Metadata) diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index 258c96f8f..e15d78d4f 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -44,7 +44,7 @@ const ( // //nolint:funlen func ToFormatModel(s sbom.SBOM) *spdx.Document { - name, namespace := DocumentNameAndNamespace(s.Source) + name, namespace := DocumentNameAndNamespace(s.Source, s.Descriptor) packages := toPackages(s.Artifacts.Packages, s) @@ -136,7 +136,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document { CreatorType: "Organization", }, { - Creator: internal.ApplicationName + "-" + s.Descriptor.Version, + Creator: s.Descriptor.Name + "-" + s.Descriptor.Version, CreatorType: "Tool", }, }, diff --git a/syft/formats/github/encoder.go b/syft/formats/github/encoder.go index 261ff6b18..6f8ff5719 100644 --- a/syft/formats/github/encoder.go +++ b/syft/formats/github/encoder.go @@ -8,7 +8,6 @@ import ( "github.com/mholt/archiver/v3" "github.com/anchore/packageurl-go" - "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" @@ -26,7 +25,7 @@ func toGithubModel(s *sbom.SBOM) DependencySnapshot { Version: 0, // TODO allow property input to specify the Job, Sha, and Ref Detector: DetectorMetadata{ - Name: internal.ApplicationName, + Name: s.Descriptor.Name, URL: "https://github.com/anchore/syft", Version: v, }, diff --git a/syft/formats/github/encoder_test.go b/syft/formats/github/encoder_test.go index 3325509ce..e027c7936 100644 --- a/syft/formats/github/encoder_test.go +++ b/syft/formats/github/encoder_test.go @@ -18,6 +18,9 @@ import ( func sbomFixture() sbom.SBOM { s := sbom.SBOM{ + Descriptor: sbom.Descriptor{ + Name: "syft", + }, Source: source.Description{ Metadata: source.StereoscopeImageSourceMetadata{ UserInput: "ubuntu:18.04", diff --git a/syft/formats/internal/testutils/snapshot.go b/syft/formats/internal/testutils/snapshot.go index 7eae36594..f06850b1d 100644 --- a/syft/formats/internal/testutils/snapshot.go +++ b/syft/formats/internal/testutils/snapshot.go @@ -4,7 +4,7 @@ import ( "bytes" "testing" - "github.com/sergi/go-diff/diffmatchpatch" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -64,12 +64,16 @@ func AssertEncoderAgainstGoldenSnapshot(t *testing.T, cfg EncoderSnapshotTestCon if cfg.IsJSON { require.JSONEq(t, string(expected), string(actual)) - } else if !bytes.Equal(expected, actual) { - dmp := diffmatchpatch.New() - diffs := dmp.DiffMain(string(expected), string(actual), true) - t.Logf("len: %d\nexpected: %s", len(expected), expected) - t.Logf("len: %d\nactual: %s", len(actual), actual) - t.Errorf("mismatched output:\n%s", dmp.DiffPrettyText(diffs)) + } else { + requireEqual(t, expected, actual) + } +} + +func requireEqual(t *testing.T, expected any, actual any) { + if diff := cmp.Diff(expected, actual); diff != "" { + t.Logf("expected: %s", expected) + t.Logf("actual: %s", actual) + t.Fatalf("mismatched output: %s", diff) } } diff --git a/syft/formats/spdxtagvalue/encoder_test.go b/syft/formats/spdxtagvalue/encoder_test.go index baafe2b02..e5fe4a5af 100644 --- a/syft/formats/spdxtagvalue/encoder_test.go +++ b/syft/formats/spdxtagvalue/encoder_test.go @@ -115,7 +115,7 @@ func redactor(values ...string) testutils.Redactor { `Created: .*`: "Created: redacted", // each SBOM reports a unique documentNamespace when generated, this is not useful for snapshot testing - `DocumentNamespace: https://anchore.com/syft/.*`: "DocumentNamespace: redacted", + `DocumentNamespace: https://anchore.com/.*`: "DocumentNamespace: redacted", // the license list will be updated periodically, the value here should not be directly tested in snapshot tests `LicenseListVersion: .*`: "LicenseListVersion: redacted", diff --git a/syft/pkg/cataloger/rpm/parse_rpm_db.go b/syft/pkg/cataloger/rpm/parse_rpm_db.go index 02106f62c..1dee8c0f8 100644 --- a/syft/pkg/cataloger/rpm/parse_rpm_db.go +++ b/syft/pkg/cataloger/rpm/parse_rpm_db.go @@ -7,7 +7,6 @@ import ( rpmdb "github.com/knqyf263/go-rpmdb/pkg" - "github.com/anchore/syft/internal" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" @@ -18,7 +17,7 @@ import ( // parseRpmDb parses an "Packages" RPM DB and returns the Packages listed within it. func parseRpmDB(resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { - f, err := os.CreateTemp("", internal.ApplicationName+"-rpmdb") + f, err := os.CreateTemp("", "rpmdb") if err != nil { return nil, nil, fmt.Errorf("failed to create temp rpmdb file: %w", err) } diff --git a/test/cli/packages_cmd_test.go b/test/cli/packages_cmd_test.go index e9b87ae48..52efac920 100644 --- a/test/cli/packages_cmd_test.go +++ b/test/cli/packages_cmd_test.go @@ -153,7 +153,7 @@ func TestPackagesCmdFlags(t *testing.T) { }, { name: "responds-to-package-cataloger-search-options", - args: []string{"packages", "-vv"}, + args: []string{"--help"}, env: map[string]string{ "SYFT_PACKAGE_SEARCH_UNINDEXED_ARCHIVES": "true", "SYFT_PACKAGE_SEARCH_INDEXED_ARCHIVES": "false", @@ -294,7 +294,7 @@ func TestRegistryAuth(t *testing.T) { args: args, env: map[string]string{ "SYFT_REGISTRY_AUTH_AUTHORITY": host, - "SYFT_REGISTRY_AUTH_TOKEN": "token", + "SYFT_REGISTRY_AUTH_TOKEN": "my-token", }, assertions: []traitAssertion{ assertInOutput("source=OciRegistry"), @@ -303,7 +303,7 @@ func TestRegistryAuth(t *testing.T) { }, }, { - name: "not enough info fallsback to keychain", + name: "not enough info fallback to keychain", args: args, env: map[string]string{ "SYFT_REGISTRY_AUTH_AUTHORITY": host, diff --git a/test/cli/trait_assertions_test.go b/test/cli/trait_assertions_test.go index 465c389d2..d441d021b 100644 --- a/test/cli/trait_assertions_test.go +++ b/test/cli/trait_assertions_test.go @@ -4,8 +4,6 @@ import ( "encoding/json" "fmt" "os" - "os/exec" - "path/filepath" "regexp" "sort" "strings" @@ -88,8 +86,10 @@ func assertNotInOutput(data string) traitAssertion { func assertInOutput(data string) traitAssertion { return func(tb testing.TB, stdout, stderr string, _ int) { tb.Helper() - if !strings.Contains(stripansi.Strip(stderr), data) && !strings.Contains(stripansi.Strip(stdout), data) { - tb.Errorf("data=%q was NOT found in any output, but should have been there", data) + stdout = stripansi.Strip(stdout) + stderr = stripansi.Strip(stderr) + if !strings.Contains(stdout, data) && !strings.Contains(stderr, data) { + tb.Errorf("data=%q was NOT found in any output, but should have been there\nSTDOUT:%s\nSTDERR:%s", data, stdout, stderr) } } } @@ -148,46 +148,6 @@ func assertSuccessfulReturnCode(tb testing.TB, _, _ string, rc int) { } } -func assertVerifyAttestation(coverageImage string) traitAssertion { - return func(tb testing.TB, stdout, _ string, _ int) { - tb.Helper() - cosignPath := filepath.Join(repoRoot(tb), ".tmp/cosign") - err := os.WriteFile("attestation.json", []byte(stdout), 0664) - if err != nil { - tb.Errorf("could not write attestation to disk") - } - defer os.Remove("attestation.json") - attachCmd := exec.Command( - cosignPath, - "attach", - "attestation", - "--attestation", - "attestation.json", - coverageImage, // TODO which remote image to use? - ) - - stdout, stderr, _ := runCommand(attachCmd, nil) - if attachCmd.ProcessState.ExitCode() != 0 { - tb.Log("STDOUT", stdout) - tb.Log("STDERR", stderr) - tb.Fatalf("could not attach image") - } - - verifyCmd := exec.Command( - cosignPath, - "verify-attestation", - coverageImage, // TODO which remote image to use? - ) - - stdout, stderr, _ = runCommand(verifyCmd, nil) - if attachCmd.ProcessState.ExitCode() != 0 { - tb.Log("STDOUT", stdout) - tb.Log("STDERR", stderr) - tb.Fatalf("could not verify attestation") - } - } -} - func assertFileExists(file string) traitAssertion { return func(tb testing.TB, _, _ string, _ int) { tb.Helper() diff --git a/test/integration/convert_test.go b/test/integration/convert_test.go index d20cab27b..f1c0eb326 100644 --- a/test/integration/convert_test.go +++ b/test/integration/convert_test.go @@ -1,15 +1,14 @@ package integration import ( - "context" "fmt" "os" "testing" "github.com/stretchr/testify/require" - "github.com/anchore/syft/cmd/syft/cli/convert" - "github.com/anchore/syft/internal/config" + "github.com/anchore/syft/cmd/syft/cli/commands" + "github.com/anchore/syft/cmd/syft/cli/options" "github.com/anchore/syft/syft/formats" "github.com/anchore/syft/syft/formats/cyclonedxjson" "github.com/anchore/syft/syft/formats/cyclonedxxml" @@ -72,9 +71,10 @@ func TestConvertCmd(t *testing.T) { _ = os.Remove(syftFile.Name()) }() - ctx := context.Background() - app := &config.Application{ - Outputs: []string{fmt.Sprintf("%s=%s", test.format.ID().String(), formatFile.Name())}, + opts := &commands.ConvertOptions{ + MultiOutput: options.MultiOutput{ + Outputs: []string{fmt.Sprintf("%s=%s", test.format.ID().String(), formatFile.Name())}, + }, } // stdout reduction of test noise @@ -84,7 +84,7 @@ func TestConvertCmd(t *testing.T) { os.Stdout = rescue }() - err = convert.Run(ctx, app, []string{syftFile.Name()}) + err = commands.RunConvert(opts, syftFile.Name()) require.NoError(t, err) contents, err := os.ReadFile(formatFile.Name()) require.NoError(t, err) From cfebae27f5216d042b87b7508fcf39086c6b0338 Mon Sep 17 00:00:00 2001 From: William Murphy Date: Wed, 30 Aug 2023 08:37:50 -0400 Subject: [PATCH 028/106] fix: don't panic on universal go binaries (#2078) If crypto settings or arch cannot be determined, still attempt to catalog packages from the build info, rather than panicking. Signed-off-by: Will Murphy --- syft/pkg/cataloger/golang/parse_go_binary.go | 43 +-------- .../cataloger/golang/parse_go_binary_test.go | 93 ++++++++++--------- syft/pkg/cataloger/golang/scan_binary.go | 25 +++-- 3 files changed, 70 insertions(+), 91 deletions(-) diff --git a/syft/pkg/cataloger/golang/parse_go_binary.go b/syft/pkg/cataloger/golang/parse_go_binary.go index 6da185839..5286270e1 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary.go +++ b/syft/pkg/cataloger/golang/parse_go_binary.go @@ -16,7 +16,6 @@ import ( "golang.org/x/mod/module" "github.com/anchore/syft/internal" - "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -57,11 +56,11 @@ func (c *goBinaryCataloger) parseGoBinary(resolver file.Resolver, _ *generic.Env return nil, nil, err } - mods, archs := scanFile(unionReader, reader.RealPath) + mods := scanFile(unionReader, reader.RealPath) internal.CloseAndLogError(reader.ReadCloser, reader.RealPath) - for i, mod := range mods { - pkgs = append(pkgs, c.buildGoPkgInfo(resolver, reader.Location, mod, archs[i])...) + for _, mod := range mods { + pkgs = append(pkgs, c.buildGoPkgInfo(resolver, reader.Location, mod, mod.arch)...) } return pkgs, nil, nil } @@ -151,42 +150,6 @@ func extractVersionFromLDFlags(ldflags string) (majorVersion string, fullVersion return "", "" } -// getArchs finds a binary architecture by two ways: -// 1) reading build info from binaries compiled by go1.18+ -// 2) reading file headers from binaries compiled by < go1.18 -func getArchs(readers []io.ReaderAt, builds []*extendedBuildInfo) []string { - if len(readers) != len(builds) { - log.Trace("golang cataloger: bin parsing: number of builds and readers doesn't match") - return nil - } - - if len(readers) == 0 || len(builds) == 0 { - log.Tracef("golang cataloger: bin parsing: %d readers and %d build info items", len(readers), len(builds)) - return nil - } - - archs := make([]string, len(builds)) - for i, build := range builds { - archs[i] = getGOARCH(build.Settings) - } - - // if architecture was found via build settings return - if archs[0] != "" { - return archs - } - - for i, r := range readers { - a, err := getGOARCHFromBin(r) - if err != nil { - log.Tracef("golang cataloger: bin parsing: getting arch from binary: %v", err) - continue - } - - archs[i] = a - } - return archs -} - func getGOARCH(settings []debug.BuildSetting) string { for _, s := range settings { if s.Key == GOARCH { diff --git a/syft/pkg/cataloger/golang/parse_go_binary_test.go b/syft/pkg/cataloger/golang/parse_go_binary_test.go index 901444564..6b19a5465 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary_test.go +++ b/syft/pkg/cataloger/golang/parse_go_binary_test.go @@ -156,18 +156,12 @@ func TestBuildGoPkgInfo(t *testing.T) { tests := []struct { name string mod *extendedBuildInfo - arch string expected []pkg.Package }{ - { - name: "parse an empty mod", - mod: nil, - expected: []pkg.Package(nil), - }, { name: "package without name", mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ Deps: []*debug.Module{ { Path: "github.com/adrg/xdg", @@ -177,7 +171,9 @@ func TestBuildGoPkgInfo(t *testing.T) { Version: "v0.2.1", }, }, - }, nil, + }, + cryptoSettings: nil, + arch: "", }, expected: []pkg.Package{ { @@ -200,14 +196,13 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "buildGoPkgInfo parses a blank mod and returns no packages", - mod: &extendedBuildInfo{&debug.BuildInfo{}, nil}, + mod: &extendedBuildInfo{&debug.BuildInfo{}, nil, ""}, expected: []pkg.Package(nil), }, { name: "parse a mod without main module", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Settings: []debug.BuildSetting{ {Key: "GOARCH", Value: archDetails}, @@ -221,7 +216,9 @@ func TestBuildGoPkgInfo(t *testing.T) { Sum: "h1:VSVdnH7cQ7V+B33qSJHTCRlNgra1607Q8PzEmnvb2Ic=", }, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -249,9 +246,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse a mod with path but no main module", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Settings: []debug.BuildSetting{ {Key: "GOARCH", Value: archDetails}, @@ -259,7 +255,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOAMD64", Value: "v1"}, }, Path: "github.com/a/b/c", - }, []string{"boringcrypto + fips"}, + }, + cryptoSettings: []string{"boringcrypto + fips"}, + arch: archDetails, }, expected: []pkg.Package{ { @@ -294,9 +292,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse a mod without packages", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -304,15 +301,16 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOOS", Value: "darwin"}, {Key: "GOAMD64", Value: "v1"}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{unmodifiedMain}, }, { name: "parse main mod and replace devel pseudo version and ldflags exists (but contains no version)", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -323,7 +321,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X blah=foobar`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -359,9 +359,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with one from ldflags with vcs. build settings", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -372,7 +371,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -408,9 +409,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with one from ldflags without any vcs. build settings", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -419,7 +419,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOAMD64", Value: "v1"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X github.com/anchore/syft/internal/version.version=0.79.0`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -453,9 +455,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with one from ldflags main.version without any vcs. build settings", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -464,7 +465,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOAMD64", Value: "v1"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.version=0.79.0`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -498,9 +501,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with one from ldflags main.Version without any vcs. build settings", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -509,7 +511,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "GOAMD64", Value: "v1"}, {Key: "-ldflags", Value: `build -ldflags="-w -s -extldflags '-static' -X main.Version=0.79.0`}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -543,9 +547,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse main mod and replace devel version with a pseudo version", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -555,7 +558,9 @@ func TestBuildGoPkgInfo(t *testing.T) { {Key: "vcs.revision", Value: "41bc6bb410352845f22766e27dd48ba93aa825a4"}, {Key: "vcs.time", Value: "2022-10-14T19:54:57Z"}, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -590,9 +595,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse a populated mod string and returns packages but no source info", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -612,7 +616,9 @@ func TestBuildGoPkgInfo(t *testing.T) { Sum: "h1:DYssiUV1pBmKqzKsm4mqXx8artqC0Q8HgZsVI3lMsAg=", }, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -664,9 +670,8 @@ func TestBuildGoPkgInfo(t *testing.T) { }, { name: "parse a populated mod string and returns packages when a replace directive exists", - arch: archDetails, mod: &extendedBuildInfo{ - &debug.BuildInfo{ + BuildInfo: &debug.BuildInfo{ GoVersion: goCompiledVersion, Main: debug.Module{Path: "github.com/anchore/syft", Version: "(devel)"}, Settings: []debug.BuildSetting{ @@ -691,7 +696,9 @@ func TestBuildGoPkgInfo(t *testing.T) { }, }, }, - }, nil, + }, + cryptoSettings: nil, + arch: archDetails, }, expected: []pkg.Package{ { @@ -756,7 +763,7 @@ func TestBuildGoPkgInfo(t *testing.T) { ) c := goBinaryCataloger{} - pkgs := c.buildGoPkgInfo(fileresolver.Empty{}, location, test.mod, test.arch) + pkgs := c.buildGoPkgInfo(fileresolver.Empty{}, location, test.mod, test.mod.arch) assert.Equal(t, test.expected, pkgs) }) } diff --git a/syft/pkg/cataloger/golang/scan_binary.go b/syft/pkg/cataloger/golang/scan_binary.go index b19e24274..892729929 100644 --- a/syft/pkg/cataloger/golang/scan_binary.go +++ b/syft/pkg/cataloger/golang/scan_binary.go @@ -15,16 +15,17 @@ import ( type extendedBuildInfo struct { *debug.BuildInfo cryptoSettings []string + arch string } // scanFile scans file to try to report the Go and module versions. -func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuildInfo, []string) { +func scanFile(reader unionreader.UnionReader, filename string) []*extendedBuildInfo { // NOTE: multiple readers are returned to cover universal binaries, which are files // with more than one binary readers, err := unionreader.GetReaders(reader) if err != nil { log.WithFields("error", err).Warnf("failed to open a golang binary") - return nil, nil + return nil } var builds []*extendedBuildInfo @@ -34,6 +35,7 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuild log.WithFields("file", filename, "error", err).Trace("unable to read golang buildinfo") continue } + // it's possible the reader just isn't a go binary, in which case just skip it if bi == nil { continue } @@ -41,15 +43,22 @@ func scanFile(reader unionreader.UnionReader, filename string) ([]*extendedBuild v, err := getCryptoInformation(r) if err != nil { log.WithFields("file", filename, "error", err).Trace("unable to read golang version info") - continue + // don't skip this build info. + // we can still catalog packages, even if we can't get the crypto information + } + arch := getGOARCH(bi.Settings) + if arch == "" { + arch, err = getGOARCHFromBin(r) + if err != nil { + log.WithFields("file", filename, "error", err).Trace("unable to read golang arch info") + // don't skip this build info. + // we can still catalog packages, even if we can't get the arch information + } } - builds = append(builds, &extendedBuildInfo{bi, v}) + builds = append(builds, &extendedBuildInfo{bi, v, arch}) } - - archs := getArchs(readers, builds) - - return builds, archs + return builds } func getCryptoInformation(reader io.ReaderAt) ([]string, error) { From 51d38f8e592dbea672954c9d5c6c9f4bffc97b9b Mon Sep 17 00:00:00 2001 From: William Murphy Date: Thu, 31 Aug 2023 10:19:55 -0400 Subject: [PATCH 029/106] fix: in some cases, try to use pom info to guess name and version to top level jar (#2080) Otherwise, small renames like 'hudson-war-2.2.1.war' to 'hudson.war', would cause syft to incorrectly catolog the archive. Signed-off-by: Will Murphy --- syft/pkg/cataloger/java/archive_parser.go | 51 +++++++++++++++++-- .../regression_java_virtualpath_test.go | 36 +++++++++++++ .../Dockerfile | 7 +++ 3 files changed, 91 insertions(+), 3 deletions(-) create mode 100644 test/integration/regression_java_virtualpath_test.go create mode 100644 test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index a9662be65..f6570cb79 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -149,7 +149,6 @@ func (j *archiveParser) parse() ([]pkg.Package, []artifact.Relationship, error) // discoverMainPackage parses the root Java manifest used as the parent package to all discovered nested packages. func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { // search and parse java manifest files - // TODO: do we want to prefer or check for pom files over manifest here? manifestMatches := j.fileManifest.GlobMatch(manifestGlob) if len(manifestMatches) > 1 { return nil, fmt.Errorf("found multiple manifests in the jar: %+v", manifestMatches) @@ -186,9 +185,24 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest licenses := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...) + /* + We should name and version from, in this order: + 1. pom.properties if we find exactly 1 + 2. pom.xml if we find exactly 1 + 3. manifest + 4. filename + */ + name, version := j.guessMainPackageNameAndVersionFromPomInfo() + if name == "" { + name = selectName(manifest, j.fileInfo) + } + if version == "" { + version = selectVersion(manifest, j.fileInfo) + } return &pkg.Package{ - Name: selectName(manifest, j.fileInfo), - Version: selectVersion(manifest, j.fileInfo), + // TODO: maybe select name should just have a pom properties in it? + Name: name, + Version: version, Language: pkg.Java, Licenses: pkg.NewLicenseSet(licenses...), Locations: file.NewLocationSet( @@ -204,6 +218,37 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { }, nil } +func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (string, string) { + pomPropertyMatches := j.fileManifest.GlobMatch(pomPropertiesGlob) + pomMatches := j.fileManifest.GlobMatch(pomXMLGlob) + var pomPropertiesObject pkg.PomProperties + var pomProjectObject pkg.PomProject + if len(pomPropertyMatches) == 1 || len(pomMatches) == 1 { + // we have exactly 1 pom.properties or pom.xml in the archive; assume it represents the + // package we're scanning if the names seem like a plausible match + properties, _ := pomPropertiesByParentPath(j.archivePath, j.location, pomPropertyMatches) + projects, _ := pomProjectByParentPath(j.archivePath, j.location, pomMatches) + + for parentPath, propertiesObj := range properties { + if propertiesObj.ArtifactID != "" && j.fileInfo.name != "" && strings.HasPrefix(propertiesObj.ArtifactID, j.fileInfo.name) { + pomPropertiesObject = propertiesObj + if proj, exists := projects[parentPath]; exists { + pomProjectObject = proj + } + } + } + } + name := pomPropertiesObject.ArtifactID + if name == "" { + name = pomProjectObject.ArtifactID + } + version := pomPropertiesObject.Version + if version == "" { + version = pomProjectObject.Version + } + return name, version +} + // discoverPkgsFromAllMavenFiles parses Maven POM properties/xml for a given // parent package, returning all listed Java packages found for each pom // properties discovered and potentially updating the given parentPkg with new diff --git a/test/integration/regression_java_virtualpath_test.go b/test/integration/regression_java_virtualpath_test.go new file mode 100644 index 000000000..659e5d017 --- /dev/null +++ b/test/integration/regression_java_virtualpath_test.go @@ -0,0 +1,36 @@ +package integration + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +func TestWarCatalogedCorrectlyIfRenamed(t *testing.T) { + // install hudson-war@2.2.1 and renames the file to `/hudson.war` + sbom, _ := catalogFixtureImage(t, "image-java-virtualpath-regression", source.SquashedScope, nil) + + badPURL := "pkg:maven/hudson/hudson@2.2.1" + goodPURL := "pkg:maven/org.jvnet.hudson.main/hudson-war@2.2.1" + foundCorrectPackage := false + badVirtualPath := "/hudson.war:org.jvnet.hudson.main:hudson-war" + goodVirtualPath := "/hudson.war" + for _, p := range sbom.Artifacts.Packages.Sorted() { + if p.Type == pkg.JavaPkg && strings.Contains(p.Name, "hudson") { + assert.NotEqual(t, badPURL, p.PURL, "must not find bad purl %q", badPURL) + virtPath := "" + if meta, ok := p.Metadata.(pkg.JavaMetadata); ok { + virtPath = meta.VirtualPath + if p.PURL == goodPURL && virtPath == goodVirtualPath { + foundCorrectPackage = true + } + } + assert.NotEqual(t, badVirtualPath, virtPath, "must not find bad virtual path %q", badVirtualPath) + } + } + assert.True(t, foundCorrectPackage, "must find correct package, but did not") +} diff --git a/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile b/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile new file mode 100644 index 000000000..a8d70f16a --- /dev/null +++ b/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile @@ -0,0 +1,7 @@ +FROM alpine:latest + +RUN wget https://repo1.maven.org/maven2/org/jvnet/hudson/main/hudson-war/2.2.1/hudson-war-2.2.1.war + +RUN mv hudson-war-2.2.1.war hudson.war + + From 36d794febed917de18ecae93c5d82f0af1c410f7 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 31 Aug 2023 10:40:11 -0400 Subject: [PATCH 030/106] fix quiet flag (#2081) Signed-off-by: Alex Goodman --- cmd/syft/cli/cli.go | 2 +- test/cli/packages_cmd_test.go | 18 ++++++++++++++++++ test/cli/trait_assertions_test.go | 7 +++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/cmd/syft/cli/cli.go b/cmd/syft/cli/cli.go index 187cf09c4..b1347b274 100644 --- a/cmd/syft/cli/cli.go +++ b/cmd/syft/cli/cli.go @@ -29,7 +29,7 @@ func New(id clio.Identification) clio.Application { // select a UI based on the logging configuration and state of stdin (if stdin is a tty) func(cfg clio.Config) ([]clio.UI, error) { noUI := ui.None(cfg.Log.Quiet) - if !cfg.Log.AllowUI(os.Stdin) { + if !cfg.Log.AllowUI(os.Stdin) || cfg.Log.Quiet { return []clio.UI{noUI}, nil } diff --git a/test/cli/packages_cmd_test.go b/test/cli/packages_cmd_test.go index 52efac920..dc100ebeb 100644 --- a/test/cli/packages_cmd_test.go +++ b/test/cli/packages_cmd_test.go @@ -36,6 +36,24 @@ func TestPackagesCmdFlags(t *testing.T) { assertSuccessfulReturnCode, }, }, + { + name: "quiet-flag-with-logger", + args: []string{"packages", "-qvv", "-o", "json", coverageImage}, + assertions: []traitAssertion{ + assertJsonReport, + assertNoStderr, + assertSuccessfulReturnCode, + }, + }, + { + name: "quiet-flag-with-tui", + args: []string{"packages", "-q", "-o", "json", coverageImage}, + assertions: []traitAssertion{ + assertJsonReport, + assertNoStderr, + assertSuccessfulReturnCode, + }, + }, { name: "multiple-output-flags", args: []string{"packages", "-o", "table", "-o", "json=" + tmp + ".tmp/multiple-output-flag-test.json", coverageImage}, diff --git a/test/cli/trait_assertions_test.go b/test/cli/trait_assertions_test.go index d441d021b..828b407bf 100644 --- a/test/cli/trait_assertions_test.go +++ b/test/cli/trait_assertions_test.go @@ -83,6 +83,13 @@ func assertNotInOutput(data string) traitAssertion { } } +func assertNoStderr(tb testing.TB, _, stderr string, _ int) { + tb.Helper() + if len(stderr) > 0 { + tb.Errorf("expected stderr to be empty, but got %q", stderr) + } +} + func assertInOutput(data string) traitAssertion { return func(tb testing.TB, stdout, stderr string, _ int) { tb.Helper() From b454160549bbd199e0a5693750856f30b41767f7 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 31 Aug 2023 10:50:32 -0400 Subject: [PATCH 031/106] tidy gomod and gitignore (#2082) Signed-off-by: Alex Goodman --- .gitignore | 34 ++++++++++++++++++++++++---------- go.mod | 7 ++----- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/.gitignore b/.gitignore index 4cfd0ada7..e6fde93b5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,18 +1,38 @@ +# local development tailoring go.work go.work.sum +.tool-versions + +# app configuration /.syft.yaml + +# tool and bin directories +.tmp/ +bin/ /bin /.bin /build -CHANGELOG.md -VERSION -/test/results /dist /snapshot -.server/ + +# changelog generation +CHANGELOG.md +VERSION + +# IDE configuration .vscode/ +.idea/ +.server/ .history/ + +# test related *.fingerprint +/test/results +coverage.txt +*.log + +# probable archives +.images *.tar *.jar *.war @@ -20,13 +40,7 @@ VERSION *.jpi *.hpi *.zip -.idea/ *.iml -*.log -.images -.tmp/ -coverage.txt -bin/ # Binaries for programs and plugins *.exe diff --git a/go.mod b/go.mod index cb60f1ffd..ce057da92 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/adrg/xdg v0.4.0 // indirect github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 github.com/anchore/clio v0.0.0-20230823172630-c42d666061af + github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 @@ -41,6 +42,7 @@ require ( github.com/google/uuid v1.3.1 github.com/gookit/color v1.5.4 github.com/hashicorp/go-multierror v1.1.1 + github.com/iancoleman/strcase v0.3.0 github.com/invopop/jsonschema v0.7.0 github.com/jinzhu/copier v0.4.0 github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 @@ -79,11 +81,6 @@ require ( golang.org/x/mod v0.12.0 golang.org/x/net v0.14.0 golang.org/x/term v0.11.0 -) - -require ( - github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe - github.com/iancoleman/strcase v0.3.0 gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.25.0 ) From 007b034ee38063fd5b41c82741e7561448dc817d Mon Sep 17 00:00:00 2001 From: William Murphy Date: Thu, 31 Aug 2023 16:57:55 -0400 Subject: [PATCH 032/106] fix: correct group IDs for commons-codec, okhttp, okio, and add integration tests for Java PURL generation (#2075) Add overall integration test for java PURL detection. Signed-off-by: Will Murphy --- .../cataloger/common/cpe/java_groupid_map.go | 3 + test/integration/java_purl_test.go | 197 ++++++++++++++++++ .../image-test-java-purls/Dockerfile | 1 + 3 files changed, 201 insertions(+) create mode 100644 test/integration/java_purl_test.go create mode 100644 test/integration/test-fixtures/image-test-java-purls/Dockerfile diff --git a/syft/pkg/cataloger/common/cpe/java_groupid_map.go b/syft/pkg/cataloger/common/cpe/java_groupid_map.go index 5205c81e9..ece0b004b 100644 --- a/syft/pkg/cataloger/common/cpe/java_groupid_map.go +++ b/syft/pkg/cataloger/common/cpe/java_groupid_map.go @@ -36,6 +36,9 @@ var DefaultArtifactIDToGroupID = map[string]string{ "ant-trax": "org.apache.ant", "ant-weblogic": "org.apache.ant", "ant-xz": "org.apache.ant", + "commons-codec": "commons-codec", + "okhttp": "com.squareup.okhttp3", + "okio": "com.squareup.okio", "spring": "org.springframework", "spring-amqp": "org.springframework.amqp", "spring-batch-core": "org.springframework.batch", diff --git a/test/integration/java_purl_test.go b/test/integration/java_purl_test.go new file mode 100644 index 000000000..06c54d66d --- /dev/null +++ b/test/integration/java_purl_test.go @@ -0,0 +1,197 @@ +package integration + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/source" +) + +func TestJavaPURLs(t *testing.T) { + sbom, _ := catalogFixtureImage(t, "image-test-java-purls", source.SquashedScope, nil) + found := make(map[string]string) + for _, p := range sbom.Artifacts.Packages.Sorted() { + if p.Type != pkg.JavaPkg && p.Type != pkg.JenkinsPluginPkg { + continue + } + key := fmt.Sprintf("%s@%s", p.Name, p.Version) + found[key] = p.PURL + } + for key, expectedPURL := range expectedPURLs { + purl := found[key] + assert.Equal(t, expectedPURL, purl, fmt.Sprintf("found wrong or missing PURL for %s want %s, got %s", key, expectedPURL, purl)) + } + for key, foundPURL := range found { + expectedPURL := expectedPURLs[key] + assert.Equal(t, expectedPURL, foundPURL, fmt.Sprintf("found extra purl for %s want %s, got %s", key, expectedPURL, foundPURL)) + } +} + +// Constructed by: +// syft anchore/test_images:java-56d52bc -o template -t /tmp/test.templ | grep 'pkg:maven' | sort | uniq >> test/integration/java_purl_test.go +// where the template is: +/* +{{ range .Artifacts}}"{{.Name}}@{{.Version}}":"{{.PURL}}", +{{ end }} +*/ +// The map was then hand-edited for correctness by comparing to Maven Central. +var expectedPURLs = map[string]string{ + "TwilioNotifier@0.2.1": "pkg:maven/com.twilio.jenkins/TwilioNotifier@0.2.1", + "access-modifier-annotation@1.0": "pkg:maven/org.kohsuke/access-modifier-annotation@1.0", + "acegi-security@1.0.5": "pkg:maven/org.acegisecurity/acegi-security@1.0.5", + "activation@1.1.1-hudson-1": "pkg:maven/org.jvnet.hudson/activation@1.1.1-hudson-1", + "akuma@1.2": "pkg:maven/com.sun.akuma/akuma@1.2", + "animal-sniffer-annotation@1.0": "pkg:maven/org.jvnet/animal-sniffer-annotation@1.0", + "annotation-indexer@1.2": "pkg:maven/org.jvnet.hudson/annotation-indexer@1.2", + "annotations@13.0": "pkg:maven/org.jetbrains/annotations@13.0", + "ant-launcher@1.8.0": "pkg:maven/org.apache.ant/ant-launcher@1.8.0", + "ant@1.8.0": "pkg:maven/org.apache.ant/ant@1.8.0", + "antlr@2.7.6": "pkg:maven/antlr/antlr@2.7.6", + "aopalliance@1.0": "pkg:maven/aopalliance/aopalliance@1.0", + "args4j@2.0.16": "pkg:maven/args4j/args4j@2.0.16", + "asm-commons@2.2.3": "pkg:maven/asm-commons/asm-commons@2.2.3", + "asm-tree@2.2.3": "pkg:maven/asm-tree/asm-tree@2.2.3", + "asm@2.2.3": "pkg:maven/asm/asm@2.2.3", + "avalon-framework@4.1.3": "pkg:maven/avalon-framework/avalon-framework@4.1.3", + "bridge-method-annotation@1.2": "pkg:maven/com.infradna.tool/bridge-method-annotation@1.2", + "classworlds@1.1": "pkg:maven/org.codehaus.classworlds/classworlds@1.1", + "cli@1.390": "pkg:maven/org.jvnet.hudson.main/cli@1.390", + "commons-beanutils@1.8.0": "pkg:maven/commons-beanutils/commons-beanutils@1.8.0", + "commons-codec@1.2": "pkg:maven/commons-codec/commons-codec@1.2", + "commons-codec@1.4": "pkg:maven/commons-codec/commons-codec@1.4", + "commons-collections@3.2": "pkg:maven/commons-collections/commons-collections@3.2", + "commons-digester@1.7": "pkg:maven/commons-digester/commons-digester@1.7", + "commons-discovery@0.4": "pkg:maven/commons-discovery/commons-discovery@0.4", + "commons-fileupload@1.2.1": "pkg:maven/commons-fileupload/commons-fileupload@1.2.1", + "commons-httpclient@3.1": "pkg:maven/org.apache/commons-httpclient@3.1", + "commons-httpclient@3.1-rc1": "pkg:maven/commons-httpclient/commons-httpclient@3.1-rc1", + "commons-io@1.4": "pkg:maven/commons-io/commons-io@1.4", + "commons-jelly-tags-define@1.0.1-hudson-20071021": "pkg:maven/org.jvnet.hudson/commons-jelly-tags-define@1.0.1-hudson-20071021", + "commons-jelly-tags-fmt@1.0": "pkg:maven/commons-jelly-tags-fmt/commons-jelly-tags-fmt@1.0", + "commons-jelly-tags-xml@1.1": "pkg:maven/commons-jelly-tags-xml/commons-jelly-tags-xml@1.1", + "commons-jelly@1.1-hudson-20100305": "pkg:maven/org.jvnet.hudson/commons-jelly@1.1-hudson-20100305", + "commons-jexl@1.1-hudson-20090508": "pkg:maven/org.jvnet.hudson/commons-jexl@1.1-hudson-20090508", + "commons-lang@2.4": "pkg:maven/commons-lang/commons-lang@2.4", + "commons-lang@2.5": "pkg:maven/commons-lang/commons-lang@2.5", + "commons-logging@1.0.4": "pkg:maven/org.apache.commons.logging/commons-logging@1.0.4", + "commons-logging@1.1": "pkg:maven/org.apache.commons.logging/commons-logging@1.1", + "commons-logging@1.1.1": "pkg:maven/commons-logging/commons-logging@1.1.1", + "commons-pool@1.3": "pkg:maven/commons-pool/commons-pool@1.3", + "crypto-util@1.0": "pkg:maven/org.jvnet.hudson/crypto-util@1.0", + "cvs@1.2": "pkg:maven/org.jvnet.hudson.plugins/cvs@1.2", + "dom4j@1.6.1-hudson-3": "pkg:maven/dom4j/dom4j@1.6.1-hudson-3", + "doxia-sink-api@1.0-alpha-10": "pkg:maven/org.apache.maven.doxia/doxia-sink-api@1.0-alpha-10", + "easymock@2.4": "pkg:maven/org.easymock/easymock@2.4", + "embedded_su4j@1.1": "pkg:maven/com.sun.solaris/embedded_su4j@1.1", + "example-java-app-gradle@0.1.0": "pkg:maven/example-java-app-gradle/example-java-app-gradle@0.1.0", + "ezmorph@1.0.3": "pkg:maven/net.sf.ezmorph/ezmorph@1.0.3", + "graph-layouter@1.0": "pkg:maven/org.kohsuke/graph-layouter@1.0", + "groovy-all@1.6.0": "pkg:maven/groovy-all/groovy-all@1.6.0", + "gson@2.8.6": "pkg:maven/com.google.code.gson/gson@2.8.6", + "guava@r06": "pkg:maven/com.google.guava/guava@r06", + "httpclient@4.1.1": "pkg:maven/org.apache.httpcomponents/httpclient@4.1.1", + "httpcore@4.1": "pkg:maven/org.apache.httpcomponents/httpcore@4.1", + "hudson-cli@": "pkg:maven/hudson-cli/hudson-cli", + "hudson-core@1.390": "pkg:maven/org.jvnet.hudson.main/hudson-core@1.390", + "hudson-war@1.390": "pkg:maven/org.jvnet.hudson.main/hudson-war@1.390", + "j-interop@2.0.5": "pkg:maven/j-interop/j-interop@2.0.5", + "j-interopdeps@2.0.5": "pkg:maven/j-interopdeps/j-interopdeps@2.0.5", + "jaxen@1.1-beta-11": "pkg:maven/org.jaxen/jaxen@1.1-beta-11", + "jcaptcha-all@1.0-RC6": "pkg:maven/jcaptcha-all/jcaptcha-all@1.0-RC6", + "jcifs@1.3.14-kohsuke-1": "pkg:maven/org.samba.jcifs/jcifs@1.3.14-kohsuke-1", + "jcommon@1.0.12": "pkg:maven/jfree/jcommon@1.0.12", + "jfreechart@1.0.9": "pkg:maven/jfreechart/jfreechart@1.0.9", + "jinterop-proxy@1.1": "pkg:maven/org.kohsuke.jinterop/jinterop-proxy@1.1", + "jinterop-wmi@1.0": "pkg:maven/org.jvnet.hudson/jinterop-wmi@1.0", + "jline@0.9.94": "pkg:maven/jline/jline@0.9.94", + "jmdns@3.1.6-hudson-2": "pkg:maven/com.strangeberry.jmdns.tools.Main/jmdns@3.1.6-hudson-2", + "jna-posix@1.0.3": "pkg:maven/org.jruby.ext.posix/jna-posix@1.0.3", + "jna@3.2.4": "pkg:maven/com.sun.jna/jna@3.2.4", + "jsch@0.1.27": "pkg:maven/jsch/jsch@0.1.27", + "json-lib@2.1-rev6": "pkg:maven/json-lib/json-lib@2.1-rev6", + "json@20200518": "pkg:maven/org.json/json@20200518", + "jstl@1.1.0": "pkg:maven/com.sun/jstl@1.1.0", + "jtidy@4aug2000r7-dev-hudson-1": "pkg:maven/jtidy/jtidy@4aug2000r7-dev-hudson-1", + "junit@4.13.1": "pkg:maven/junit/junit@4.13.1", + "kotlin-stdlib-common@1.3.70": "pkg:maven/kotlin-stdlib-common/kotlin-stdlib-common@1.3.70", + "kotlin-stdlib@1.3.70": "pkg:maven/kotlin-stdlib/kotlin-stdlib@1.3.70", + "libpam4j@1.2": "pkg:maven/org.jvnet.libpam4j/libpam4j@1.2", + "libzfs@0.5": "pkg:maven/org.jvnet.libzfs/libzfs@0.5", + "localizer@1.10": "pkg:maven/org.jvnet.localizer/localizer@1.10", + "log4j@1.2.9": "pkg:maven/log4j/log4j@1.2.9", + "logkit@1.0.1": "pkg:maven/logkit/logkit@1.0.1", + "mail@1.4": "pkg:maven/com.sun/mail@1.4", + "maven-agent@1.390": "pkg:maven/org.jvnet.hudson.main/maven-agent@1.390", + "maven-artifact-manager@2.0.9": "pkg:maven/org.apache.maven/maven-artifact-manager@2.0.9", + "maven-artifact@2.0.9": "pkg:maven/org.apache.maven/maven-artifact@2.0.9", + "maven-core@2.0.9": "pkg:maven/org.apache.maven/maven-core@2.0.9", + "maven-embedder@2.0.4": "pkg:maven/org.apache.maven/maven-embedder@2.0.4", + "maven-embedder@2.0.4-hudson-1": "pkg:maven/org.jvnet.hudson/maven-embedder@2.0.4-hudson-1", + "maven-error-diagnostics@2.0.9": "pkg:maven/org.apache.maven/maven-error-diagnostics@2.0.9", + "maven-interceptor@1.390": "pkg:maven/org.jvnet.hudson.main/maven-interceptor@1.390", + "maven-model@2.0.9": "pkg:maven/org.apache.maven/maven-model@2.0.9", + "maven-monitor@2.0.9": "pkg:maven/org.apache.maven/maven-monitor@2.0.9", + "maven-plugin-api@2.0.9": "pkg:maven/org.apache.maven/maven-plugin-api@2.0.9", + "maven-plugin-descriptor@2.0.9": "pkg:maven/org.apache.maven/maven-plugin-descriptor@2.0.9", + "maven-plugin-parameter-documenter@2.0.9": "pkg:maven/org.apache.maven/maven-plugin-parameter-documenter@2.0.9", + "maven-plugin-registry@2.0.9": "pkg:maven/org.apache.maven/maven-plugin-registry@2.0.9", + "maven-plugin@1.390": "pkg:maven/org.jvnet.hudson.main/maven-plugin@1.390", + "maven-profile@2.0.9": "pkg:maven/org.apache.maven/maven-profile@2.0.9", + "maven-project@2.0.9": "pkg:maven/org.apache.maven/maven-project@2.0.9", + "maven-reporting-api@2.0.9": "pkg:maven/org.apache.maven.reporting/maven-reporting-api@2.0.9", + "maven-repository-metadata@2.0.9": "pkg:maven/org.apache.maven/maven-repository-metadata@2.0.9", + "maven-settings@2.0.9": "pkg:maven/org.apache.maven/maven-settings@2.0.9", + "maven2.1-interceptor@1.2": "pkg:maven/org.jvnet.hudson/maven2.1-interceptor@1.2", + "memory-monitor@1.3": "pkg:maven/org.jvnet.hudson/memory-monitor@1.3", + "nomad@0.7.4": "pkg:maven/org.jenkins-ci.plugins/nomad@0.7.4", + "okhttp@4.5.0": "pkg:maven/com.squareup.okhttp3/okhttp@4.5.0", + "okio@2.5.0": "pkg:maven/com.squareup.okio/okio@2.5.0", + "oro@2.0.8": "pkg:maven/org.apache.oro/oro@2.0.8", + "plexus-container-default@1.0-alpha-9-stable-1": "pkg:maven/org.codehaus.plexus/plexus-container-default@1.0-alpha-9-stable-1", + "plexus-interactivity-api@1.0-alpha-4": "pkg:maven/org.codehaus.plexus/plexus-interactivity-api@1.0-alpha-4", + "plexus-utils@1.5.1": "pkg:maven/org.codehaus.plexus/plexus-utils@1.5.1", + "remoting@1.390": "pkg:maven/org.jvnet.hudson.main/remoting@1.390", + "robust-http-client@1.1": "pkg:maven/org.jvnet.robust-http-client/robust-http-client@1.1", + "sdk@3.0": "pkg:maven/sdk/sdk@3.0", + "sezpoz@1.7": "pkg:maven/net.java.sezpoz/sezpoz@1.7", + "slave@": "pkg:maven/slave/slave", + "slide-webdavlib@2.1": "pkg:maven/slide-webdavlib/slide-webdavlib@2.1", + "spring-aop@2.5": "pkg:maven/org.springframework.bundle.spring.aop/spring-aop@2.5", + "spring-beans@2.5": "pkg:maven/org.springframework/spring-beans@2.5", + "spring-context@2.5": "pkg:maven/org.springframework.bundle.spring.context/spring-context@2.5", + "spring-core@2.5": "pkg:maven/org.springframework/spring-core@2.5", + "spring-dao@1.2.9": "pkg:maven/spring-dao/spring-dao@1.2.9", + "spring-jdbc@1.2.9": "pkg:maven/spring-jdbc/spring-jdbc@1.2.9", + "spring-web@2.5": "pkg:maven/org.springframework/spring-web@2.5", + "ssh-slaves@0.14": "pkg:maven/org.jvnet.hudson.plugins/ssh-slaves@0.14", + "stapler-adjunct-timeline@1.2": "pkg:maven/org.kohsuke.stapler/stapler-adjunct-timeline@1.2", + "stapler-jelly@1.155": "pkg:maven/org.kohsuke.stapler/stapler-jelly@1.155", + "stapler@1.155": "pkg:maven/org.kohsuke.stapler/stapler@1.155", + "stax-api@1.0.1": "pkg:maven/stax-api/stax-api@1.0.1", + "subversion@1.20": "pkg:maven/org.jvnet.hudson.plugins/subversion@1.20", + "svnkit@1.3.4-hudson-2": "pkg:maven/svnkit/svnkit@1.3.4-hudson-2", + "task-reactor@1.2": "pkg:maven/org.jvnet.hudson/task-reactor@1.2", + "tiger-types@1.3": "pkg:maven/org.jvnet/tiger-types@1.3", + "trilead-putty-extension@1.0": "pkg:maven/org.kohsuke/trilead-putty-extension@1.0", + "trilead-ssh2@build212-hudson-5": "pkg:maven/org.jvnet.hudson/trilead-ssh2@build212-hudson-5", + "txw2@20070624": "pkg:maven/txw2/txw2@20070624", + "wagon-file@1.0-beta-2": "pkg:maven/org.apache.maven.wagon/wagon-file@1.0-beta-2", + "wagon-http-lightweight@1.0-beta-2": "pkg:maven/org.apache.maven.wagon/wagon-http-lightweight@1.0-beta-2", + "wagon-http-shared@1.0-beta-2": "pkg:maven/org.apache.maven.wagon/wagon-http-shared@1.0-beta-2", + "wagon-provider-api@1.0-beta-2": "pkg:maven/org.apache.maven.wagon/wagon-provider-api@1.0-beta-2", + "wagon-ssh-common@1.0-beta-2": "pkg:maven/org.apache.maven.wagon/wagon-ssh-common@1.0-beta-2", + "wagon-ssh-external@1.0-beta-2": "pkg:maven/org.apache.maven.wagon/wagon-ssh-external@1.0-beta-2", + "wagon-ssh@1.0-beta-2": "pkg:maven/org.apache.maven.wagon/wagon-ssh@1.0-beta-2", + "wagon-webdav@1.0-beta-2-hudson-1": "pkg:maven/org.jvnet.hudson/wagon-webdav@1.0-beta-2-hudson-1", + "windows-remote-command@1.0": "pkg:maven/org.jvnet.hudson/windows-remote-command@1.0", + "winp@1.14": "pkg:maven/org.jvnet.winp/winp@1.14", + "winstone@0.9.10-hudson-24": "pkg:maven/org.jvnet.hudson.winstone/winstone@0.9.10-hudson-24", + "wstx-asl@3.2.7": "pkg:maven/wstx-asl/wstx-asl@3.2.7", + "xml-im-exporter@1.1": "pkg:maven/xml-im-exporter/xml-im-exporter@1.1", + "xpp3@1.1.4c": "pkg:maven/xpp3/xpp3@1.1.4c", + "xpp3_min@1.1.4c": "pkg:maven/xpp3_min/xpp3_min@1.1.4c", + "xstream@1.3.1-hudson-8": "pkg:maven/org.jvnet.hudson/xstream@1.3.1-hudson-8", +} diff --git a/test/integration/test-fixtures/image-test-java-purls/Dockerfile b/test/integration/test-fixtures/image-test-java-purls/Dockerfile new file mode 100644 index 000000000..05545a7ce --- /dev/null +++ b/test/integration/test-fixtures/image-test-java-purls/Dockerfile @@ -0,0 +1 @@ +FROM anchore/test_images@sha256:10008791acbc5866de04108746a02a0c4029ce3a4400a9b3dad45d7f2245f9da From 49e7f399f90455a43635a5f5a747f3958a7151bd Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 5 Sep 2023 10:33:38 -0400 Subject: [PATCH 033/106] expose cobra command in cli package (#2097) Signed-off-by: Alex Goodman --- cmd/syft/cli/cli.go | 19 ++++++++++++++++--- cmd/syft/main.go | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/cmd/syft/cli/cli.go b/cmd/syft/cli/cli.go index b1347b274..91b7c5f80 100644 --- a/cmd/syft/cli/cli.go +++ b/cmd/syft/cli/cli.go @@ -4,6 +4,7 @@ import ( "os" cranecmd "github.com/google/go-containerregistry/cmd/crane/cmd" + "github.com/spf13/cobra" "github.com/anchore/clio" "github.com/anchore/stereoscope" @@ -15,12 +16,24 @@ import ( "github.com/anchore/syft/internal/redact" ) -// New constructs the `syft packages` command, aliases the root command to `syft packages`, +// Application constructs the `syft packages` command, aliases the root command to `syft packages`, // and constructs the `syft power-user` command. It is also responsible for // organizing flag usage and injecting the application config for each command. // It also constructs the syft attest command and the syft version command. // `RunE` is the earliest that the complete application configuration can be loaded. -func New(id clio.Identification) clio.Application { +func Application(id clio.Identification) clio.Application { + app, _ := create(id) + return app +} + +// Command returns the root command for the syft CLI application. This is useful for embedding the entire syft CLI +// into an existing application. +func Command(id clio.Identification) *cobra.Command { + _, cmd := create(id) + return cmd +} + +func create(id clio.Identification) (clio.Application, *cobra.Command) { clioCfg := clio.NewSetupConfig(id). WithGlobalConfigFlag(). // add persistent -c for reading an application config from WithGlobalLoggingFlags(). // add persistent -v and -q flags tied to the logging config @@ -77,5 +90,5 @@ func New(id clio.Identification) clio.Application { cranecmd.NewCmdAuthLogin(id.Name), // syft login uses the same command as crane ) - return app + return app, rootCmd } diff --git a/cmd/syft/main.go b/cmd/syft/main.go index c9a13881a..d1f303d0e 100644 --- a/cmd/syft/main.go +++ b/cmd/syft/main.go @@ -20,7 +20,7 @@ var ( ) func main() { - app := cli.New( + app := cli.Application( clio.Identification{ Name: applicationName, Version: version, From 305ee8705284897223e6112577aeef3f33c90e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=E1=BB=97=20Tr=E1=BB=8Dng=20H=E1=BA=A3i?= <41283691+hainenber@users.noreply.github.com> Date: Tue, 5 Sep 2023 21:39:51 +0700 Subject: [PATCH 034/106] fix(cdx): validate external refs before encoding (#2091) Signed-off-by: hainenber --- .../cyclonedxhelpers/external_references.go | 17 ++++++-- .../external_references_test.go | 43 ++++++++++++++++++- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/syft/formats/common/cyclonedxhelpers/external_references.go b/syft/formats/common/cyclonedxhelpers/external_references.go index 59f388717..29770070a 100644 --- a/syft/formats/common/cyclonedxhelpers/external_references.go +++ b/syft/formats/common/cyclonedxhelpers/external_references.go @@ -2,6 +2,7 @@ package cyclonedxhelpers import ( "fmt" + "net/url" "strings" "github.com/CycloneDX/cyclonedx-go" @@ -15,9 +16,11 @@ import ( func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference { var refs []cyclonedx.ExternalReference if hasMetadata(p) { + // Skip adding extracted URL and Homepage metadata + // as "external_reference" if the metadata isn't IRI-compliant switch metadata := p.Metadata.(type) { case pkg.ApkMetadata: - if metadata.URL != "" { + if metadata.URL != "" && isValidExternalRef(metadata.URL) { refs = append(refs, cyclonedx.ExternalReference{ URL: metadata.URL, Type: cyclonedx.ERTypeDistribution, @@ -31,20 +34,20 @@ func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference { }) } case pkg.NpmPackageJSONMetadata: - if metadata.URL != "" { + if metadata.URL != "" && isValidExternalRef(metadata.URL) { refs = append(refs, cyclonedx.ExternalReference{ URL: metadata.URL, Type: cyclonedx.ERTypeDistribution, }) } - if metadata.Homepage != "" { + if metadata.Homepage != "" && isValidExternalRef(metadata.Homepage) { refs = append(refs, cyclonedx.ExternalReference{ URL: metadata.Homepage, Type: cyclonedx.ERTypeWebsite, }) } case pkg.GemMetadata: - if metadata.Homepage != "" { + if metadata.Homepage != "" && isValidExternalRef(metadata.Homepage) { refs = append(refs, cyclonedx.ExternalReference{ URL: metadata.Homepage, Type: cyclonedx.ERTypeWebsite, @@ -158,3 +161,9 @@ func refComment(c *cyclonedx.Component, typ cyclonedx.ExternalReferenceType) str } return "" } + +// isValidExternalRef checks for IRI-comppliance for input string to be added into "external_reference" +func isValidExternalRef(s string) bool { + parsed, err := url.Parse(s) + return err == nil && parsed != nil && parsed.Host != "" +} diff --git a/syft/formats/common/cyclonedxhelpers/external_references_test.go b/syft/formats/common/cyclonedxhelpers/external_references_test.go index c6ce0355b..67fd73778 100644 --- a/syft/formats/common/cyclonedxhelpers/external_references_test.go +++ b/syft/formats/common/cyclonedxhelpers/external_references_test.go @@ -32,7 +32,7 @@ func Test_encodeExternalReferences(t *testing.T) { }, }, { - name: "from npm", + name: "from npm with valid URL", input: pkg.Package{ Metadata: pkg.NpmPackageJSONMetadata{ URL: "http://a-place.gov", @@ -42,6 +42,18 @@ func Test_encodeExternalReferences(t *testing.T) { {URL: "http://a-place.gov", Type: cyclonedx.ERTypeDistribution}, }, }, + { + name: "from npm with invalid URL but valid Homepage", + input: pkg.Package{ + Metadata: pkg.NpmPackageJSONMetadata{ + URL: "b-place", + Homepage: "http://b-place.gov", + }, + }, + expected: &[]cyclonedx.ExternalReference{ + {URL: "http://b-place.gov", Type: cyclonedx.ERTypeWebsite}, + }, + }, { name: "from cargo lock", input: pkg.Package{ @@ -132,3 +144,32 @@ func Test_encodeExternalReferences(t *testing.T) { }) } } + +func Test_isValidExternalRef(t *testing.T) { + tests := []struct { + name string + input string + expected bool + }{ + { + name: "valid URL for external_reference, git protocol", + input: "git+https://github.com/abc/def.git", + expected: true, + }, + { + name: "valid URL for external_reference, git protocol", + input: "git+https://github.com/abc/def.git", + expected: true, + }, + { + name: "invalid URL for external_reference", + input: "abc/def", + expected: false, + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert.Equal(t, test.expected, isValidExternalRef(test.input)) + }) + } +} From f8ab7c4695bad05ee522f8e8ef21b0583855452b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=E1=BB=97=20Tr=E1=BB=8Dng=20H=E1=BA=A3i?= <41283691+hainenber@users.noreply.github.com> Date: Wed, 6 Sep 2023 22:43:01 +0700 Subject: [PATCH 035/106] feat(cmd/update): add UA header with current ver when check for update (#2100) Signed-off-by: hainenber --- cmd/syft/cli/commands/update.go | 13 ++-- cmd/syft/cli/commands/update_test.go | 99 +++++++++++++++++----------- 2 files changed, 69 insertions(+), 43 deletions(-) diff --git a/cmd/syft/cli/commands/update.go b/cmd/syft/cli/commands/update.go index 3ef1812e1..88f159700 100644 --- a/cmd/syft/cli/commands/update.go +++ b/cmd/syft/cli/commands/update.go @@ -38,7 +38,7 @@ func applicationUpdateCheck(id clio.Identification, check *options.UpdateCheck) func checkForApplicationUpdate(id clio.Identification) { log.Debugf("checking if a new version of %s is available", id.Name) - isAvailable, newVersion, err := isUpdateAvailable(id.Version) + isAvailable, newVersion, err := isUpdateAvailable(id) if err != nil { // this should never stop the application log.Errorf(err.Error()) @@ -59,18 +59,18 @@ func checkForApplicationUpdate(id clio.Identification) { } // isUpdateAvailable indicates if there is a newer application version available, and if so, what the new version is. -func isUpdateAvailable(version string) (bool, string, error) { - if !isProductionBuild(version) { +func isUpdateAvailable(id clio.Identification) (bool, string, error) { + if !isProductionBuild(id.Version) { // don't allow for non-production builds to check for a version. return false, "", nil } - currentVersion, err := hashiVersion.NewVersion(version) + currentVersion, err := hashiVersion.NewVersion(id.Version) if err != nil { return false, "", fmt.Errorf("failed to parse current application version: %w", err) } - latestVersion, err := fetchLatestApplicationVersion() + latestVersion, err := fetchLatestApplicationVersion(id) if err != nil { return false, "", err } @@ -89,11 +89,12 @@ func isProductionBuild(version string) bool { return true } -func fetchLatestApplicationVersion() (*hashiVersion.Version, error) { +func fetchLatestApplicationVersion(id clio.Identification) (*hashiVersion.Version, error) { req, err := http.NewRequest(http.MethodGet, latestAppVersionURL.host+latestAppVersionURL.path, nil) if err != nil { return nil, fmt.Errorf("failed to create request for latest version: %w", err) } + req.Header.Add("User-Agent", fmt.Sprintf("%v %v", id.Name, id.Version)) client := http.Client{} resp, err := client.Do(req) diff --git a/cmd/syft/cli/commands/update_test.go b/cmd/syft/cli/commands/update_test.go index 96cd7de34..bc04a7465 100644 --- a/cmd/syft/cli/commands/update_test.go +++ b/cmd/syft/cli/commands/update_test.go @@ -5,6 +5,7 @@ import ( "net/http/httptest" "testing" + "github.com/anchore/clio" hashiVersion "github.com/anchore/go-version" "github.com/anchore/syft/cmd/syft/internal" ) @@ -106,7 +107,7 @@ func TestIsUpdateAvailable(t *testing.T) { t.Run(test.name, func(t *testing.T) { // setup mocks // local... - version := test.buildVersion + id := clio.Identification{Name: "Syft", Version: test.buildVersion} // remote... handler := http.NewServeMux() handler.HandleFunc(latestAppVersionURL.path, func(w http.ResponseWriter, r *http.Request) { @@ -117,7 +118,7 @@ func TestIsUpdateAvailable(t *testing.T) { latestAppVersionURL.host = mockSrv.URL defer mockSrv.Close() - isAvailable, newVersion, err := isUpdateAvailable(version) + isAvailable, newVersion, err := isUpdateAvailable(id) if err != nil && !test.err { t.Fatalf("got error but expected none: %+v", err) } else if err == nil && test.err { @@ -138,52 +139,67 @@ func TestIsUpdateAvailable(t *testing.T) { func TestFetchLatestApplicationVersion(t *testing.T) { tests := []struct { - name string - response string - code int - err bool - expected *hashiVersion.Version + name string + response string + code int + err bool + id clio.Identification + expected *hashiVersion.Version + expectedHeaders map[string]string }{ { - name: "gocase", - response: "1.0.0", - code: 200, - expected: hashiVersion.Must(hashiVersion.NewVersion("1.0.0")), + name: "gocase", + response: "1.0.0", + code: 200, + id: clio.Identification{Name: "Syft", Version: "0.0.0"}, + expected: hashiVersion.Must(hashiVersion.NewVersion("1.0.0")), + expectedHeaders: map[string]string{"User-Agent": "Syft 0.0.0"}, + err: false, }, { - name: "garbage", - response: "garbage", - code: 200, - expected: nil, - err: true, + name: "garbage", + response: "garbage", + code: 200, + id: clio.Identification{Name: "Syft", Version: "0.0.0"}, + expected: nil, + expectedHeaders: nil, + err: true, }, { - name: "http 500", - response: "1.0.0", - code: 500, - expected: nil, - err: true, + name: "http 500", + response: "1.0.0", + code: 500, + id: clio.Identification{Name: "Syft", Version: "0.0.0"}, + expected: nil, + expectedHeaders: nil, + err: true, }, { - name: "http 404", - response: "1.0.0", - code: 404, - expected: nil, - err: true, + name: "http 404", + response: "1.0.0", + code: 404, + id: clio.Identification{Name: "Syft", Version: "0.0.0"}, + expected: nil, + expectedHeaders: nil, + err: true, }, { - name: "empty", - response: "", - code: 200, - expected: nil, - err: true, + name: "empty", + response: "", + code: 200, + id: clio.Identification{Name: "Syft", Version: "0.0.0"}, + expected: nil, + expectedHeaders: nil, + err: true, }, { - name: "too long", - response: "this is really long this is really long this is really long this is really long this is really long this is really long this is really long this is really long ", - code: 200, - expected: nil, - err: true, + name: "too long", + response: "this is really long this is really long this is really long this is really long this is really long this is really long this is really long this is really long ", + code: 200, + id: clio.Identification{Name: "Syft", Version: "0.0.0"}, + expected: nil, + expectedHeaders: nil, + err: true, }, } @@ -192,6 +208,15 @@ func TestFetchLatestApplicationVersion(t *testing.T) { // setup mock handler := http.NewServeMux() handler.HandleFunc(latestAppVersionURL.path, func(w http.ResponseWriter, r *http.Request) { + if test.expectedHeaders != nil { + for headerName, headerValue := range test.expectedHeaders { + actualHeader := r.Header.Get(headerName) + if actualHeader != headerValue { + t.Fatalf("expected header %v=%v but got %v", headerName, headerValue, actualHeader) + } + } + } + w.WriteHeader(test.code) _, _ = w.Write([]byte(test.response)) }) @@ -199,7 +224,7 @@ func TestFetchLatestApplicationVersion(t *testing.T) { latestAppVersionURL.host = mockSrv.URL defer mockSrv.Close() - actual, err := fetchLatestApplicationVersion() + actual, err := fetchLatestApplicationVersion(test.id) if err != nil && !test.err { t.Fatalf("got error but expected none: %+v", err) } else if err == nil && test.err { From ce32f8bb74e487d6ac3e9f656ad7e3a4d3d2ff06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:26:56 -0400 Subject: [PATCH 036/106] chore(deps): bump golang.org/x/net from 0.14.0 to 0.15.0 (#2099) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.14.0 to 0.15.0. - [Commits](https://github.com/golang/net/compare/v0.14.0...v0.15.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 20 ++++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/go.mod b/go.mod index ce057da92..203334f03 100644 --- a/go.mod +++ b/go.mod @@ -76,11 +76,11 @@ require ( github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 - golang.org/x/crypto v0.12.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b golang.org/x/mod v0.12.0 - golang.org/x/net v0.14.0 - golang.org/x/term v0.11.0 + golang.org/x/net v0.15.0 + golang.org/x/term v0.12.0 gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.25.0 ) @@ -182,8 +182,8 @@ require ( github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect golang.org/x/sync v0.2.0 // indirect - golang.org/x/sys v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.13.0 // indirect golang.org/x/tools v0.9.1 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect diff --git a/go.sum b/go.sum index 341d07475..6bd5651e1 100644 --- a/go.sum +++ b/go.sum @@ -756,8 +756,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -849,8 +849,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -964,16 +964,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -986,8 +986,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 7645d5759d62253317be8d6df1aaf694f4362ed5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:27:21 -0400 Subject: [PATCH 037/106] chore(deps): bump github.com/docker/docker (#2098) Bumps [github.com/docker/docker](https://github.com/docker/docker) from 24.0.5+incompatible to 24.0.6+incompatible. - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v24.0.5...v24.0.6) --- updated-dependencies: - dependency-name: github.com/docker/docker dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 203334f03..0674d4f3b 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/dave/jennifer v1.7.0 github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da github.com/docker/distribution v2.8.2+incompatible - github.com/docker/docker v24.0.5+incompatible + github.com/docker/docker v24.0.6+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 github.com/github/go-spdx/v2 v2.1.2 diff --git a/go.sum b/go.sum index 6bd5651e1..3ed50c2a7 100644 --- a/go.sum +++ b/go.sum @@ -191,8 +191,8 @@ github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qe github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY= -github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= +github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= From 9caf51596ec5b738c7252430b776f434039cb4eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:29:06 -0400 Subject: [PATCH 038/106] chore(deps): bump github.com/saferwall/pe from 1.4.4 to 1.4.5 (#2096) Bumps [github.com/saferwall/pe](https://github.com/saferwall/pe) from 1.4.4 to 1.4.5. - [Release notes](https://github.com/saferwall/pe/releases) - [Changelog](https://github.com/saferwall/pe/blob/main/CHANGELOG.md) - [Commits](https://github.com/saferwall/pe/compare/v1.4.4...v1.4.5) --- updated-dependencies: - dependency-name: github.com/saferwall/pe dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0674d4f3b..37128e917 100644 --- a/go.mod +++ b/go.mod @@ -57,7 +57,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 github.com/pelletier/go-toml v1.9.5 github.com/pkg/errors v0.9.1 // indirect - github.com/saferwall/pe v1.4.4 + github.com/saferwall/pe v1.4.5 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/sassoftware/go-rpmutils v0.2.0 // pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5 diff --git a/go.sum b/go.sum index 3ed50c2a7..f3406d7a2 100644 --- a/go.sum +++ b/go.sum @@ -597,8 +597,8 @@ github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/f github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/saferwall/pe v1.4.4 h1:Ml++7/2/Z1iKwV4zCsd1nIqTEAdUQKAetwbbcCarhOg= -github.com/saferwall/pe v1.4.4/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s= +github.com/saferwall/pe v1.4.5 h1:ACIe9QnLTdiRIbuN3BbEUI8SqCQmNrPBb7O2lJTmsK4= +github.com/saferwall/pe v1.4.5/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= From 6800a5f64b0a3332f99948375602e9ff36afdacf Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:30:18 -0400 Subject: [PATCH 039/106] chore(deps): update CPE dictionary index (#2089) Signed-off-by: GitHub Co-authored-by: wagoodman --- syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json | 1 + 1 file changed, 1 insertion(+) diff --git a/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json b/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json index 7ffc387cc..a111bc0c3 100644 --- a/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json +++ b/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json @@ -1059,6 +1059,7 @@ "pipreqs": "cpe:2.3:a:pipreqs_project:pipreqs:*:*:*:*:*:python:*:*", "proxy.py": "cpe:2.3:a:proxy.py_project:proxy.py:*:*:*:*:*:*:*:*", "py-bcrypt": "cpe:2.3:a:python:py-bcrypt:*:*:*:*:*:*:*:*", + "py-xml": "cpe:2.3:a:py-xml_project:py-xml:*:*:*:*:*:python:*:*", "py7zr": "cpe:2.3:a:py7zr_project:py7zr:*:*:*:*:*:python:*:*", "pybluemonday": "cpe:2.3:a:python:pybluemonday:*:*:*:*:*:*:*:*", "pycryptodome": "cpe:2.3:a:python:pycryptodome:*:*:*:*:*:*:*:*", From 46e4ac147465bb19be2a05a9569fea374a277159 Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:30:44 -0400 Subject: [PATCH 040/106] chore(deps): update bootstrap tools to latest versions (#2086) Signed-off-by: GitHub Co-authored-by: spiffcs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 852269117..869357c25 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,7 @@ BOUNCER_VERSION := v0.4.0 CHRONICLE_VERSION := v0.7.0 GORELEASER_VERSION := v1.20.0 YAJSV_VERSION := v1.4.1 -COSIGN_VERSION := v2.1.1 +COSIGN_VERSION := v2.2.0 QUILL_VERSION := v0.4.1 GLOW_VERSION := v1.5.1 From 212aa9b6cf89cb85f8da568e53ab7a0e3ddf5ddb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:56:41 -0400 Subject: [PATCH 041/106] chore(deps): bump github.com/gkampitakis/go-snaps from 0.4.7 to 0.4.10 (#2106) Bumps [github.com/gkampitakis/go-snaps](https://github.com/gkampitakis/go-snaps) from 0.4.7 to 0.4.10. - [Release notes](https://github.com/gkampitakis/go-snaps/releases) - [Commits](https://github.com/gkampitakis/go-snaps/compare/v0.4.7...v0.4.10) --- updated-dependencies: - dependency-name: github.com/gkampitakis/go-snaps dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 15 ++++++++------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 37128e917..a0df5fd39 100644 --- a/go.mod +++ b/go.mod @@ -32,7 +32,7 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 github.com/github/go-spdx/v2 v2.1.2 - github.com/gkampitakis/go-snaps v0.4.7 + github.com/gkampitakis/go-snaps v0.4.10 github.com/go-git/go-billy/v5 v5.4.1 github.com/go-git/go-git/v5 v5.8.1 github.com/go-test/deep v1.1.0 @@ -115,7 +115,7 @@ require ( github.com/felixge/fgprof v0.9.3 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.0 // indirect - github.com/gkampitakis/ciinfo v0.2.4 // indirect + github.com/gkampitakis/ciinfo v0.2.5 // indirect github.com/gkampitakis/go-diff v1.3.2 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-restruct/restruct v1.2.0-alpha // indirect @@ -160,7 +160,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.2.0 // indirect - github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/skeema/knownhosts v1.2.0 // indirect github.com/spf13/cast v1.5.1 // indirect @@ -169,7 +169,7 @@ require ( github.com/sylabs/sif/v2 v2.11.5 // indirect github.com/sylabs/squashfs v0.6.1 // indirect github.com/therootcompany/xz v1.0.1 // indirect - github.com/tidwall/gjson v1.14.4 // indirect + github.com/tidwall/gjson v1.16.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect diff --git a/go.sum b/go.sum index f3406d7a2..12307b72c 100644 --- a/go.sum +++ b/go.sum @@ -239,12 +239,12 @@ github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmx github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/github/go-spdx/v2 v2.1.2 h1:p+Tv0yMgcuO0/vnMe9Qh4tmUgYhI6AsLVlakZ/Sx+DM= github.com/github/go-spdx/v2 v2.1.2/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w= -github.com/gkampitakis/ciinfo v0.2.4 h1:Ip1hf4K7ISRuVlDrheuhaeffg1VOhlyeFGaQ/vTxrtE= -github.com/gkampitakis/ciinfo v0.2.4/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= +github.com/gkampitakis/ciinfo v0.2.5 h1:K0mac90lGguc1conc46l0YEsB7/nioWCqSnJp/6z8Eo= +github.com/gkampitakis/ciinfo v0.2.5/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= -github.com/gkampitakis/go-snaps v0.4.7 h1:w1hOUdiKELxiTcRySQLmLuV3KW40mIRw3rzcYJGrb3I= -github.com/gkampitakis/go-snaps v0.4.7/go.mod h1:8HW4KX3JKV8M0GSw69CvT+Jqhd1AlBPMPpBfjBI3bdY= +github.com/gkampitakis/go-snaps v0.4.10 h1:rUcTH4k6+rzw6ylDALMifzw2c/f9cG3NZe/n+7Ygdr4= +github.com/gkampitakis/go-snaps v0.4.10/go.mod h1:N4TpqxI4CqKUfHzDFqrqZ5UP0I0ESz2g2NMslh7MiJw= github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= @@ -592,8 +592,9 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -673,8 +674,8 @@ github.com/sylabs/squashfs v0.6.1/go.mod h1:ZwpbPCj0ocIvMy2br6KZmix6Gzh6fsGQcCny github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM= -github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg= +github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= From 1315cfd787c3258911afdb88b6d190fd91a949f0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Sep 2023 09:57:51 -0400 Subject: [PATCH 042/106] chore(deps): bump actions/checkout from 3 to 4 (#2094) Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/benchmark-testing.yaml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/release.yaml | 4 ++-- .github/workflows/update-bootstrap-tools.yml | 2 +- .github/workflows/update-cpe-dictionary-index.yml | 2 +- .github/workflows/update-stereoscope-release.yml | 2 +- .github/workflows/validations.yaml | 14 +++++++------- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/benchmark-testing.yaml b/.github/workflows/benchmark-testing.yaml index d581850d8..50f5d390b 100644 --- a/.github/workflows/benchmark-testing.yaml +++ b/.github/workflows/benchmark-testing.yaml @@ -13,7 +13,7 @@ jobs: # we also want to run on push such that merges to main are recorded to the cache. For this reason we don't filter # the job by event. steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Bootstrap environment uses: ./.github/actions/bootstrap diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index c6ac1cdb1..143b84953 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -30,7 +30,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a2cbd05ac..9ef4cb932 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -14,7 +14,7 @@ jobs: environment: release runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Check if tag already exists # note: this will fail if the tag already exists @@ -94,7 +94,7 @@ jobs: contents: write packages: write steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 diff --git a/.github/workflows/update-bootstrap-tools.yml b/.github/workflows/update-bootstrap-tools.yml index 8a21df672..37316484d 100644 --- a/.github/workflows/update-bootstrap-tools.yml +++ b/.github/workflows/update-bootstrap-tools.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'anchore/syft' # only run for main repo steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: diff --git a/.github/workflows/update-cpe-dictionary-index.yml b/.github/workflows/update-cpe-dictionary-index.yml index c7541bbae..e748772be 100644 --- a/.github/workflows/update-cpe-dictionary-index.yml +++ b/.github/workflows/update-cpe-dictionary-index.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'anchore/syft' # only run for main repo steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: diff --git a/.github/workflows/update-stereoscope-release.yml b/.github/workflows/update-stereoscope-release.yml index f2d09c238..d86273ce8 100644 --- a/.github/workflows/update-stereoscope-release.yml +++ b/.github/workflows/update-stereoscope-release.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'anchore/syft' # only run for main repo steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-go@v4 with: diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 9ddb8c965..36502c133 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -14,7 +14,7 @@ jobs: name: "Static analysis" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -28,7 +28,7 @@ jobs: name: "Unit tests" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -72,7 +72,7 @@ jobs: name: "Integration tests" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -94,7 +94,7 @@ jobs: name: "Build snapshot artifacts" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -125,7 +125,7 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download snapshot build uses: actions/cache/restore@v3 @@ -161,7 +161,7 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download snapshot build uses: actions/cache/restore@v3 @@ -189,7 +189,7 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Bootstrap environment uses: ./.github/actions/bootstrap From 9f22ab6137cb72e7d2e36ad25887e5f974fe0a57 Mon Sep 17 00:00:00 2001 From: dlorenc Date: Thu, 7 Sep 2023 14:55:52 -0400 Subject: [PATCH 043/106] Bump the golang.org/x/exp dependency and fix a build breakage. (#2088) * Bump the golang.org/x/exp dependency and fix a build breakage. --------- Signed-off-by: Dan Lorenc Signed-off-by: Christopher Phillips Co-authored-by: Christopher Phillips --- .github/actions/bootstrap/action.yaml | 4 +-- .github/workflows/release.yaml | 2 +- .github/workflows/update-bootstrap-tools.yml | 2 +- .../workflows/update-cpe-dictionary-index.yml | 2 +- .../workflows/update-stereoscope-release.yml | 2 +- go.mod | 10 ++++--- go.sum | 30 +++++++++++++++---- .../formats/common/cyclonedxhelpers/format.go | 5 ++-- 8 files changed, 39 insertions(+), 18 deletions(-) diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index 10ef12956..b294c90ae 100644 --- a/.github/actions/bootstrap/action.yaml +++ b/.github/actions/bootstrap/action.yaml @@ -4,7 +4,7 @@ inputs: go-version: description: "Go version to install" required: true - default: "1.20.x" + default: "1.21.x" use-go-cache: description: "Restore go cache" required: true @@ -24,7 +24,7 @@ inputs: runs: using: "composite" steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: go-version: ${{ inputs.go-version }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9ef4cb932..9b83238f9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -7,7 +7,7 @@ on: required: true env: - GO_VERSION: "1.19.x" + GO_VERSION: "1.21.x" jobs: quality-gate: diff --git a/.github/workflows/update-bootstrap-tools.yml b/.github/workflows/update-bootstrap-tools.yml index 37316484d..d0cc1821c 100644 --- a/.github/workflows/update-bootstrap-tools.yml +++ b/.github/workflows/update-bootstrap-tools.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: env: - GO_VERSION: "1.20.x" + GO_VERSION: "1.21.x" GO_STABLE_VERSION: true jobs: diff --git a/.github/workflows/update-cpe-dictionary-index.yml b/.github/workflows/update-cpe-dictionary-index.yml index e748772be..2c1a3bbd0 100644 --- a/.github/workflows/update-cpe-dictionary-index.yml +++ b/.github/workflows/update-cpe-dictionary-index.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: env: - GO_VERSION: "1.20.x" + GO_VERSION: "1.21.x" GO_STABLE_VERSION: true jobs: diff --git a/.github/workflows/update-stereoscope-release.yml b/.github/workflows/update-stereoscope-release.yml index d86273ce8..482aedcc4 100644 --- a/.github/workflows/update-stereoscope-release.yml +++ b/.github/workflows/update-stereoscope-release.yml @@ -6,7 +6,7 @@ on: workflow_dispatch: env: - GO_VERSION: "1.20.x" + GO_VERSION: "1.21.x" GO_STABLE_VERSION: true jobs: diff --git a/go.mod b/go.mod index a0df5fd39..c3ed52738 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,8 @@ module github.com/anchore/syft -go 1.19 +go 1.21 + +toolchain go1.21.0 require ( github.com/CycloneDX/cyclonedx-go v0.7.1 @@ -77,7 +79,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 golang.org/x/crypto v0.13.0 // indirect - golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b + golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 golang.org/x/mod v0.12.0 golang.org/x/net v0.15.0 golang.org/x/term v0.12.0 @@ -181,10 +183,10 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect - golang.org/x/sync v0.2.0 // indirect + golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.9.1 // indirect + golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.55.0 // indirect diff --git a/go.sum b/go.sum index 12307b72c..5805cbb94 100644 --- a/go.sum +++ b/go.sum @@ -113,6 +113,7 @@ github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= +github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 h1:vmXNl+HDfqqXgr0uY1UgK1GAhps8nbAAtqHNBcgyf+4= github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46/go.mod h1:olhPNdiiAAMiSujemd1O/sc6GcyePr23f/6uGKtthNg= @@ -124,6 +125,7 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA= @@ -135,6 +137,7 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc= github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= +github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= @@ -207,6 +210,7 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= +github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -231,6 +235,7 @@ github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0C github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= @@ -246,12 +251,15 @@ github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6O github.com/gkampitakis/go-snaps v0.4.10 h1:rUcTH4k6+rzw6ylDALMifzw2c/f9cG3NZe/n+7Ygdr4= github.com/gkampitakis/go-snaps v0.4.10/go.mod h1:N4TpqxI4CqKUfHzDFqrqZ5UP0I0ESz2g2NMslh7MiJw= github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= +github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= +github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= +github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -493,6 +501,7 @@ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= @@ -521,12 +530,14 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= +github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -609,6 +620,7 @@ github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvv github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= +github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= @@ -769,8 +781,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b h1:EqBVA+nNsObCwQoBEHy4wLU0pi7i8a4AL3pbItPdPkE= -golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -882,8 +894,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI= -golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -993,6 +1005,7 @@ golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -1051,8 +1064,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= -golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= +golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1235,6 +1248,7 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1249,7 +1263,9 @@ modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM= modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak= modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ= @@ -1263,9 +1279,11 @@ modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= +modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= +modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/syft/formats/common/cyclonedxhelpers/format.go b/syft/formats/common/cyclonedxhelpers/format.go index 1712e0f49..4c06a4333 100644 --- a/syft/formats/common/cyclonedxhelpers/format.go +++ b/syft/formats/common/cyclonedxhelpers/format.go @@ -1,6 +1,7 @@ package cyclonedxhelpers import ( + "strings" "time" "github.com/CycloneDX/cyclonedx-go" @@ -182,8 +183,8 @@ func toDependencies(relationships []artifact.Relationship) []cyclonedx.Dependenc result = append(result, *dep) } - slices.SortFunc(result, func(a, b cyclonedx.Dependency) bool { - return a.Ref < b.Ref + slices.SortFunc(result, func(a, b cyclonedx.Dependency) int { + return strings.Compare(a.Ref, b.Ref) }) return result From 3842d28e9007215b2598cc624518fbfc7e109d41 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Thu, 7 Sep 2023 15:54:42 -0400 Subject: [PATCH 044/106] fix: update codeql-analysis for go 1.21 (#2108) * fix: update codeql-analysis for go 1.21 Signed-off-by: Christopher Phillips * nit: remove comment Signed-off-by: Christopher Phillips --------- Signed-off-by: Christopher Phillips --- .github/workflows/codeql-analysis.yml | 5 +++++ go.mod | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 143b84953..0b2d988fa 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -32,6 +32,11 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Install Go + uses: actions/setup-go@v4 + with: + go-version-file: go.mod + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/go.mod b/go.mod index c3ed52738..5966ead89 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,6 @@ module github.com/anchore/syft -go 1.21 - -toolchain go1.21.0 +go 1.21.0 require ( github.com/CycloneDX/cyclonedx-go v0.7.1 From e3c525b4b8d8808a99907907334733db9faa3b52 Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Fri, 8 Sep 2023 14:10:00 -0400 Subject: [PATCH 045/106] chore(deps): update stereoscope to 057dda3667e7f2b5e6ec6716747badd5f403c6de (#2109) Signed-off-by: GitHub Co-authored-by: kzantow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5966ead89..18980352f 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ 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.1.1-0.20230104203445-02e0a6721501 - github.com/anchore/stereoscope v0.0.0-20230829142608-334c2222e137 + github.com/anchore/stereoscope v0.0.0-20230907152702-057dda3667e7 // 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 diff --git a/go.sum b/go.sum index 5805cbb94..4bc3adbd2 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,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.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230829142608-334c2222e137 h1:ZuiAV3lYKbGPkZR42UpoKecp/9aFU38CZSjaxjXCMYc= -github.com/anchore/stereoscope v0.0.0-20230829142608-334c2222e137/go.mod h1:jkylrnuv++srUa2rsqCnG0n1ShD+0k2Xa6YtoNoRjAY= +github.com/anchore/stereoscope v0.0.0-20230907152702-057dda3667e7 h1:mbYk+Cvvlfr/kRUz7LqATmjOT4+mV0YufNtj3aqU2fw= +github.com/anchore/stereoscope v0.0.0-20230907152702-057dda3667e7/go.mod h1:bEbCTAbTx/Ot+kSWVfXgInyU+88+0gP+G8L+LPgrunw= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= From ec22f4b773f55ca5c4dd05bc0727b8ecbc28f438 Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:42:59 -0400 Subject: [PATCH 046/106] chore(deps): update CPE dictionary index (#2114) Signed-off-by: GitHub Co-authored-by: wagoodman --- syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json | 1 + 1 file changed, 1 insertion(+) diff --git a/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json b/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json index a111bc0c3..4d021dc1e 100644 --- a/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json +++ b/syft/pkg/cataloger/common/cpe/dictionary/data/cpe-index.json @@ -475,6 +475,7 @@ "fast-csv": "cpe:2.3:a:c2fo:fast-csv:*:*:*:*:*:node.js:*:*", "fast-http": "cpe:2.3:a:fast-http_project:fast-http:*:*:*:*:*:node.js:*:*", "fast-http-cli": "cpe:2.3:a:fast-http-cli_project:fast-http-cli:*:*:*:*:*:node.js:*:*", + "fastest-json-copy": "cpe:2.3:a:fastest-json-copy_project:fastest-json-copy:*:*:*:*:*:node.js:*:*", "ffmepg": "cpe:2.3:a:ffmepg_project:ffmepg:*:*:*:*:*:node.js:*:*", "ffmpegdotjs": "cpe:2.3:a:ffmpegdotjs_project:ffmpegdotjs:*:*:*:*:*:node.js:*:*", "fibjs": "cpe:2.3:a:fibjs_project:fibjs:*:*:*:*:*:node.js:*:*", From b2be411f77c73b031383fcaa5d2d9e7a8266ce7b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Sep 2023 09:56:22 -0400 Subject: [PATCH 047/106] chore(deps): bump tibdex/github-app-token from 1 to 2 (#2116) Bumps [tibdex/github-app-token](https://github.com/tibdex/github-app-token) from 1 to 2. - [Release notes](https://github.com/tibdex/github-app-token/releases) - [Commits](https://github.com/tibdex/github-app-token/compare/v1...v2) --- updated-dependencies: - dependency-name: tibdex/github-app-token dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/update-bootstrap-tools.yml | 2 +- .github/workflows/update-cpe-dictionary-index.yml | 2 +- .github/workflows/update-stereoscope-release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update-bootstrap-tools.yml b/.github/workflows/update-bootstrap-tools.yml index d0cc1821c..bdf133179 100644 --- a/.github/workflows/update-bootstrap-tools.yml +++ b/.github/workflows/update-bootstrap-tools.yml @@ -55,7 +55,7 @@ jobs: echo "GLOW=GLOW_LATEST_VERSION" >> $GITHUB_OUTPUT id: latest-versions - - uses: tibdex/github-app-token@v1 + - uses: tibdex/github-app-token@v2 id: generate-token with: app_id: ${{ secrets.TOKEN_APP_ID }} diff --git a/.github/workflows/update-cpe-dictionary-index.yml b/.github/workflows/update-cpe-dictionary-index.yml index 2c1a3bbd0..fb1659293 100644 --- a/.github/workflows/update-cpe-dictionary-index.yml +++ b/.github/workflows/update-cpe-dictionary-index.yml @@ -24,7 +24,7 @@ jobs: - run: | make generate-cpe-dictionary-index - - uses: tibdex/github-app-token@v1 + - uses: tibdex/github-app-token@v2 id: generate-token with: app_id: ${{ secrets.TOKEN_APP_ID }} diff --git a/.github/workflows/update-stereoscope-release.yml b/.github/workflows/update-stereoscope-release.yml index 482aedcc4..bd4e74b5c 100644 --- a/.github/workflows/update-stereoscope-release.yml +++ b/.github/workflows/update-stereoscope-release.yml @@ -32,7 +32,7 @@ jobs: echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_OUTPUT id: latest-version - - uses: tibdex/github-app-token@v1 + - uses: tibdex/github-app-token@v2 id: generate-token with: app_id: ${{ secrets.TOKEN_APP_ID }} From b82c0ffc3417bdc8c38f4633af95a668ec29fa35 Mon Sep 17 00:00:00 2001 From: GGMU <49076226+tomersein@users.noreply.github.com> Date: Mon, 11 Sep 2023 19:12:04 +0300 Subject: [PATCH 048/106] fix(help): power-user help text to indicate it supports file-system (#2113) Signed-off-by: tomersein --- cmd/syft/cli/commands/poweruser.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/syft/cli/commands/poweruser.go b/cmd/syft/cli/commands/poweruser.go index 2c9ff185a..b8776e56c 100644 --- a/cmd/syft/cli/commands/poweruser.go +++ b/cmd/syft/cli/commands/poweruser.go @@ -21,7 +21,7 @@ import ( const powerUserExample = ` {{.appName}} {{.command}} DEPRECATED - THIS COMMAND WILL BE REMOVED in v1.0.0 - Only image sources are supported (e.g. docker: , podman: , docker-archive: , oci: , etc.), the directory source (dir:) is not supported, template outputs are not supported. + Template outputs are not supported. All behavior is controlled via application configuration and environment variables (see https://github.com/anchore/syft#configuration) ` From 3a45653cfafa7310039023b3257905feed5c8a61 Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Tue, 12 Sep 2023 11:49:20 -0400 Subject: [PATCH 049/106] chore(deps): update stereoscope to 2fc2d6c2503b6e2212e04c64ceffd57c3395ae70 (#2117) Signed-off-by: GitHub Co-authored-by: kzantow --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 18980352f..37c1cedcb 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ 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.1.1-0.20230104203445-02e0a6721501 - github.com/anchore/stereoscope v0.0.0-20230907152702-057dda3667e7 + github.com/anchore/stereoscope v0.0.0-20230911191510-2fc2d6c2503b // 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 @@ -73,7 +73,7 @@ require ( github.com/vbatts/go-mtree v0.5.3 github.com/vifraa/gopom v1.0.0 github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 - github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 + github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 golang.org/x/crypto v0.13.0 // indirect diff --git a/go.sum b/go.sum index 4bc3adbd2..93eb7594d 100644 --- a/go.sum +++ b/go.sum @@ -106,8 +106,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.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230907152702-057dda3667e7 h1:mbYk+Cvvlfr/kRUz7LqATmjOT4+mV0YufNtj3aqU2fw= -github.com/anchore/stereoscope v0.0.0-20230907152702-057dda3667e7/go.mod h1:bEbCTAbTx/Ot+kSWVfXgInyU+88+0gP+G8L+LPgrunw= +github.com/anchore/stereoscope v0.0.0-20230911191510-2fc2d6c2503b h1:TFqPfhf7Q+59gW1RuZixnEhh8lYY/FdqTRpSHYKcOsw= +github.com/anchore/stereoscope v0.0.0-20230911191510-2fc2d6c2503b/go.mod h1:CpgmJLpFsMBSq4WnWpBIfk2etTvv/jx00q15MWnzKDY= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= @@ -710,8 +710,8 @@ github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0= github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20= -github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 h1:lwgTsTy18nYqASnH58qyfRW/ldj7Gt2zzBvgYPzdA4s= -github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA= +github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c h1:mM8T8YhiD19d2wYv3vqZn8xpe1ZFJrUJCGlK4IV05xM= +github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= From 9de4129638a60dbef0c87b964bdcee561b0ee12a Mon Sep 17 00:00:00 2001 From: William Murphy Date: Tue, 12 Sep 2023 13:09:18 -0400 Subject: [PATCH 050/106] chore: enforce race detector (#2122) Previously, there were some data races in syft. Right now, none are detected, so check for data races on the overall command, and on unit tests. (Checking for races on integration tests triples the time needed for those tests, from ~1 minute to ~3 minutes on my workstation, so that was not done at this time.) Signed-off-by: Will Murphy --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 869357c25..edd3732af 100644 --- a/Makefile +++ b/Makefile @@ -147,13 +147,14 @@ check-json-schema-drift: .PHONY: unit unit: $(TEMP_DIR) fixtures ## Run unit tests (with coverage) $(call title,Running unit tests) - go test -coverprofile $(TEMP_DIR)/unit-coverage-details.txt $(shell go list ./... | grep -v anchore/syft/test) + go test -race -coverprofile $(TEMP_DIR)/unit-coverage-details.txt $(shell go list ./... | grep -v anchore/syft/test) @.github/scripts/coverage.py $(COVERAGE_THRESHOLD) $(TEMP_DIR)/unit-coverage-details.txt .PHONY: integration integration: ## Run integration tests $(call title,Running integration tests) go test -v ./test/integration + go run -race cmd/syft/main.go alpine:latest .PHONY: validate-cyclonedx-schema validate-cyclonedx-schema: From 7de56432270dc6cda97b6791793669dfa3fbd9fd Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 13 Sep 2023 10:05:24 -0400 Subject: [PATCH 051/106] fix source-version typo in flag description (#2126) Signed-off-by: Alex Goodman --- cmd/syft/cli/options/catalog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/syft/cli/options/catalog.go b/cmd/syft/cli/options/catalog.go index 50f4142d5..a9abf8760 100644 --- a/cmd/syft/cli/options/catalog.go +++ b/cmd/syft/cli/options/catalog.go @@ -88,7 +88,7 @@ func (cfg *Catalog) AddFlags(flags clio.FlagSet) { "set the name of the target being analyzed") flags.StringVarP(&cfg.Source.Version, "source-version", "", - "set the name of the target being analyzed") + "set the version of the target being analyzed") flags.StringVarP(&cfg.BasePath, "base-path", "", "base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory") From 4a2fc226ddb9d6ae51350b5084933698d9716828 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:33:47 -0400 Subject: [PATCH 052/106] chore(deps): bump github.com/go-git/go-git/v5 from 5.8.1 to 5.9.0 (#2125) Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.8.1 to 5.9.0. - [Release notes](https://github.com/go-git/go-git/releases) - [Commits](https://github.com/go-git/go-git/compare/v5.8.1...v5.9.0) --- updated-dependencies: - dependency-name: github.com/go-git/go-git/v5 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 9 +++++---- go.sum | 26 ++++++++++++++------------ 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/go.mod b/go.mod index 37c1cedcb..be85b963a 100644 --- a/go.mod +++ b/go.mod @@ -33,8 +33,8 @@ require ( github.com/facebookincubator/nvdtools v0.1.5 github.com/github/go-spdx/v2 v2.1.2 github.com/gkampitakis/go-snaps v0.4.10 - github.com/go-git/go-billy/v5 v5.4.1 - github.com/go-git/go-git/v5 v5.8.1 + github.com/go-git/go-billy/v5 v5.5.0 + github.com/go-git/go-git/v5 v5.9.0 github.com/go-test/deep v1.1.0 github.com/google/go-cmp v0.5.9 github.com/google/go-containerregistry v0.16.1 @@ -92,7 +92,7 @@ require ( github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect + github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect @@ -104,6 +104,7 @@ require ( github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/containerd/containerd v1.7.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect + github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v24.0.0+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect @@ -184,7 +185,7 @@ require ( golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 // indirect + golang.org/x/tools v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect google.golang.org/grpc v1.55.0 // indirect diff --git a/go.sum b/go.sum index 93eb7594d..cd60056b9 100644 --- a/go.sum +++ b/go.sum @@ -74,8 +74,8 @@ github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= +github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE= @@ -182,6 +182,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:ma github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= +github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE= github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -209,8 +211,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= -github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0= -github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU= +github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -256,12 +258,12 @@ github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI= github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic= -github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4= -github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg= +github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU= +github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo= -github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A= -github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo= +github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY= +github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -461,7 +463,6 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -547,11 +548,12 @@ github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKt github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ= github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= +github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= @@ -1064,8 +1066,8 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846 h1:Vve/L0v7CXXuxUmaMGIEK/dEeq7uiqb5qBgQrZzIE7E= -golang.org/x/tools v0.12.1-0.20230815132531-74c255bcf846/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= +golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From c21b16d9248f00a3ae439d47c9384c55c815dc90 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:34:19 -0400 Subject: [PATCH 053/106] chore(deps): bump docker/login-action from 2 to 3 (#2119) Bumps [docker/login-action](https://github.com/docker/login-action) from 2 to 3. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/v2...v3) --- updated-dependencies: - dependency-name: docker/login-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9b83238f9..a76247ccf 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -105,13 +105,13 @@ jobs: build-cache-key-prefix: "snapshot" - name: Login to Docker Hub - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: username: ${{ secrets.TOOLBOX_DOCKER_USER }} password: ${{ secrets.TOOLBOX_DOCKER_PASS }} - name: Login to GitHub Container Registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} From 5035d9ca1a76e163f32f0700b79a538c53b08d7c Mon Sep 17 00:00:00 2001 From: Ahmet Taha Date: Wed, 13 Sep 2023 19:14:14 +0200 Subject: [PATCH 054/106] fix: allow cyclonedx json input with no components (#2127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ahmet Taha Özdemir --- syft/formats/common/cyclonedxhelpers/decoder.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/syft/formats/common/cyclonedxhelpers/decoder.go b/syft/formats/common/cyclonedxhelpers/decoder.go index 741e51ea5..3400cf9ef 100644 --- a/syft/formats/common/cyclonedxhelpers/decoder.go +++ b/syft/formats/common/cyclonedxhelpers/decoder.go @@ -27,7 +27,8 @@ func GetValidator(format cyclonedx.BOMFileFormat) sbom.Validator { } xmlWithoutNS := format == cyclonedx.BOMFileFormatXML && !strings.Contains(bom.XMLNS, cycloneDXXmlSchema) - if (cyclonedx.BOM{} == *bom || bom.Components == nil || xmlWithoutNS) { + xmlWithoutComponents := format == cyclonedx.BOMFileFormatXML && bom.Components == nil + if (cyclonedx.BOM{} == *bom || xmlWithoutComponents || xmlWithoutNS) { return fmt.Errorf("not a valid CycloneDX document") } return nil From 3e16c6813ffdaa12b6130565d80af132112110c4 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Wed, 13 Sep 2023 14:50:22 -0400 Subject: [PATCH 055/106] feat: add cyclonedx schema version selection (#2123) --------- Signed-off-by: Christopher Phillips --- go.mod | 2 +- go.sum | 6 ++- schema/cyclonedx/README.md | 2 +- syft/formats/cyclonedxjson/encoder.go | 35 ++++++++++++-- syft/formats/cyclonedxjson/format.go | 58 +++++++++++++++++++++-- syft/formats/cyclonedxjson/format_test.go | 34 +++++++++++++ syft/formats/cyclonedxxml/encoder.go | 37 +++++++++++++-- syft/formats/cyclonedxxml/format.go | 58 +++++++++++++++++++++-- syft/formats/formats.go | 32 ++++++++++--- syft/formats/formats_test.go | 1 - syft/formats/syftjson/format_test.go | 33 +++++++++++++ 11 files changed, 274 insertions(+), 24 deletions(-) create mode 100644 syft/formats/cyclonedxjson/format_test.go create mode 100644 syft/formats/syftjson/format_test.go diff --git a/go.mod b/go.mod index be85b963a..f8a3ef97f 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/anchore/syft go 1.21.0 require ( - github.com/CycloneDX/cyclonedx-go v0.7.1 + github.com/CycloneDX/cyclonedx-go v0.7.2 github.com/Masterminds/semver v1.5.0 github.com/Masterminds/sprig/v3 v3.2.3 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d diff --git a/go.sum b/go.sum index cd60056b9..4039a4a91 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.2.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.7.1 h1:5w1SxjGm9MTMNTuRbEPyw21ObdbaagTWF/KfF0qHTRE= -github.com/CycloneDX/cyclonedx-go v0.7.1/go.mod h1:N/nrdWQI2SIjaACyyDs/u7+ddCkyl/zkNs8xFsHF2Ps= +github.com/CycloneDX/cyclonedx-go v0.7.2 h1:kKQ0t1dPOlugSIYVOMiMtFqeXI2wp/f5DBIdfux8gnQ= +github.com/CycloneDX/cyclonedx-go v0.7.2/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk= github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= @@ -685,6 +685,8 @@ github.com/sylabs/sif/v2 v2.11.5 h1:7ssPH3epSonsTrzbS1YxeJ9KuqAN7ISlSM61a7j/mQM= github.com/sylabs/sif/v2 v2.11.5/go.mod h1:GBoZs9LU3e4yJH1dcZ3Akf/jsqYgy5SeguJQC+zd75Y= github.com/sylabs/squashfs v0.6.1 h1:4hgvHnD9JGlYWwT0bPYNt9zaz23mAV3Js+VEgQoRGYQ= github.com/sylabs/squashfs v0.6.1/go.mod h1:ZwpbPCj0ocIvMy2br6KZmix6Gzh6fsGQcCnydMF+Kx8= +github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo= +github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= diff --git a/schema/cyclonedx/README.md b/schema/cyclonedx/README.md index 200d6393e..b9278db66 100644 --- a/schema/cyclonedx/README.md +++ b/schema/cyclonedx/README.md @@ -1,6 +1,6 @@ # CycloneDX Schemas -`syft` generates a CycloneDX BOm output. We want to be able to validate the CycloneDX schemas +`syft` generates a CycloneDX Bom output. We want to be able to validate the CycloneDX schemas (and dependent schemas) against generated syft output. The best way to do this is with `xmllint`, however, this tool does not know how to deal with references from HTTP, only the local filesystem. For this reason we've included a copy of all schemas needed to validate `syft` output, modified diff --git a/syft/formats/cyclonedxjson/encoder.go b/syft/formats/cyclonedxjson/encoder.go index 13ad32f68..297b80265 100644 --- a/syft/formats/cyclonedxjson/encoder.go +++ b/syft/formats/cyclonedxjson/encoder.go @@ -9,11 +9,40 @@ import ( "github.com/anchore/syft/syft/sbom" ) -func encoder(output io.Writer, s sbom.SBOM) error { +func encoderV1_0(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_0) +} + +func encoderV1_1(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_1) +} + +func encoderV1_2(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_2) +} + +func encoderV1_3(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_3) +} + +func encoderV1_4(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_4) +} + +func encoderV1_5(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_5) +} + +func buildEncoder(output io.Writer, s sbom.SBOM) (cyclonedx.BOMEncoder, *cyclonedx.BOM) { bom := cyclonedxhelpers.ToFormatModel(s) enc := cyclonedx.NewBOMEncoder(output, cyclonedx.BOMFileFormatJSON) enc.SetPretty(true) enc.SetEscapeHTML(false) - err := enc.Encode(bom) - return err + return enc, bom } diff --git a/syft/formats/cyclonedxjson/format.go b/syft/formats/cyclonedxjson/format.go index 8c1e2e016..97a088aae 100644 --- a/syft/formats/cyclonedxjson/format.go +++ b/syft/formats/cyclonedxjson/format.go @@ -9,10 +9,62 @@ import ( const ID sbom.FormatID = "cyclonedx-json" -func Format() sbom.Format { +var Format = Format1_4 + +func Format1_0() sbom.Format { return sbom.NewFormat( - sbom.AnyVersion, - encoder, + cyclonedx.SpecVersion1_0.String(), + encoderV1_0, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON), + ID, + ) +} + +func Format1_1() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_1.String(), + encoderV1_1, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON), + ID, + ) +} + +func Format1_2() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_2.String(), + encoderV1_2, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON), + ID, + ) +} + +func Format1_3() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_3.String(), + encoderV1_3, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON), + ID, + ) +} + +func Format1_4() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_4.String(), + encoderV1_4, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON), + ID, + ) +} + +func Format1_5() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_5.String(), + encoderV1_5, cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatJSON), cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatJSON), ID, diff --git a/syft/formats/cyclonedxjson/format_test.go b/syft/formats/cyclonedxjson/format_test.go new file mode 100644 index 000000000..2cfd5e545 --- /dev/null +++ b/syft/formats/cyclonedxjson/format_test.go @@ -0,0 +1,34 @@ +package cyclonedxjson + +import ( + "testing" + + "github.com/CycloneDX/cyclonedx-go" +) + +func TestFormatVersions(t *testing.T) { + tests := []struct { + name string + expectedVersion string + }{ + { + + "cyclonedx-json should default to v1.4", + cyclonedx.SpecVersion1_4.String(), + }, + } + + for _, c := range tests { + c := c + t.Run(c.name, func(t *testing.T) { + sbomFormat := Format() + if sbomFormat.ID() != ID { + t.Errorf("expected ID %q, got %q", ID, sbomFormat.ID()) + } + + if sbomFormat.Version() != c.expectedVersion { + t.Errorf("expected version %q, got %q", c.expectedVersion, sbomFormat.Version()) + } + }) + } +} diff --git a/syft/formats/cyclonedxxml/encoder.go b/syft/formats/cyclonedxxml/encoder.go index b8abdf81a..3941feca0 100644 --- a/syft/formats/cyclonedxxml/encoder.go +++ b/syft/formats/cyclonedxxml/encoder.go @@ -9,11 +9,40 @@ import ( "github.com/anchore/syft/syft/sbom" ) -func encoder(output io.Writer, s sbom.SBOM) error { +func encoderV1_0(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_0) +} + +func encoderV1_1(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_1) +} + +func encoderV1_2(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_2) +} + +func encoderV1_3(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_3) +} + +func encoderV1_4(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_4) +} + +func encoderV1_5(output io.Writer, s sbom.SBOM) error { + enc, bom := buildEncoder(output, s) + return enc.EncodeVersion(bom, cyclonedx.SpecVersion1_5) +} + +func buildEncoder(output io.Writer, s sbom.SBOM) (cyclonedx.BOMEncoder, *cyclonedx.BOM) { bom := cyclonedxhelpers.ToFormatModel(s) enc := cyclonedx.NewBOMEncoder(output, cyclonedx.BOMFileFormatXML) enc.SetPretty(true) - - err := enc.Encode(bom) - return err + enc.SetEscapeHTML(false) + return enc, bom } diff --git a/syft/formats/cyclonedxxml/format.go b/syft/formats/cyclonedxxml/format.go index 7fe53c4f7..1b22cee14 100644 --- a/syft/formats/cyclonedxxml/format.go +++ b/syft/formats/cyclonedxxml/format.go @@ -9,10 +9,62 @@ import ( const ID sbom.FormatID = "cyclonedx-xml" -func Format() sbom.Format { +var Format = Format1_4 + +func Format1_0() sbom.Format { return sbom.NewFormat( - sbom.AnyVersion, - encoder, + cyclonedx.SpecVersion1_0.String(), + encoderV1_0, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML), + ID, "cyclonedx", "cyclone", + ) +} + +func Format1_1() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_1.String(), + encoderV1_1, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML), + ID, "cyclonedx", "cyclone", + ) +} + +func Format1_2() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_2.String(), + encoderV1_2, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML), + ID, "cyclonedx", "cyclone", + ) +} + +func Format1_3() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_3.String(), + encoderV1_3, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML), + ID, "cyclonedx", "cyclone", + ) +} + +func Format1_4() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_4.String(), + encoderV1_4, + cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML), + cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML), + ID, "cyclonedx", "cyclone", + ) +} + +func Format1_5() sbom.Format { + return sbom.NewFormat( + cyclonedx.SpecVersion1_5.String(), + encoderV1_5, cyclonedxhelpers.GetDecoder(cyclonedx.BOMFileFormatXML), cyclonedxhelpers.GetValidator(cyclonedx.BOMFileFormatXML), ID, "cyclonedx", "cyclone", diff --git a/syft/formats/formats.go b/syft/formats/formats.go index 13af6cf05..b61027ed4 100644 --- a/syft/formats/formats.go +++ b/syft/formats/formats.go @@ -26,17 +26,27 @@ import ( func Formats() []sbom.Format { return []sbom.Format{ syftjson.Format(), - cyclonedxxml.Format(), - cyclonedxjson.Format(), github.Format(), + table.Format(), + text.Format(), + template.Format(), + cyclonedxxml.Format1_0(), + cyclonedxxml.Format1_1(), + cyclonedxxml.Format1_2(), + cyclonedxxml.Format1_3(), + cyclonedxxml.Format1_4(), + cyclonedxxml.Format1_5(), + cyclonedxjson.Format1_0(), + cyclonedxjson.Format1_1(), + cyclonedxjson.Format1_2(), + cyclonedxjson.Format1_3(), + cyclonedxjson.Format1_4(), + cyclonedxjson.Format1_5(), spdxtagvalue.Format2_1(), spdxtagvalue.Format2_2(), spdxtagvalue.Format2_3(), spdxjson.Format2_2(), spdxjson.Format2_3(), - table.Format(), - text.Format(), - template.Format(), } } @@ -55,7 +65,7 @@ func Identify(by []byte) sbom.Format { // ByName accepts a name@version string, such as: // -// spdx-json@2.1 or cyclonedx@2 +// spdx-json@2.1 or cyclonedx@1.5 func ByName(name string) sbom.Format { parts := strings.SplitN(name, "@", 2) version := sbom.AnyVersion @@ -71,6 +81,16 @@ func ByNameAndVersion(name string, version string) sbom.Format { for _, f := range Formats() { for _, n := range f.IDs() { if cleanFormatName(string(n)) == name && versionMatches(f.Version(), version) { + // if the version is not specified and the format is cyclonedx, then we want to return the most recent version up to 1.4 + // If more aliases like cdx are added this will not catch those - we want to eventually provide a way for + // formats to inform this function what their default version is + // TODO: remove this check when 1.5 is stable or default formats are designed. PR below should be merged. + // https://github.com/CycloneDX/cyclonedx-go/pull/90 + if version == sbom.AnyVersion && strings.Contains(string(n), "cyclone") { + if f.Version() == "1.5" { + continue + } + } if mostRecentFormat == nil || f.Version() > mostRecentFormat.Version() { mostRecentFormat = f } diff --git a/syft/formats/formats_test.go b/syft/formats/formats_test.go index 60dae72c9..2cdc99b8a 100644 --- a/syft/formats/formats_test.go +++ b/syft/formats/formats_test.go @@ -70,7 +70,6 @@ func TestFormats_EmptyInput(t *testing.T) { } func TestByName(t *testing.T) { - tests := []struct { name string want sbom.FormatID diff --git a/syft/formats/syftjson/format_test.go b/syft/formats/syftjson/format_test.go new file mode 100644 index 000000000..4ef7a21a1 --- /dev/null +++ b/syft/formats/syftjson/format_test.go @@ -0,0 +1,33 @@ +package syftjson + +import ( + "testing" + + "github.com/anchore/syft/internal" +) + +func TestFormat(t *testing.T) { + tests := []struct { + name string + version string + }{ + { + name: "default version should use latest internal version", + version: "", + }, + } + + for _, c := range tests { + c := c + t.Run(c.name, func(t *testing.T) { + sbomFormat := Format() + if sbomFormat.ID() != ID { + t.Errorf("expected ID %q, got %q", ID, sbomFormat.ID()) + } + + if sbomFormat.Version() != internal.JSONSchemaVersion { + t.Errorf("expected version %q, got %q", c.version, sbomFormat.Version()) + } + }) + } +} From a46d12270f1e49d9bddb8bb4c082dcec34a8e95b Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Wed, 13 Sep 2023 16:14:20 -0400 Subject: [PATCH 056/106] fix: encode and decode FileLicenses and FileContents in Syft JSON (#2083) Signed-off-by: Keith Zantow --- internal/constants.go | 2 +- schema/json/schema-10.0.2.json | 1976 +++++++++++++++++++ syft/formats/syftjson/decoder_test.go | 151 ++ syft/formats/syftjson/model/file.go | 15 + syft/formats/syftjson/to_format_model.go | 19 + syft/formats/syftjson/to_syft_model.go | 25 + syft/formats/syftjson/to_syft_model_test.go | 2 + 7 files changed, 2189 insertions(+), 1 deletion(-) create mode 100644 schema/json/schema-10.0.2.json diff --git a/internal/constants.go b/internal/constants.go index 6903ef00d..4b16a021c 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -3,5 +3,5 @@ package internal const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "10.0.1" + JSONSchemaVersion = "10.0.2" ) diff --git a/schema/json/schema-10.0.2.json b/schema/json/schema-10.0.2.json new file mode 100644 index 000000000..d28a36bc0 --- /dev/null +++ b/schema/json/schema-10.0.2.json @@ -0,0 +1,1976 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/10.0.2/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "type": "string" + }, + "build_requires": { + "type": "string" + }, + "py_requires": { + "type": "string" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableMetadata": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GemMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelMetadata": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModuleMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NixStoreMetadata": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelModuleMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NixStoreMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/PythonRequirementsMetadata" + }, + { + "$ref": "#/$defs/RDescriptionFileMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + }, + { + "$ref": "#/$defs/SwiftPackageManagerMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonRequirementsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "RDescriptionFileMetadata": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerMetadata": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/syft/formats/syftjson/decoder_test.go b/syft/formats/syftjson/decoder_test.go index e0de5fffb..e0e184a3e 100644 --- a/syft/formats/syftjson/decoder_test.go +++ b/syft/formats/syftjson/decoder_test.go @@ -9,8 +9,17 @@ import ( "github.com/go-test/deep" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + stereoscopeFile "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/syft/cpe" + "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/formats/internal/testutils" + "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 TestEncodeDecodeCycle(t *testing.T) { @@ -86,3 +95,145 @@ func TestOutOfDateParser(t *testing.T) { }) } } + +func Test_encodeDecodeFileMetadata(t *testing.T) { + p := pkg.Package{ + Name: "pkg", + Version: "version", + FoundBy: "something", + Locations: file.NewLocationSet(file.Location{ + LocationData: file.LocationData{ + Coordinates: file.Coordinates{ + RealPath: "/somewhere", + FileSystemID: "id", + }, + }, + LocationMetadata: file.LocationMetadata{ + Annotations: map[string]string{ + "key": "value", + }, + }, + }), + Licenses: pkg.NewLicenseSet(pkg.License{ + Value: "MIT", + SPDXExpression: "MIT", + Type: "MIT", + URLs: internal.NewStringSet("https://example.org/license"), + Locations: file.LocationSet{}, + }), + Language: "language", + Type: "type", + CPEs: []cpe.CPE{ + { + Part: "a", + Vendor: "vendor", + Product: "product", + Version: "version", + Update: "update", + }, + }, + PURL: "pkg:generic/pkg@version", + MetadataType: "", + Metadata: nil, + } + p.SetID() + + c := file.Coordinates{ + RealPath: "some-file", + FileSystemID: "some-fs-id", + } + + s := sbom.SBOM{ + Artifacts: sbom.Artifacts{ + Packages: pkg.NewCollection(p), + FileMetadata: map[file.Coordinates]file.Metadata{ + c: { + FileInfo: stereoscopeFile.ManualInfo{ + NameValue: c.RealPath, + ModeValue: 0644, + SizeValue: 7, + }, + Path: c.RealPath, + Type: stereoscopeFile.TypeRegular, + UserID: 1, + GroupID: 2, + MIMEType: "text/plain", + }, + }, + FileDigests: map[file.Coordinates][]file.Digest{ + c: { + { + Algorithm: "sha1", + Value: "d34db33f", + }, + }, + }, + FileContents: map[file.Coordinates]string{ + c: "some contents", + }, + FileLicenses: map[file.Coordinates][]file.License{ + c: { + { + Value: "MIT", + SPDXExpression: "MIT", + Type: "MIT", + LicenseEvidence: &file.LicenseEvidence{ + Confidence: 1, + Offset: 2, + Extent: 3, + }, + }, + }, + }, + LinuxDistribution: &linux.Release{ + PrettyName: "some os", + Name: "os", + ID: "os-id", + IDLike: []string{"os"}, + Version: "version", + VersionID: "version", + VersionCodename: "codename", + BuildID: "build-id", + ImageID: "image-id", + ImageVersion: "image-version", + Variant: "variant", + VariantID: "variant-id", + HomeURL: "https://example.org/os", + SupportURL: "https://example.org/os/support", + BugReportURL: "https://example.org/os/bugs", + PrivacyPolicyURL: "https://example.org/os/privacy", + CPEName: "os-cpe", + SupportEnd: "now", + }, + }, + Relationships: nil, + Source: source.Description{ + ID: "some-id", + Name: "some-name", + Version: "some-version", + Metadata: source.FileSourceMetadata{ + Path: "/some-file-source-path", + Digests: []file.Digest{ + { + Algorithm: "sha1", + Value: "d34db33f", + }, + }, + MIMEType: "file/zip", + }, + }, + Descriptor: sbom.Descriptor{ + Name: "syft", + Version: "this-version", + }, + } + + buf := &bytes.Buffer{} + err := encoder(buf, s) + require.NoError(t, err) + + got, err := decoder(buf) + require.NoError(t, err) + + require.Equal(t, s, *got) +} diff --git a/syft/formats/syftjson/model/file.go b/syft/formats/syftjson/model/file.go index 757a29315..5827b3184 100644 --- a/syft/formats/syftjson/model/file.go +++ b/syft/formats/syftjson/model/file.go @@ -2,6 +2,7 @@ package model import ( "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" ) type File struct { @@ -10,6 +11,7 @@ type File struct { Metadata *FileMetadataEntry `json:"metadata,omitempty"` Contents string `json:"contents,omitempty"` Digests []file.Digest `json:"digests,omitempty"` + Licenses []FileLicense `json:"licenses,omitempty"` } type FileMetadataEntry struct { @@ -21,3 +23,16 @@ type FileMetadataEntry struct { MIMEType string `json:"mimeType"` Size int64 `json:"size"` } + +type FileLicense struct { + Value string `json:"value"` + SPDXExpression string `json:"spdxExpression"` + Type license.Type `json:"type"` + Evidence *FileLicenseEvidence `json:"evidence,omitempty"` +} + +type FileLicenseEvidence struct { + Confidence int `json:"confidence"` + Offset int `json:"offset"` + Extent int `json:"extent"` +} diff --git a/syft/formats/syftjson/to_format_model.go b/syft/formats/syftjson/to_format_model.go index 2cafddf14..1cfda4618 100644 --- a/syft/formats/syftjson/to_format_model.go +++ b/syft/formats/syftjson/to_format_model.go @@ -106,12 +106,31 @@ func toFile(s sbom.SBOM) []model.File { contents = contentsForLocation } + var licenses []model.FileLicense + for _, l := range artifacts.FileLicenses[coordinates] { + var evidence *model.FileLicenseEvidence + if e := l.LicenseEvidence; e != nil { + evidence = &model.FileLicenseEvidence{ + Confidence: e.Confidence, + Offset: e.Offset, + Extent: e.Extent, + } + } + licenses = append(licenses, model.FileLicense{ + Value: l.Value, + SPDXExpression: l.SPDXExpression, + Type: l.Type, + Evidence: evidence, + }) + } + results = append(results, model.File{ ID: string(coordinates.ID()), Location: coordinates, Metadata: toFileMetadataEntry(coordinates, metadata), Digests: digests, Contents: contents, + Licenses: licenses, }) } diff --git a/syft/formats/syftjson/to_syft_model.go b/syft/formats/syftjson/to_syft_model.go index 419cf3ed4..990a33e27 100644 --- a/syft/formats/syftjson/to_syft_model.go +++ b/syft/formats/syftjson/to_syft_model.go @@ -34,6 +34,8 @@ func toSyftModel(doc model.Document) (*sbom.SBOM, error) { Packages: catalog, FileMetadata: fileArtifacts.FileMetadata, FileDigests: fileArtifacts.FileDigests, + FileContents: fileArtifacts.FileContents, + FileLicenses: fileArtifacts.FileLicenses, LinuxDistribution: toSyftLinuxRelease(doc.Distro), }, Source: *toSyftSourceData(doc.Source), @@ -66,6 +68,8 @@ func toSyftFiles(files []model.File) sbom.Artifacts { ret := sbom.Artifacts{ FileMetadata: make(map[file.Coordinates]file.Metadata), FileDigests: make(map[file.Coordinates][]file.Digest), + FileContents: make(map[file.Coordinates]string), + FileLicenses: make(map[file.Coordinates][]file.License), } for _, f := range files { @@ -100,6 +104,27 @@ func toSyftFiles(files []model.File) sbom.Artifacts { Value: d.Value, }) } + + if f.Contents != "" { + ret.FileContents[coord] = f.Contents + } + + for _, l := range f.Licenses { + var evidence *file.LicenseEvidence + if e := l.Evidence; e != nil { + evidence = &file.LicenseEvidence{ + Confidence: e.Confidence, + Offset: e.Offset, + Extent: e.Extent, + } + } + ret.FileLicenses[coord] = append(ret.FileLicenses[coord], file.License{ + Value: l.Value, + SPDXExpression: l.SPDXExpression, + Type: l.Type, + LicenseEvidence: evidence, + }) + } } return ret diff --git a/syft/formats/syftjson/to_syft_model_test.go b/syft/formats/syftjson/to_syft_model_test.go index 5600ec155..c4a6c0348 100644 --- a/syft/formats/syftjson/to_syft_model_test.go +++ b/syft/formats/syftjson/to_syft_model_test.go @@ -312,6 +312,8 @@ func Test_toSyftFiles(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + tt.want.FileContents = make(map[file.Coordinates]string) + tt.want.FileLicenses = make(map[file.Coordinates][]file.License) assert.Equal(t, tt.want, toSyftFiles(tt.files)) }) } From 2eb2d555518f6842b7010a48d44f09b907b67fb5 Mon Sep 17 00:00:00 2001 From: William Murphy Date: Fri, 15 Sep 2023 12:49:02 -0400 Subject: [PATCH 057/106] chore: pin all cli test FROM lines to linux/amd64 (#2137) Many of these images have a slightly different sets of packages when the arm64 variant is pulled, so that leaving this digest unpinned causes the tests to fail on arm64 hosts. Pin the FROM lines to force stable platform values regardless of host architecture. Signed-off-by: Will Murphy --- test/cli/test-fixtures/image-bad-binaries/Dockerfile | 2 +- test/cli/test-fixtures/image-hidden-packages/Dockerfile | 4 ++-- test/cli/test-fixtures/image-java-spdx-tools/Dockerfile | 2 +- test/cli/test-fixtures/image-node-binary/Dockerfile | 2 +- test/cli/test-fixtures/registry/Dockerfile | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/cli/test-fixtures/image-bad-binaries/Dockerfile b/test/cli/test-fixtures/image-bad-binaries/Dockerfile index 7851a945b..db48c26f8 100644 --- a/test/cli/test-fixtures/image-bad-binaries/Dockerfile +++ b/test/cli/test-fixtures/image-bad-binaries/Dockerfile @@ -1,4 +1,4 @@ -FROM debian:sid +FROM debian:sid@sha256:bee393b48f83fdeb978ce68abb9dc955d6398b984fca88b52a09dceb45ac74b5 ADD sources.list /etc/apt/sources.list.d/sources.list RUN apt update -y && apt install -y dpkg-dev # this as a "macho-invalid" directory which is useful for testing diff --git a/test/cli/test-fixtures/image-hidden-packages/Dockerfile b/test/cli/test-fixtures/image-hidden-packages/Dockerfile index cf8ea3bfa..1150209e8 100644 --- a/test/cli/test-fixtures/image-hidden-packages/Dockerfile +++ b/test/cli/test-fixtures/image-hidden-packages/Dockerfile @@ -1,4 +1,4 @@ -FROM centos:7.9.2009 +FROM centos:7.9.2009@sha256:dead07b4d8ed7e29e98de0f4504d87e8880d4347859d839686a31da35a3b532f # all-layers scope should pickup on vsftpd RUN yum install -y vsftpd -RUN yum remove -y vsftpd \ No newline at end of file +RUN yum remove -y vsftpd diff --git a/test/cli/test-fixtures/image-java-spdx-tools/Dockerfile b/test/cli/test-fixtures/image-java-spdx-tools/Dockerfile index 36a3da7b6..938ca24d0 100644 --- a/test/cli/test-fixtures/image-java-spdx-tools/Dockerfile +++ b/test/cli/test-fixtures/image-java-spdx-tools/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:11 +FROM openjdk:11@sha256:e81b7f317654b0f26d3993e014b04bcb29250339b11b9de41e130feecd4cd43c RUN wget --no-verbose https://github.com/spdx/tools-java/releases/download/v1.1.3/tools-java-1.1.3.zip && \ unzip tools-java-1.1.3.zip && \ diff --git a/test/cli/test-fixtures/image-node-binary/Dockerfile b/test/cli/test-fixtures/image-node-binary/Dockerfile index 6bda80cab..53a785448 100644 --- a/test/cli/test-fixtures/image-node-binary/Dockerfile +++ b/test/cli/test-fixtures/image-node-binary/Dockerfile @@ -1 +1 @@ -FROM node:19-alpine3.15 \ No newline at end of file +FROM node:19-alpine3.15@sha256:07050181369f52460e788738272bcb22b71c7cfc6fafee0b5cd27d2022513a86 diff --git a/test/cli/test-fixtures/registry/Dockerfile b/test/cli/test-fixtures/registry/Dockerfile index b09b037ca..294558cee 100644 --- a/test/cli/test-fixtures/registry/Dockerfile +++ b/test/cli/test-fixtures/registry/Dockerfile @@ -1 +1 @@ -FROM alpine:latest +FROM alpine@sha256:c5c5fda71656f28e49ac9c5416b3643eaa6a108a8093151d6d1afc9463be8e33 From 094b41b301c13687050a6eeaac07d13586319b65 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Fri, 15 Sep 2023 14:18:42 -0400 Subject: [PATCH 058/106] chore: pin and update all workflow dependencies; add permission scopes (#2138) --------- Signed-off-by: Christopher Phillips --- .github/actions/bootstrap/action.yaml | 9 ++-- .github/dependabot.yml | 12 ++--- .github/workflows/benchmark-testing.yaml | 12 +++-- .github/workflows/codeql-analysis.yml | 16 ++++--- .github/workflows/oss-project-board-add.yaml | 4 +- .github/workflows/release.yaml | 30 +++++++------ .github/workflows/update-bootstrap-tools.yml | 11 +++-- .../workflows/update-cpe-dictionary-index.yml | 9 ++-- .../workflows/update-stereoscope-release.yml | 11 +++-- .github/workflows/validations.yaml | 44 ++++++++++--------- Dockerfile | 2 +- Dockerfile.debug | 2 +- .../image-files-deleted/Dockerfile | 2 +- .../test-fixtures/image-busybox/Dockerfile | 2 +- .../environments/Dockerfile-alpine-3.6 | 4 +- .../environments/Dockerfile-ubuntu-20.04 | 4 +- .../test-fixtures/image-distro-id/Dockerfile | 2 +- .../image-go-bin-arch-coverage/Dockerfile | 2 +- .../image-java-no-main-package/Dockerfile | 2 +- .../Dockerfile | 2 +- .../image-os-binary-overlap/Dockerfile | 2 +- .../image-owning-package/Dockerfile | 4 +- .../image-photon-all-layers/Dockerfile | 2 +- .../image-rust-auditable/Dockerfile | 2 +- .../image-suse-all-layers/Dockerfile | 2 +- .../image-vertical-package-dups/Dockerfile | 4 +- 26 files changed, 114 insertions(+), 84 deletions(-) diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index b294c90ae..8a68cd497 100644 --- a/.github/actions/bootstrap/action.yaml +++ b/.github/actions/bootstrap/action.yaml @@ -1,4 +1,5 @@ name: "Bootstrap" + description: "Bootstrap all tools and dependencies" inputs: go-version: @@ -24,13 +25,13 @@ inputs: runs: using: "composite" steps: - - uses: actions/setup-go@v4 + - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 with: go-version: ${{ inputs.go-version }} - name: Restore tool cache id: tool-cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: ${{ github.workspace }}/.tmp key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('Makefile') }} @@ -40,7 +41,7 @@ runs: - name: Restore go module cache id: go-mod-cache if: inputs.use-go-cache == 'true' - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: | ~/go/pkg/mod @@ -56,7 +57,7 @@ runs: - name: Restore go build cache id: go-cache if: inputs.use-go-cache == 'true' - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: | ~/.cache/go-build diff --git a/.github/dependabot.yml b/.github/dependabot.yml index eb2c65955..271775b7b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,10 +1,12 @@ version: 2 updates: + - package-ecosystem: gomod + directory: "/" + schedule: + interval: "daily" + open-pull-requests-limit: 10 - package-ecosystem: "github-actions" directory: "/" schedule: - interval: daily - - package-ecosystem: "gomod" - directory: "/" - schedule: - interval: daily + interval: "daily" + open-pull-requests-limit: 10 \ No newline at end of file diff --git a/.github/workflows/benchmark-testing.yaml b/.github/workflows/benchmark-testing.yaml index 50f5d390b..556fc5ab8 100644 --- a/.github/workflows/benchmark-testing.yaml +++ b/.github/workflows/benchmark-testing.yaml @@ -4,6 +4,9 @@ on: workflow_dispatch: pull_request: +permissions: + contents: read + jobs: Benchmark-Test: @@ -13,13 +16,14 @@ jobs: # we also want to run on push such that merges to main are recorded to the cache. For this reason we don't filter # the job by event. steps: - - uses: actions/checkout@v4 + - name: Checkout code + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap - name: Restore base benchmark result - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: test/results/benchmark-main.txt # use base sha for PR or new commit hash for main push in benchmark result key @@ -35,13 +39,13 @@ jobs: OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters echo "result=$OUTPUT" >> $GITHUB_OUTPUT - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3 with: name: benchmark-test-results path: test/results/**/* - name: Update PR benchmark results comment - uses: marocchino/sticky-pull-request-comment@v2 + uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd #v2.8.0 continue-on-error: true with: header: benchmark diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 0b2d988fa..eb351aa1a 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -14,11 +14,17 @@ on: schedule: - cron: '0 0 * * 3' +permissions: + contents: read + jobs: analyze: name: Analyze runs-on: ubuntu-latest + permissions: + security-events: write + strategy: fail-fast: false matrix: @@ -30,16 +36,16 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - name: Install Go - uses: actions/setup-go@v4 + uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 with: go-version-file: go.mod # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2 + uses: github/codeql-action/init@04daf014b50eaf774287bf3f0f1869d4b4c4b913 #v2.21.7 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -50,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2 + uses: github/codeql-action/autobuild@04daf014b50eaf774287bf3f0f1869d4b4c4b913 #v2.21.7 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -64,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 + uses: github/codeql-action/analyze@04daf014b50eaf774287bf3f0f1869d4b4c4b913 #v2.21.7 diff --git a/.github/workflows/oss-project-board-add.yaml b/.github/workflows/oss-project-board-add.yaml index b0d1fca00..0472de8e5 100644 --- a/.github/workflows/oss-project-board-add.yaml +++ b/.github/workflows/oss-project-board-add.yaml @@ -1,5 +1,8 @@ name: Add to OSS board +permissions: + contents: read + on: issues: types: @@ -9,7 +12,6 @@ on: - labeled jobs: - run: uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main" secrets: diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index a76247ccf..4596748e8 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -1,4 +1,8 @@ name: "Release" + +permissions: + contents: read + on: workflow_dispatch: inputs: @@ -14,7 +18,7 @@ jobs: environment: release runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - name: Check if tag already exists # note: this will fail if the tag already exists @@ -23,7 +27,7 @@ jobs: git tag ${{ github.event.inputs.version }} - name: Check static analysis results - uses: fountainhead/action-wait-for-check@v1.1.0 + uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0 id: static-analysis with: token: ${{ secrets.GITHUB_TOKEN }} @@ -32,7 +36,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Check unit test results - uses: fountainhead/action-wait-for-check@v1.1.0 + uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0 id: unit with: token: ${{ secrets.GITHUB_TOKEN }} @@ -41,7 +45,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Check integration test results - uses: fountainhead/action-wait-for-check@v1.1.0 + uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0 id: integration with: token: ${{ secrets.GITHUB_TOKEN }} @@ -50,7 +54,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Check acceptance test results (linux) - uses: fountainhead/action-wait-for-check@v1.1.0 + uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0 id: acceptance-linux with: token: ${{ secrets.GITHUB_TOKEN }} @@ -59,7 +63,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Check acceptance test results (mac) - uses: fountainhead/action-wait-for-check@v1.1.0 + uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0 id: acceptance-mac with: token: ${{ secrets.GITHUB_TOKEN }} @@ -68,7 +72,7 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Check cli test results (linux) - uses: fountainhead/action-wait-for-check@v1.1.0 + uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0 id: cli-linux with: token: ${{ secrets.GITHUB_TOKEN }} @@ -94,7 +98,7 @@ jobs: contents: write packages: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 with: fetch-depth: 0 @@ -105,13 +109,13 @@ jobs: build-cache-key-prefix: "snapshot" - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d #v3.0.0 with: username: ${{ secrets.TOOLBOX_DOCKER_USER }} password: ${{ secrets.TOOLBOX_DOCKER_PASS }} - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d #v3.0.0 with: registry: ghcr.io username: ${{ github.actor }} @@ -141,12 +145,12 @@ jobs: AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }} - - uses: anchore/sbom-action@v0 + - uses: anchore/sbom-action@78fc58e266e87a38d4194b2137a3d4e9bcaf7ca1 #v0.14.3 continue-on-error: true with: artifact-name: sbom.spdx.json - - uses: 8398a7/action-slack@v3 + - uses: 8398a7/action-slack@49aa08e51e8782f49c601d79ec72d07fd39a4612 #v3.15.1 continue-on-error: true with: status: ${{ job.status }} @@ -156,7 +160,7 @@ jobs: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }} if: ${{ success() }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 #v3.1.3 with: name: artifacts path: dist/**/* diff --git a/.github/workflows/update-bootstrap-tools.yml b/.github/workflows/update-bootstrap-tools.yml index bdf133179..7396258f9 100644 --- a/.github/workflows/update-bootstrap-tools.yml +++ b/.github/workflows/update-bootstrap-tools.yml @@ -9,14 +9,17 @@ env: GO_VERSION: "1.21.x" GO_STABLE_VERSION: true +permissions: + contents: read + jobs: update-bootstrap-tools: runs-on: ubuntu-latest if: github.repository == 'anchore/syft' # only run for main repo steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 with: go-version: ${{ env.GO_VERSION }} stable: ${{ env.GO_STABLE_VERSION }} @@ -55,13 +58,13 @@ jobs: echo "GLOW=GLOW_LATEST_VERSION" >> $GITHUB_OUTPUT id: latest-versions - - uses: tibdex/github-app-token@v2 + - uses: tibdex/github-app-token@0914d50df753bbc42180d982a6550f195390069f #v2.0.0 id: generate-token with: app_id: ${{ secrets.TOKEN_APP_ID }} private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }} - - uses: peter-evans/create-pull-request@v5 + - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 #v5.0.2 with: signoff: true delete-branch: true diff --git a/.github/workflows/update-cpe-dictionary-index.yml b/.github/workflows/update-cpe-dictionary-index.yml index fb1659293..c956a80f5 100644 --- a/.github/workflows/update-cpe-dictionary-index.yml +++ b/.github/workflows/update-cpe-dictionary-index.yml @@ -5,6 +5,9 @@ on: workflow_dispatch: +permissions: + contents: read + env: GO_VERSION: "1.21.x" GO_STABLE_VERSION: true @@ -16,7 +19,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 with: go-version: ${{ env.GO_VERSION }} stable: ${{ env.GO_STABLE_VERSION }} @@ -24,13 +27,13 @@ jobs: - run: | make generate-cpe-dictionary-index - - uses: tibdex/github-app-token@v2 + - uses: tibdex/github-app-token@0914d50df753bbc42180d982a6550f195390069f #v2.0.0 id: generate-token with: app_id: ${{ secrets.TOKEN_APP_ID }} private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }} - - uses: peter-evans/create-pull-request@v5 + - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 #v5.0.2 with: signoff: true delete-branch: true diff --git a/.github/workflows/update-stereoscope-release.yml b/.github/workflows/update-stereoscope-release.yml index bd4e74b5c..f1fe98831 100644 --- a/.github/workflows/update-stereoscope-release.yml +++ b/.github/workflows/update-stereoscope-release.yml @@ -9,14 +9,17 @@ env: GO_VERSION: "1.21.x" GO_STABLE_VERSION: true +permissions: + contents: read + jobs: upgrade-stereoscope: runs-on: ubuntu-latest if: github.repository == 'anchore/syft' # only run for main repo steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - - uses: actions/setup-go@v4 + - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 with: go-version: ${{ env.GO_VERSION }} stable: ${{ env.GO_STABLE_VERSION }} @@ -32,13 +35,13 @@ jobs: echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_OUTPUT id: latest-version - - uses: tibdex/github-app-token@v2 + - uses: tibdex/github-app-token@0914d50df753bbc42180d982a6550f195390069f #v2.0.0 id: generate-token with: app_id: ${{ secrets.TOKEN_APP_ID }} private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }} - - uses: peter-evans/create-pull-request@v5 + - uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 #v5.0.2 with: signoff: true delete-branch: true diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 36502c133..8a20bae14 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -7,14 +7,16 @@ on: branches: - main -jobs: +permissions: + contents: read +jobs: Static-Analysis: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Static analysis" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -28,37 +30,37 @@ jobs: name: "Unit tests" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap - name: Restore Java test-fixture cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint' ) }} - name: Restore RPM test-fixture cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: syft/pkg/cataloger/rpm/test-fixtures/rpms key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/rpm/test-fixtures/rpms.fingerprint' ) }} - name: Restore go binary test-fixture cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }} - name: Restore binary cataloger test-fixture cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: syft/pkg/cataloger/binary/test-fixtures/classifiers/dynamic key: ${{ runner.os }}-unit-binary-cataloger-cache-${{ hashFiles( 'syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint' ) }} - name: Restore Kernel test-fixture cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: syft/pkg/cataloger/kernel/test-fixtures/cache key: ${{ runner.os }}-unit-kernel-cache-${{ hashFiles( 'syft/pkg/cataloger/kernel/test-fixtures/cache.fingerprint' ) }} @@ -72,7 +74,7 @@ jobs: name: "Integration tests" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -81,7 +83,7 @@ jobs: run: make validate-cyclonedx-schema - name: Restore integration test cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: ${{ github.workspace }}/test/integration/test-fixtures/cache key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }} @@ -94,7 +96,7 @@ jobs: name: "Build snapshot artifacts" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -113,7 +115,7 @@ jobs: # why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach). # see https://github.com/actions/upload-artifact/issues/199 for more info - name: Upload snapshot artifacts - uses: actions/cache/save@v3 + uses: actions/cache/save@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: snapshot key: snapshot-build-${{ github.run_id }} @@ -125,10 +127,10 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - name: Download snapshot build - uses: actions/cache/restore@v3 + uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: snapshot key: snapshot-build-${{ github.run_id }} @@ -138,7 +140,7 @@ jobs: - name: Restore install.sh test image cache id: install-test-image-cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: ${{ github.workspace }}/test/install/cache key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }} @@ -161,17 +163,17 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - name: Download snapshot build - uses: actions/cache/restore@v3 + uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: snapshot key: snapshot-build-${{ github.run_id }} - name: Restore docker image cache for compare testing id: mac-compare-testing-cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: image.tar key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }} @@ -189,19 +191,19 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap - name: Restore CLI test-fixture cache - uses: actions/cache@v3 + uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: ${{ github.workspace }}/test/cli/test-fixtures/cache key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }} - name: Download snapshot build - uses: actions/cache/restore@v3 + uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 with: path: snapshot key: snapshot-build-${{ github.run_id }} diff --git a/Dockerfile b/Dockerfile index c93c21fab..e922e3d56 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM gcr.io/distroless/static-debian11:debug AS build +FROM gcr.io/distroless/static-debian11:debug@sha256:a0a404776dec98be120089ae42bbdfbe48c177921d856937d124d48eb8c0b951 AS build FROM scratch # needed for version check HTTPS request diff --git a/Dockerfile.debug b/Dockerfile.debug index 3c1761cf4..37a657b3f 100644 --- a/Dockerfile.debug +++ b/Dockerfile.debug @@ -1,4 +1,4 @@ -FROM gcr.io/distroless/static-debian11:debug +FROM gcr.io/distroless/static-debian11:debug@sha256:a0a404776dec98be120089ae42bbdfbe48c177921d856937d124d48eb8c0b951 # create the /tmp dir, which is needed for image content cache WORKDIR /tmp diff --git a/syft/internal/fileresolver/test-fixtures/image-files-deleted/Dockerfile b/syft/internal/fileresolver/test-fixtures/image-files-deleted/Dockerfile index 5c5755194..10894f948 100644 --- a/syft/internal/fileresolver/test-fixtures/image-files-deleted/Dockerfile +++ b/syft/internal/fileresolver/test-fixtures/image-files-deleted/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:3.17.1 as tools +FROM alpine:3.17.1@sha256:93d5a28ff72d288d69b5997b8ba47396d2cbb62a72b5d87cd3351094b5d578a0 as tools FROM scratch COPY --from=tools /bin /bin COPY --from=tools /lib /lib diff --git a/syft/pkg/cataloger/binary/test-fixtures/image-busybox/Dockerfile b/syft/pkg/cataloger/binary/test-fixtures/image-busybox/Dockerfile index 94b54d2f4..5af8c83d1 100644 --- a/syft/pkg/cataloger/binary/test-fixtures/image-busybox/Dockerfile +++ b/syft/pkg/cataloger/binary/test-fixtures/image-busybox/Dockerfile @@ -1 +1 @@ -FROM busybox:1.35 \ No newline at end of file +FROM busybox:1.35@sha256:7ae8447f3a7f5bccaa765926f25fc038e425cf1b2be6748727bbea9a13102094 diff --git a/test/install/environments/Dockerfile-alpine-3.6 b/test/install/environments/Dockerfile-alpine-3.6 index 982e54029..f0a5fcf72 100644 --- a/test/install/environments/Dockerfile-alpine-3.6 +++ b/test/install/environments/Dockerfile-alpine-3.6 @@ -1,2 +1,2 @@ -FROM alpine:3.6 -RUN apk update && apk add python3 wget unzip make ca-certificates \ No newline at end of file +FROM alpine:3.6@sha256:66790a2b79e1ea3e1dabac43990c54aca5d1ddf268d9a5a0285e4167c8b24475 +RUN apk update && apk add python3 wget unzip make ca-certificates diff --git a/test/install/environments/Dockerfile-ubuntu-20.04 b/test/install/environments/Dockerfile-ubuntu-20.04 index dafb64ed7..07341fc38 100644 --- a/test/install/environments/Dockerfile-ubuntu-20.04 +++ b/test/install/environments/Dockerfile-ubuntu-20.04 @@ -1,2 +1,2 @@ -FROM ubuntu:20.04 -RUN apt update -y && apt install make python3 curl unzip -y \ No newline at end of file +FROM ubuntu:20.04@sha256:33a5cc25d22c45900796a1aca487ad7a7cb09f09ea00b779e3b2026b4fc2faba +RUN apt update -y && apt install make python3 curl unzip -y diff --git a/test/integration/test-fixtures/image-distro-id/Dockerfile b/test/integration/test-fixtures/image-distro-id/Dockerfile index 400d03032..0983819b3 100644 --- a/test/integration/test-fixtures/image-distro-id/Dockerfile +++ b/test/integration/test-fixtures/image-distro-id/Dockerfile @@ -1,3 +1,3 @@ -FROM busybox:1.31.1 +FROM busybox:1.31.1@sha256:95cf004f559831017cdf4628aaf1bb30133677be8702a8c5f2994629f637a209 diff --git a/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile b/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile index 7b252568c..6bf28daaa 100644 --- a/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile +++ b/test/integration/test-fixtures/image-go-bin-arch-coverage/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:latest as builder +FROM golang:1.21.1@sha256:cffaba795c36f07e372c7191b35ceaae114d74c31c3763d442982e3a4df3b39e as builder WORKDIR /app COPY go.sum go.mod app.go ./ diff --git a/test/integration/test-fixtures/image-java-no-main-package/Dockerfile b/test/integration/test-fixtures/image-java-no-main-package/Dockerfile index 7e1ac4de7..dce8deba3 100644 --- a/test/integration/test-fixtures/image-java-no-main-package/Dockerfile +++ b/test/integration/test-fixtures/image-java-no-main-package/Dockerfile @@ -1,4 +1,4 @@ -FROM jenkins/jenkins:2.346.3-slim-jdk17 +FROM jenkins/jenkins:2.346.3-slim-jdk17@sha256:028fbbd9112c60ed086f5197fcba71992317864d27644e5949cf9c52ff4b65f0 USER root diff --git a/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile b/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile index a8d70f16a..63fc6c92a 100644 --- a/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile +++ b/test/integration/test-fixtures/image-java-virtualpath-regression/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:latest +FROM alpine:3.18.3@sha256:7144f7bab3d4c2648d7e59409f15ec52a18006a128c733fcff20d3a4a54ba44a RUN wget https://repo1.maven.org/maven2/org/jvnet/hudson/main/hudson-war/2.2.1/hudson-war-2.2.1.war diff --git a/test/integration/test-fixtures/image-os-binary-overlap/Dockerfile b/test/integration/test-fixtures/image-os-binary-overlap/Dockerfile index 0951649a3..21c4d8479 100644 --- a/test/integration/test-fixtures/image-os-binary-overlap/Dockerfile +++ b/test/integration/test-fixtures/image-os-binary-overlap/Dockerfile @@ -1,4 +1,4 @@ -FROM alpine:latest +FROM alpine:3.18.3@sha256:7144f7bab3d4c2648d7e59409f15ec52a18006a128c733fcff20d3a4a54ba44a # syft should not longer show the binary package for this image: # https://github.com/anchore/syft/issues/931 diff --git a/test/integration/test-fixtures/image-owning-package/Dockerfile b/test/integration/test-fixtures/image-owning-package/Dockerfile index 37346c9c7..192998626 100644 --- a/test/integration/test-fixtures/image-owning-package/Dockerfile +++ b/test/integration/test-fixtures/image-owning-package/Dockerfile @@ -1,3 +1,3 @@ -FROM ubuntu:20.04 +FROM ubuntu:20.04@sha256:33a5cc25d22c45900796a1aca487ad7a7cb09f09ea00b779e3b2026b4fc2faba # this covers rpm-python -RUN apt-get update && apt-get install -y python-pil=6.2.1-3 \ No newline at end of file +RUN apt-get update && apt-get install -y python-pil=6.2.1-3 diff --git a/test/integration/test-fixtures/image-photon-all-layers/Dockerfile b/test/integration/test-fixtures/image-photon-all-layers/Dockerfile index ab09f97ad..17bb3691b 100644 --- a/test/integration/test-fixtures/image-photon-all-layers/Dockerfile +++ b/test/integration/test-fixtures/image-photon-all-layers/Dockerfile @@ -1 +1 @@ -FROM photon:5.0-20230729 +FROM photon:5.0-20230729@sha256:4cf2a1ce0a3f4625f13a0becb6b9bccfdb014c565be6e9a2ec4c4aad1ff8a5d9 diff --git a/test/integration/test-fixtures/image-rust-auditable/Dockerfile b/test/integration/test-fixtures/image-rust-auditable/Dockerfile index a35897003..64540e99f 100644 --- a/test/integration/test-fixtures/image-rust-auditable/Dockerfile +++ b/test/integration/test-fixtures/image-rust-auditable/Dockerfile @@ -1,2 +1,2 @@ # An image containing the example hello-auditable binary from https://github.com/Shnatsel/rust-audit/tree/master/hello-auditable -FROM docker.io/tofay/hello-rust-auditable:latest +FROM docker.io/tofay/hello-rust-auditable@sha256:1d35d1e007180b3f7500aae5e27560697909132ca9a6d480c4c825534c1c47a9 diff --git a/test/integration/test-fixtures/image-suse-all-layers/Dockerfile b/test/integration/test-fixtures/image-suse-all-layers/Dockerfile index c8d708b59..339983d88 100644 --- a/test/integration/test-fixtures/image-suse-all-layers/Dockerfile +++ b/test/integration/test-fixtures/image-suse-all-layers/Dockerfile @@ -1,2 +1,2 @@ -FROM registry.suse.com/suse/sle15:15.3.17.20.20 +FROM registry.suse.com/suse/sle15:15.3.17.20.20@sha256:fd657ecbab5ca564d6933e887f6ae8542a9398e6a4b399f352ce10c3a24afc64 RUN zypper in -y wget diff --git a/test/integration/test-fixtures/image-vertical-package-dups/Dockerfile b/test/integration/test-fixtures/image-vertical-package-dups/Dockerfile index 8f4f2a3a6..cd0e69b5d 100644 --- a/test/integration/test-fixtures/image-vertical-package-dups/Dockerfile +++ b/test/integration/test-fixtures/image-vertical-package-dups/Dockerfile @@ -1,6 +1,6 @@ -FROM centos:7.9.2009 +FROM centos:7.9.2009@sha256:be65f488b7764ad3638f236b7b515b3678369a5124c47b8d32916d6487418ea4 # modifying the RPM DB multiple times will result in duplicate packages when using all-layers (if there was no de-dup logic) # curl is tricky, it already exists in the image and is being upgraded RUN yum install -y wget-1.14-18.el7_6.1 curl-7.29.0-59.el7_9.1 RUN yum install -y vsftpd-3.0.2-29.el7_9 -RUN yum install -y httpd-2.4.6-97.el7.centos.5 \ No newline at end of file +RUN yum install -y httpd-2.4.6-97.el7.centos.5 From ec4d59592012dd362b982e78862cd16a156f751f Mon Sep 17 00:00:00 2001 From: Stefan Profanter Date: Fri, 15 Sep 2023 20:31:08 +0200 Subject: [PATCH 059/106] feat: add dependency information to conan lockfile parser (#2131) Signed-off-by: Stefan Profanter --- internal/constants.go | 2 +- schema/json/schema-11.0.0.json | 1985 +++++++++++++++++ syft/pkg/cataloger/cpp/parse_conanlock.go | 43 +- .../pkg/cataloger/cpp/parse_conanlock_test.go | 296 ++- .../pkg/cataloger/cpp/test-fixtures/README.md | 17 + .../cataloger/cpp/test-fixtures/conan.lock | 55 +- .../golang/test-fixtures/go-sum-hashes/go.sum | 11 + syft/pkg/conan_lock_metadata.go | 6 +- 8 files changed, 2366 insertions(+), 49 deletions(-) create mode 100644 schema/json/schema-11.0.0.json create mode 100644 syft/pkg/cataloger/cpp/test-fixtures/README.md diff --git a/internal/constants.go b/internal/constants.go index 4b16a021c..794da714a 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -3,5 +3,5 @@ package internal const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "10.0.2" + JSONSchemaVersion = "11.0.0" ) diff --git a/schema/json/schema-11.0.0.json b/schema/json/schema-11.0.0.json new file mode 100644 index 000000000..a36403387 --- /dev/null +++ b/schema/json/schema-11.0.0.json @@ -0,0 +1,1985 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/11.0.0/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableMetadata": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GemMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelMetadata": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModuleMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NixStoreMetadata": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelModuleMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NixStoreMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/PythonRequirementsMetadata" + }, + { + "$ref": "#/$defs/RDescriptionFileMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + }, + { + "$ref": "#/$defs/SwiftPackageManagerMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonRequirementsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "RDescriptionFileMetadata": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerMetadata": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/syft/pkg/cataloger/cpp/parse_conanlock.go b/syft/pkg/cataloger/cpp/parse_conanlock.go index 511000ea1..ef1b3b1a9 100644 --- a/syft/pkg/cataloger/cpp/parse_conanlock.go +++ b/syft/pkg/cataloger/cpp/parse_conanlock.go @@ -36,12 +36,24 @@ func parseConanlock(_ file.Resolver, _ *generic.Environment, reader file.Locatio if err := json.NewDecoder(reader).Decode(&cl); err != nil { return nil, nil, err } - for _, node := range cl.GraphLock.Nodes { + + // requires is a list of package indices. We first need to fill it, and then we can resolve the package + // in a second iteration + var indexToPkgMap = map[string]pkg.Package{} + + // we do not want to store the index list requires in the conan metadata, because it is not useful to have it in + // the SBOM. Instead, we will store it in a map and then use it to build the relationships + // maps pkg.ID to a list of indices + var parsedPkgRequires = map[artifact.ID][]string{} + + for idx, node := range cl.GraphLock.Nodes { metadata := pkg.ConanLockMetadata{ - Ref: node.Ref, - Options: parseOptions(node.Options), - Path: node.Path, - Context: node.Context, + Ref: node.Ref, + Options: parseOptions(node.Options), + Path: node.Path, + Context: node.Context, + PackageID: node.PackageID, + Prev: node.Prev, } p := newConanlockPackage( @@ -50,11 +62,28 @@ func parseConanlock(_ file.Resolver, _ *generic.Environment, reader file.Locatio ) if p != nil { - pkgs = append(pkgs, *p) + pk := *p + pkgs = append(pkgs, pk) + parsedPkgRequires[pk.ID()] = node.Requires + indexToPkgMap[idx] = pk } } - return pkgs, nil, nil + var relationships []artifact.Relationship + + for _, p := range pkgs { + requires := parsedPkgRequires[p.ID()] + for _, r := range requires { + // this is a pkg that package "p" depends on... make a relationship + relationships = append(relationships, artifact.Relationship{ + From: indexToPkgMap[r], + To: p, + Type: artifact.DependencyOfRelationship, + }) + } + } + + return pkgs, relationships, nil } func parseOptions(options string) map[string]string { diff --git a/syft/pkg/cataloger/cpp/parse_conanlock_test.go b/syft/pkg/cataloger/cpp/parse_conanlock_test.go index 66dc9ca92..5d94b570b 100644 --- a/syft/pkg/cataloger/cpp/parse_conanlock_test.go +++ b/syft/pkg/cataloger/cpp/parse_conanlock_test.go @@ -13,56 +13,296 @@ func TestParseConanlock(t *testing.T) { fixture := "test-fixtures/conan.lock" expected := []pkg.Package{ { - Name: "spdlog", - Version: "1.11.0", - PURL: "pkg:conan/spdlog@1.11.0", + Name: "mfast", + Version: "1.2.2", + PURL: "pkg:conan/my_user/mfast@1.2.2?channel=my_channel", Locations: file.NewLocationSet(file.NewLocation(fixture)), Language: pkg.CPP, Type: pkg.ConanPkg, MetadataType: pkg.ConanLockMetadataType, Metadata: pkg.ConanLockMetadata{ - Ref: "spdlog/1.11.0", + Ref: "mfast/1.2.2@my_user/my_channel#c6f6387c9b99780f0ee05e25f99d0f39", Options: map[string]string{ - "fPIC": "True", - "header_only": "False", - "no_exceptions": "False", - "shared": "False", - "wchar_filenames": "False", - "wchar_support": "False", - "fmt:fPIC": "True", - "fmt:header_only": "False", - "fmt:shared": "False", - "fmt:with_fmt_alias": "False", - "fmt:with_os_api": "True", + "fPIC": "True", + "shared": "False", + "with_sqlite3": "False", + "boost:addr2line_location": "/usr/bin/addr2line", + "boost:asio_no_deprecated": "False", + "boost:buildid": "None", + "boost:bzip2": "True", + "boost:debug_level": "0", + "boost:diagnostic_definitions": "False", + "boost:error_code_header_only": "False", + "boost:extra_b2_flags": "None", + "boost:fPIC": "True", + "boost:filesystem_no_deprecated": "False", + "boost:header_only": "False", + "boost:i18n_backend": "deprecated", + "boost:i18n_backend_iconv": "libc", + "boost:i18n_backend_icu": "False", + "boost:layout": "system", + "boost:lzma": "False", + "boost:magic_autolink": "False", + "boost:multithreading": "True", + "boost:namespace": "boost", + "boost:namespace_alias": "False", + "boost:numa": "True", + "boost:pch": "True", + "boost:python_executable": "None", + "boost:python_version": "None", + "boost:segmented_stacks": "False", + "boost:shared": "False", + "boost:system_no_deprecated": "False", + "boost:system_use_utf8": "False", + "boost:visibility": "hidden", + "boost:with_stacktrace_backtrace": "True", + "boost:without_atomic": "False", + "boost:without_chrono": "False", + "boost:without_container": "False", + "boost:without_context": "False", + "boost:without_contract": "False", + "boost:without_coroutine": "False", + "boost:without_date_time": "False", + "boost:without_exception": "False", + "boost:without_fiber": "False", + "boost:without_filesystem": "False", + "boost:without_graph": "False", + "boost:without_graph_parallel": "True", + "boost:without_iostreams": "False", + "boost:without_json": "False", + "boost:without_locale": "False", + "boost:without_log": "False", + "boost:without_math": "False", + "boost:without_mpi": "True", + "boost:without_nowide": "False", + "boost:without_program_options": "False", + "boost:without_python": "True", + "boost:without_random": "False", + "boost:without_regex": "False", + "boost:without_serialization": "False", + "boost:without_stacktrace": "False", + "boost:without_system": "False", + "boost:without_test": "False", + "boost:without_thread": "False", + "boost:without_timer": "False", + "boost:without_type_erasure": "False", + "boost:without_wave": "False", + "boost:zlib": "True", + "boost:zstd": "False", + "bzip2:build_executable": "True", + "bzip2:fPIC": "True", + "bzip2:shared": "False", + "libbacktrace:fPIC": "True", + "libbacktrace:shared": "False", + "tinyxml2:fPIC": "True", + "tinyxml2:shared": "False", + "zlib:fPIC": "True", + "zlib:shared": "False", }, - Path: "conanfile.py", - Context: "host", + Context: "host", + PackageID: "9d1f076b471417647c2022a78d5e2c1f834289ac", + Prev: "0ca9799450422cc55a92ccc6ffd57fba", }, }, { - Name: "fmt", - Version: "9.1.0", - PURL: "pkg:conan/my_user/fmt@9.1.0?channel=my_channel", + Name: "boost", + Version: "1.75.0", + PURL: "pkg:conan/boost@1.75.0", Locations: file.NewLocationSet(file.NewLocation(fixture)), Language: pkg.CPP, Type: pkg.ConanPkg, MetadataType: pkg.ConanLockMetadataType, Metadata: pkg.ConanLockMetadata{ - Ref: "fmt/9.1.0@my_user/my_channel#6708c9d84f98d56a6d9f2e6c2d5639ba", + Ref: "boost/1.75.0#a9c318f067216f900900e044e7af4ab1", Options: map[string]string{ - "fPIC": "True", - "header_only": "False", - "shared": "False", - "with_fmt_alias": "False", - "with_os_api": "True", + "addr2line_location": "/usr/bin/addr2line", + "asio_no_deprecated": "False", + "buildid": "None", + "bzip2": "True", + "debug_level": "0", + "diagnostic_definitions": "False", + "error_code_header_only": "False", + "extra_b2_flags": "None", + "fPIC": "True", + "filesystem_no_deprecated": "False", + "header_only": "False", + "i18n_backend": "deprecated", + "i18n_backend_iconv": "libc", + "i18n_backend_icu": "False", + "layout": "system", + "lzma": "False", + "magic_autolink": "False", + "multithreading": "True", + "namespace": "boost", + "namespace_alias": "False", + "numa": "True", + "pch": "True", + "python_executable": "None", + "python_version": "None", + "segmented_stacks": "False", + "shared": "False", + "system_no_deprecated": "False", + "system_use_utf8": "False", + "visibility": "hidden", + "with_stacktrace_backtrace": "True", + "without_atomic": "False", + "without_chrono": "False", + "without_container": "False", + "without_context": "False", + "without_contract": "False", + "without_coroutine": "False", + "without_date_time": "False", + "without_exception": "False", + "without_fiber": "False", + "without_filesystem": "False", + "without_graph": "False", + "without_graph_parallel": "True", + "without_iostreams": "False", + "without_json": "False", + "without_locale": "False", + "without_log": "False", + "without_math": "False", + "without_mpi": "True", + "without_nowide": "False", + "without_program_options": "False", + "without_python": "True", + "without_random": "False", + "without_regex": "False", + "without_serialization": "False", + "without_stacktrace": "False", + "without_system": "False", + "without_test": "False", + "without_thread": "False", + "without_timer": "False", + "without_type_erasure": "False", + "without_wave": "False", + "zlib": "True", + "zstd": "False", + "bzip2:build_executable": "True", + "bzip2:fPIC": "True", + "bzip2:shared": "False", + "libbacktrace:fPIC": "True", + "libbacktrace:shared": "False", + "zlib:fPIC": "True", + "zlib:shared": "False", + }, + Context: "host", + PackageID: "dc8aedd23a0f0a773a5fcdcfe1ae3e89c4205978", + Prev: "b9d7912e6131dfa453c725593b36c808", + }, + }, + { + Name: "zlib", + Version: "1.2.12", + PURL: "pkg:conan/zlib@1.2.12", + Locations: file.NewLocationSet(file.NewLocation(fixture)), + Language: pkg.CPP, + Type: pkg.ConanPkg, + MetadataType: pkg.ConanLockMetadataType, + Metadata: pkg.ConanLockMetadata{ + Ref: "zlib/1.2.12#c67ce17f2e96b972d42393ce50a76a1a", + Options: map[string]string{ + "fPIC": "True", + "shared": "False", + }, + Context: "host", + PackageID: "dfbe50feef7f3c6223a476cd5aeadb687084a646", + Prev: "7cd359d44f89ab08e33b5db75605002c", + }, + }, + { + Name: "bzip2", + Version: "1.0.8", + PURL: "pkg:conan/bzip2@1.0.8", + Locations: file.NewLocationSet(file.NewLocation(fixture)), + Language: pkg.CPP, + Type: pkg.ConanPkg, + MetadataType: pkg.ConanLockMetadataType, + Metadata: pkg.ConanLockMetadata{ + Ref: "bzip2/1.0.8#62a8031289639043797cf53fa876d0ef", + Options: map[string]string{ + "build_executable": "True", + "fPIC": "True", + "shared": "False", + }, + Context: "host", + PackageID: "c32092bf4d4bb47cf962af898e02823f499b017e", + Prev: "b746948bc999d6f17f52a1f76e729e80", + }, + }, + { + Name: "libbacktrace", + Version: "cci.20210118", + PURL: "pkg:conan/libbacktrace@cci.20210118", + Locations: file.NewLocationSet(file.NewLocation(fixture)), + Language: pkg.CPP, + Type: pkg.ConanPkg, + MetadataType: pkg.ConanLockMetadataType, + Metadata: pkg.ConanLockMetadata{ + Ref: "libbacktrace/cci.20210118#76e40b760e0bcd602d46db56b22820ab", + Options: map[string]string{ + "fPIC": "True", + "shared": "False", + }, + Context: "host", + PackageID: "dfbe50feef7f3c6223a476cd5aeadb687084a646", + Prev: "98a976f017e894c27e9a158b807ec0c7", + }, + }, + { + Name: "tinyxml2", + Version: "9.0.0", + PURL: "pkg:conan/tinyxml2@9.0.0", + Locations: file.NewLocationSet(file.NewLocation(fixture)), + Language: pkg.CPP, + Type: pkg.ConanPkg, + MetadataType: pkg.ConanLockMetadataType, + Metadata: pkg.ConanLockMetadata{ + Ref: "tinyxml2/9.0.0#9f13a36ebfc222cd55fe531a0a8d94d1", + Options: map[string]string{ + "fPIC": "True", + "shared": "False", }, Context: "host", + // intentionally remove to test missing PackageID and Prev + // PackageID: "6557f18ca99c0b6a233f43db00e30efaa525e27e", + // Prev: "548bb273d2980991baa519453d68e5cd", }, }, } - // TODO: relationships are not under test - var expectedRelationships []artifact.Relationship + var expectedRelationships = []artifact.Relationship{ + { + From: expected[1], // boost + To: expected[0], // mfast + Type: artifact.DependencyOfRelationship, + Data: nil, + }, + { + From: expected[5], // tinyxml2 + To: expected[0], // mfast + Type: artifact.DependencyOfRelationship, + Data: nil, + }, + { + From: expected[2], // zlib + To: expected[1], // boost + Type: artifact.DependencyOfRelationship, + Data: nil, + }, + { + From: expected[3], // bzip2 + To: expected[1], // boost + Type: artifact.DependencyOfRelationship, + Data: nil, + }, + { + From: expected[4], // libbacktrace + To: expected[1], // boost + Type: artifact.DependencyOfRelationship, + Data: nil, + }, + } pkgtest.TestFileParser(t, fixture, parseConanlock, expected, expectedRelationships) } diff --git a/syft/pkg/cataloger/cpp/test-fixtures/README.md b/syft/pkg/cataloger/cpp/test-fixtures/README.md new file mode 100644 index 000000000..3b2703366 --- /dev/null +++ b/syft/pkg/cataloger/cpp/test-fixtures/README.md @@ -0,0 +1,17 @@ +# Conan test data + +This folder contains the test data for the Conan package manager. + +## conan.lock + +The conan lock file is created in the following way. + +We explicitly use a package which has dependencies, which in turn also have dependendencies. +This is necessary to verify that the dependency tree is properly parsed. + +1. Use `conan lock create --reference "mfast/1.2.2#c6f6387c9b99780f0ee05e25f99d0f39"` +2. Manually modify the user and channel of mfast package, to be able to test that it is properly set in SBOM: + `sed -i 's|mfast/1.2.2#c6f6387c9b99780f0ee05e25f99d0f39|mfast/1.2.2@my_user/my_channel#c6f6387c9b99780f0ee05e25f99d0f39|g' conan.lock` +3. Manually delete the package id and prev from tinyxml2 entry to test conan lock parsing if they are missing: + `sed -i 's|\"package_id\": \"6557f18ca99c0b6a233f43db00e30efaa525e27e\",||g' conan.lock` + `sed -i 's|\"prev\": \"548bb273d2980991baa519453d68e5cd\",||g' conan.lock` \ No newline at end of file diff --git a/syft/pkg/cataloger/cpp/test-fixtures/conan.lock b/syft/pkg/cataloger/cpp/test-fixtures/conan.lock index 2632386b9..3afb74890 100644 --- a/syft/pkg/cataloger/cpp/test-fixtures/conan.lock +++ b/syft/pkg/cataloger/cpp/test-fixtures/conan.lock @@ -1,20 +1,55 @@ { "graph_lock": { "nodes": { - "0": { - "ref": "spdlog/1.11.0", - "options": "fPIC=True\nheader_only=False\nno_exceptions=False\nshared=False\nwchar_filenames=False\nwchar_support=False\nfmt:fPIC=True\nfmt:header_only=False\nfmt:shared=False\nfmt:with_fmt_alias=False\nfmt:with_os_api=True", + "1": { + "ref": "mfast/1.2.2@my_user/my_channel#c6f6387c9b99780f0ee05e25f99d0f39", + "options": "fPIC=True\nshared=False\nwith_sqlite3=False\nboost:addr2line_location=/usr/bin/addr2line\nboost:asio_no_deprecated=False\nboost:buildid=None\nboost:bzip2=True\nboost:debug_level=0\nboost:diagnostic_definitions=False\nboost:error_code_header_only=False\nboost:extra_b2_flags=None\nboost:fPIC=True\nboost:filesystem_no_deprecated=False\nboost:header_only=False\nboost:i18n_backend=deprecated\nboost:i18n_backend_iconv=libc\nboost:i18n_backend_icu=False\nboost:layout=system\nboost:lzma=False\nboost:magic_autolink=False\nboost:multithreading=True\nboost:namespace=boost\nboost:namespace_alias=False\nboost:numa=True\nboost:pch=True\nboost:python_executable=None\nboost:python_version=None\nboost:segmented_stacks=False\nboost:shared=False\nboost:system_no_deprecated=False\nboost:system_use_utf8=False\nboost:visibility=hidden\nboost:with_stacktrace_backtrace=True\nboost:without_atomic=False\nboost:without_chrono=False\nboost:without_container=False\nboost:without_context=False\nboost:without_contract=False\nboost:without_coroutine=False\nboost:without_date_time=False\nboost:without_exception=False\nboost:without_fiber=False\nboost:without_filesystem=False\nboost:without_graph=False\nboost:without_graph_parallel=True\nboost:without_iostreams=False\nboost:without_json=False\nboost:without_locale=False\nboost:without_log=False\nboost:without_math=False\nboost:without_mpi=True\nboost:without_nowide=False\nboost:without_program_options=False\nboost:without_python=True\nboost:without_random=False\nboost:without_regex=False\nboost:without_serialization=False\nboost:without_stacktrace=False\nboost:without_system=False\nboost:without_test=False\nboost:without_thread=False\nboost:without_timer=False\nboost:without_type_erasure=False\nboost:without_wave=False\nboost:zlib=True\nboost:zstd=False\nbzip2:build_executable=True\nbzip2:fPIC=True\nbzip2:shared=False\nlibbacktrace:fPIC=True\nlibbacktrace:shared=False\ntinyxml2:fPIC=True\ntinyxml2:shared=False\nzlib:fPIC=True\nzlib:shared=False", + "package_id": "9d1f076b471417647c2022a78d5e2c1f834289ac", + "prev": "0ca9799450422cc55a92ccc6ffd57fba", "requires": [ - "1" + "2", + "6" ], - "path": "conanfile.py", "context": "host" }, - "1": { - "ref": "fmt/9.1.0@my_user/my_channel#6708c9d84f98d56a6d9f2e6c2d5639ba", - "options": "fPIC=True\nheader_only=False\nshared=False\nwith_fmt_alias=False\nwith_os_api=True", - "package_id": "2c09c8f84c016041549fcee94e4caae5d89424b6", - "prev": "9f5ab13fc7c73e4a9f87e4e213f2cfa4", + "2": { + "ref": "boost/1.75.0#a9c318f067216f900900e044e7af4ab1", + "options": "addr2line_location=/usr/bin/addr2line\nasio_no_deprecated=False\nbuildid=None\nbzip2=True\ndebug_level=0\ndiagnostic_definitions=False\nerror_code_header_only=False\nextra_b2_flags=None\nfPIC=True\nfilesystem_no_deprecated=False\nheader_only=False\ni18n_backend=deprecated\ni18n_backend_iconv=libc\ni18n_backend_icu=False\nlayout=system\nlzma=False\nmagic_autolink=False\nmultithreading=True\nnamespace=boost\nnamespace_alias=False\nnuma=True\npch=True\npython_executable=None\npython_version=None\nsegmented_stacks=False\nshared=False\nsystem_no_deprecated=False\nsystem_use_utf8=False\nvisibility=hidden\nwith_stacktrace_backtrace=True\nwithout_atomic=False\nwithout_chrono=False\nwithout_container=False\nwithout_context=False\nwithout_contract=False\nwithout_coroutine=False\nwithout_date_time=False\nwithout_exception=False\nwithout_fiber=False\nwithout_filesystem=False\nwithout_graph=False\nwithout_graph_parallel=True\nwithout_iostreams=False\nwithout_json=False\nwithout_locale=False\nwithout_log=False\nwithout_math=False\nwithout_mpi=True\nwithout_nowide=False\nwithout_program_options=False\nwithout_python=True\nwithout_random=False\nwithout_regex=False\nwithout_serialization=False\nwithout_stacktrace=False\nwithout_system=False\nwithout_test=False\nwithout_thread=False\nwithout_timer=False\nwithout_type_erasure=False\nwithout_wave=False\nzlib=True\nzstd=False\nbzip2:build_executable=True\nbzip2:fPIC=True\nbzip2:shared=False\nlibbacktrace:fPIC=True\nlibbacktrace:shared=False\nzlib:fPIC=True\nzlib:shared=False", + "package_id": "dc8aedd23a0f0a773a5fcdcfe1ae3e89c4205978", + "prev": "b9d7912e6131dfa453c725593b36c808", + "requires": [ + "3", + "4", + "5" + ], + "context": "host" + }, + "3": { + "ref": "zlib/1.2.12#c67ce17f2e96b972d42393ce50a76a1a", + "options": "fPIC=True\nshared=False", + "package_id": "dfbe50feef7f3c6223a476cd5aeadb687084a646", + "prev": "7cd359d44f89ab08e33b5db75605002c", + "context": "host" + }, + "4": { + "ref": "bzip2/1.0.8#62a8031289639043797cf53fa876d0ef", + "options": "build_executable=True\nfPIC=True\nshared=False", + "package_id": "c32092bf4d4bb47cf962af898e02823f499b017e", + "prev": "b746948bc999d6f17f52a1f76e729e80", + "context": "host" + }, + "5": { + "ref": "libbacktrace/cci.20210118#76e40b760e0bcd602d46db56b22820ab", + "options": "fPIC=True\nshared=False", + "package_id": "dfbe50feef7f3c6223a476cd5aeadb687084a646", + "prev": "98a976f017e894c27e9a158b807ec0c7", + "context": "host" + }, + "6": { + "ref": "tinyxml2/9.0.0#9f13a36ebfc222cd55fe531a0a8d94d1", + "options": "fPIC=True\nshared=False", + + "context": "host" } }, diff --git a/syft/pkg/cataloger/golang/test-fixtures/go-sum-hashes/go.sum b/syft/pkg/cataloger/golang/test-fixtures/go-sum-hashes/go.sum index 4a2cebfd6..f87b23fd2 100644 --- a/syft/pkg/cataloger/golang/test-fixtures/go-sum-hashes/go.sum +++ b/syft/pkg/cataloger/golang/test-fixtures/go-sum-hashes/go.sum @@ -1,7 +1,18 @@ +github.com/CycloneDX/cyclonedx-go v0.6.0/go.mod h1:nQCiF4Tvrg5Ieu8qPhYMvzPGMu5I7fANZkrSsJjl5mg= github.com/CycloneDX/cyclonedx-go v0.7.0 h1:jNxp8hL7UpcvPDFXjY+Y1ibFtsW+e5zyF9QoSmhK/zg= github.com/CycloneDX/cyclonedx-go v0.7.0/go.mod h1:W5Z9w8pTTL+t+yG3PCiFRGlr8PUlE0pGWzKSJbsyXkg= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= +github.com/bradleyjkemp/cupaloy/v2 v2.7.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI= github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/syft/pkg/conan_lock_metadata.go b/syft/pkg/conan_lock_metadata.go index 401c67481..3c20b7ca6 100644 --- a/syft/pkg/conan_lock_metadata.go +++ b/syft/pkg/conan_lock_metadata.go @@ -11,9 +11,9 @@ type ConanLockMetadata struct { Ref string `json:"ref"` PackageID string `json:"package_id,omitempty"` Prev string `json:"prev,omitempty"` - Requires string `json:"requires,omitempty"` - BuildRequires string `json:"build_requires,omitempty"` - PythonRequires string `json:"py_requires,omitempty"` + Requires []string `json:"requires,omitempty"` + BuildRequires []string `json:"build_requires,omitempty"` + PythonRequires []string `json:"py_requires,omitempty"` Options map[string]string `json:"options,omitempty"` Path string `json:"path,omitempty"` Context string `json:"context,omitempty"` From 5d48882a787ef55a8df8457bbb62f5bbe9974a8e Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 15 Sep 2023 14:51:21 -0400 Subject: [PATCH 060/106] Add GitHub actions and shared workflow usage catalogers (#2140) * add github actions usage cataloger Signed-off-by: Alex Goodman * update integration and cli tests with github actions sample Signed-off-by: Alex Goodman * add support for shared workflows Signed-off-by: Alex Goodman * split github actions usage cataloger Signed-off-by: Alex Goodman * add source explanation for github action types Signed-off-by: Alex Goodman * a github purl does not always mean the package is a github action Signed-off-by: Alex Goodman * keep github action catalogers as dir only catalogers Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- .../formats/common/spdxhelpers/source_info.go | 2 + .../common/spdxhelpers/source_info_test.go | 16 ++ syft/pkg/cataloger/cataloger.go | 5 + syft/pkg/cataloger/githubactions/cataloger.go | 16 ++ .../cataloger/githubactions/cataloger_test.go | 50 +++++ syft/pkg/cataloger/githubactions/package.go | 103 +++++++++ .../githubactions/parse_composite_action.go | 51 +++++ .../parse_composite_action_test.go | 35 +++ .../cataloger/githubactions/parse_workflow.go | 91 ++++++++ .../githubactions/parse_workflow_test.go | 88 ++++++++ .../test-fixtures/call-shared-workflow.yaml | 19 ++ .../test-fixtures/composite-action.yaml | 81 +++++++ .../.github/actions/bootstrap/action.yaml | 1 + .../.github/actions/unbootstrap/action.yml | 1 + .../glob/.github/workflows/release.yml | 1 + .../glob/.github/workflows/validations.yaml | 1 + .../test-fixtures/workflow-multi-job.yaml | 210 ++++++++++++++++++ syft/pkg/type.go | 65 +++--- syft/pkg/type_test.go | 1 + test/cli/packages_cmd_test.go | 14 +- .../catalog_packages_cases_test.go | 14 ++ test/integration/catalog_packages_test.go | 2 + .../.github/workflows/validations.yaml | 18 ++ 23 files changed, 853 insertions(+), 32 deletions(-) create mode 100644 syft/pkg/cataloger/githubactions/cataloger.go create mode 100644 syft/pkg/cataloger/githubactions/cataloger_test.go create mode 100644 syft/pkg/cataloger/githubactions/package.go create mode 100644 syft/pkg/cataloger/githubactions/parse_composite_action.go create mode 100644 syft/pkg/cataloger/githubactions/parse_composite_action_test.go create mode 100644 syft/pkg/cataloger/githubactions/parse_workflow.go create mode 100644 syft/pkg/cataloger/githubactions/parse_workflow_test.go create mode 100644 syft/pkg/cataloger/githubactions/test-fixtures/call-shared-workflow.yaml create mode 100644 syft/pkg/cataloger/githubactions/test-fixtures/composite-action.yaml create mode 100644 syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/actions/bootstrap/action.yaml create mode 100644 syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/actions/unbootstrap/action.yml create mode 100644 syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/workflows/release.yml create mode 100644 syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/workflows/validations.yaml create mode 100644 syft/pkg/cataloger/githubactions/test-fixtures/workflow-multi-job.yaml create mode 100644 test/integration/test-fixtures/image-pkg-coverage/pkgs/github-actions/.github/workflows/validations.yaml diff --git a/syft/formats/common/spdxhelpers/source_info.go b/syft/formats/common/spdxhelpers/source_info.go index 2ec80786e..c4942aeca 100644 --- a/syft/formats/common/spdxhelpers/source_info.go +++ b/syft/formats/common/spdxhelpers/source_info.go @@ -56,6 +56,8 @@ func SourceInfo(p pkg.Package) string { answer = "acquired package info from R-package DESCRIPTION file" case pkg.SwiftPkg: answer = "acquired package info from resolved Swift package manifest" + case pkg.GithubActionPkg, pkg.GithubActionWorkflowPkg: + answer = "acquired package info from GitHub Actions workflow file or composite action file" default: answer = "acquired package info from the following paths" } diff --git a/syft/formats/common/spdxhelpers/source_info_test.go b/syft/formats/common/spdxhelpers/source_info_test.go index 5b30b95fc..4fd221a9f 100644 --- a/syft/formats/common/spdxhelpers/source_info_test.go +++ b/syft/formats/common/spdxhelpers/source_info_test.go @@ -239,6 +239,22 @@ func Test_SourceInfo(t *testing.T) { "from resolved Swift package manifest", }, }, + { + input: pkg.Package{ + Type: pkg.GithubActionPkg, + }, + expected: []string{ + "from GitHub Actions workflow file or composite action file", + }, + }, + { + input: pkg.Package{ + Type: pkg.GithubActionWorkflowPkg, + }, + expected: []string{ + "from GitHub Actions workflow file or composite action file", + }, + }, } var pkgTypes []pkg.Type for _, test := range tests { diff --git a/syft/pkg/cataloger/cataloger.go b/syft/pkg/cataloger/cataloger.go index 2d3580024..c02c7e233 100644 --- a/syft/pkg/cataloger/cataloger.go +++ b/syft/pkg/cataloger/cataloger.go @@ -19,6 +19,7 @@ import ( "github.com/anchore/syft/syft/pkg/cataloger/dotnet" "github.com/anchore/syft/syft/pkg/cataloger/elixir" "github.com/anchore/syft/syft/pkg/cataloger/erlang" + "github.com/anchore/syft/syft/pkg/cataloger/githubactions" "github.com/anchore/syft/syft/pkg/cataloger/golang" "github.com/anchore/syft/syft/pkg/cataloger/haskell" "github.com/anchore/syft/syft/pkg/cataloger/java" @@ -74,6 +75,8 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger { dotnet.NewDotnetPortableExecutableCataloger(), elixir.NewMixLockCataloger(), erlang.NewRebarLockCataloger(), + githubactions.NewActionUsageCataloger(), + githubactions.NewWorkflowUsageCataloger(), golang.NewGoModFileCataloger(cfg.Golang), golang.NewGoModuleBinaryCataloger(cfg.Golang), haskell.NewHackageCataloger(), @@ -110,6 +113,8 @@ func AllCatalogers(cfg Config) []pkg.Cataloger { dotnet.NewDotnetPortableExecutableCataloger(), elixir.NewMixLockCataloger(), erlang.NewRebarLockCataloger(), + githubactions.NewActionUsageCataloger(), + githubactions.NewWorkflowUsageCataloger(), golang.NewGoModFileCataloger(cfg.Golang), golang.NewGoModuleBinaryCataloger(cfg.Golang), haskell.NewHackageCataloger(), diff --git a/syft/pkg/cataloger/githubactions/cataloger.go b/syft/pkg/cataloger/githubactions/cataloger.go new file mode 100644 index 000000000..825d3942a --- /dev/null +++ b/syft/pkg/cataloger/githubactions/cataloger.go @@ -0,0 +1,16 @@ +package githubactions + +import "github.com/anchore/syft/syft/pkg/cataloger/generic" + +// NewActionUsageCataloger returns GitHub Actions used within workflows and composite actions. +func NewActionUsageCataloger() *generic.Cataloger { + return generic.NewCataloger("github-actions-usage-cataloger"). + WithParserByGlobs(parseWorkflowForActionUsage, "**/.github/workflows/*.yaml", "**/.github/workflows/*.yml"). + WithParserByGlobs(parseCompositeActionForActionUsage, "**/.github/actions/*/action.yml", "**/.github/actions/*/action.yaml") +} + +// NewWorkflowUsageCataloger returns shared workflows used within workflows. +func NewWorkflowUsageCataloger() *generic.Cataloger { + return generic.NewCataloger("github-action-workflow-usage-cataloger"). + WithParserByGlobs(parseWorkflowForWorkflowUsage, "**/.github/workflows/*.yaml", "**/.github/workflows/*.yml") +} diff --git a/syft/pkg/cataloger/githubactions/cataloger_test.go b/syft/pkg/cataloger/githubactions/cataloger_test.go new file mode 100644 index 000000000..f5866ef47 --- /dev/null +++ b/syft/pkg/cataloger/githubactions/cataloger_test.go @@ -0,0 +1,50 @@ +package githubactions + +import ( + "testing" + + "github.com/anchore/syft/syft/pkg/cataloger/generic" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func TestCataloger_Globs(t *testing.T) { + tests := []struct { + name string + fixture string + cataloger *generic.Cataloger + expected []string + }{ + { + name: "obtain all workflow and composite action files", + fixture: "test-fixtures/glob", + cataloger: NewActionUsageCataloger(), + expected: []string{ + // composite actions + ".github/actions/bootstrap/action.yaml", + ".github/actions/unbootstrap/action.yml", + // workflows + ".github/workflows/release.yml", + ".github/workflows/validations.yaml", + }, + }, + { + name: "obtain all workflow files", + fixture: "test-fixtures/glob", + cataloger: NewWorkflowUsageCataloger(), + expected: []string{ + // workflows + ".github/workflows/release.yml", + ".github/workflows/validations.yaml", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + pkgtest.NewCatalogTester(). + FromDirectory(t, test.fixture). + ExpectsResolverContentQueries(test.expected). + TestCataloger(t, test.cataloger) + }) + } +} diff --git a/syft/pkg/cataloger/githubactions/package.go b/syft/pkg/cataloger/githubactions/package.go new file mode 100644 index 000000000..7d6341d3c --- /dev/null +++ b/syft/pkg/cataloger/githubactions/package.go @@ -0,0 +1,103 @@ +package githubactions + +import ( + "strings" + + "github.com/anchore/packageurl-go" + "github.com/anchore/syft/internal/log" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" +) + +func newPackageFromUsageStatement(use string, location file.Location) *pkg.Package { + name, version := parseStepUsageStatement(use) + + if name == "" { + log.WithFields("file", location.RealPath, "statement", use).Trace("unable to parse github action usage statement") + return nil + } + + if strings.Contains(name, ".github/workflows/") { + return newGithubActionWorkflowPackageUsage(name, version, location) + } + + return newGithubActionPackageUsage(name, version, location) +} + +func newGithubActionWorkflowPackageUsage(name, version string, workflowLocation file.Location) *pkg.Package { + p := &pkg.Package{ + Name: name, + Version: version, + Locations: file.NewLocationSet(workflowLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), + PURL: packageURL(name, version), + Type: pkg.GithubActionWorkflowPkg, + } + + p.SetID() + + return p +} + +func newGithubActionPackageUsage(name, version string, workflowLocation file.Location) *pkg.Package { + p := &pkg.Package{ + Name: name, + Version: version, + Locations: file.NewLocationSet(workflowLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)), + PURL: packageURL(name, version), + Type: pkg.GithubActionPkg, + } + + p.SetID() + + return p +} + +func parseStepUsageStatement(use string) (string, string) { + // from octo-org/another-repo/.github/workflows/workflow.yml@v1 get octo-org/another-repo/.github/workflows/workflow.yml and v1 + // from ./.github/workflows/workflow-2.yml interpret as only the name + + // from actions/cache@v3 get actions/cache and v3 + + fields := strings.Split(use, "@") + switch len(fields) { + case 1: + return use, "" + case 2: + return fields[0], fields[1] + } + return "", "" +} + +func packageURL(name, version string) string { + var qualifiers packageurl.Qualifiers + var subPath string + var namespace string + + fields := strings.SplitN(name, "/", 3) + switch len(fields) { + case 1: + return "" + case 2: + namespace = fields[0] + name = fields[1] + case 3: + namespace = fields[0] + name = fields[1] + subPath = fields[2] + } + if namespace == "." { + // this is a local composite action, which is unclear how to represent in a PURL without more information + return "" + } + + // there isn't a github actions PURL but there is a github PURL type for referencing github repos, which is the + // next best thing until there is a supported type. + return packageurl.NewPackageURL( + packageurl.TypeGithub, + namespace, + name, + version, + qualifiers, + subPath, + ).ToString() +} diff --git a/syft/pkg/cataloger/githubactions/parse_composite_action.go b/syft/pkg/cataloger/githubactions/parse_composite_action.go new file mode 100644 index 000000000..0c27e32ce --- /dev/null +++ b/syft/pkg/cataloger/githubactions/parse_composite_action.go @@ -0,0 +1,51 @@ +package githubactions + +import ( + "fmt" + "io" + + "gopkg.in/yaml.v3" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" +) + +var _ generic.Parser = parseCompositeActionForActionUsage + +type compositeActionDef struct { + Runs compositeActionRunsDef `yaml:"runs"` +} + +type compositeActionRunsDef struct { + Steps []stepDef `yaml:"steps"` +} + +func parseCompositeActionForActionUsage(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + contents, err := io.ReadAll(reader) + if err != nil { + return nil, nil, fmt.Errorf("unable to read yaml composite action file: %w", err) + } + + var ca compositeActionDef + if err = yaml.Unmarshal(contents, &ca); err != nil { + return nil, nil, fmt.Errorf("unable to parse yaml composite action file: %w", err) + } + + // we use a collection to help with deduplication before raising to higher level processing + pkgs := pkg.NewCollection() + + for _, step := range ca.Runs.Steps { + if step.Uses == "" { + continue + } + + p := newPackageFromUsageStatement(step.Uses, reader.Location) + if p != nil { + pkgs.Add(*p) + } + } + + return pkgs.Sorted(), nil, nil +} diff --git a/syft/pkg/cataloger/githubactions/parse_composite_action_test.go b/syft/pkg/cataloger/githubactions/parse_composite_action_test.go new file mode 100644 index 000000000..e39e18e1a --- /dev/null +++ b/syft/pkg/cataloger/githubactions/parse_composite_action_test.go @@ -0,0 +1,35 @@ +package githubactions + +import ( + "testing" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_parseCompositeActionForActionUsage(t *testing.T) { + fixture := "test-fixtures/composite-action.yaml" + fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) + + expected := []pkg.Package{ + { + Name: "actions/setup-go", + Version: "v4", + Type: pkg.GithubActionPkg, + Locations: fixtureLocationSet, + PURL: "pkg:github/actions/setup-go@v4", + }, + { + Name: "actions/cache", + Version: "v3", + Type: pkg.GithubActionPkg, + Locations: fixtureLocationSet, + PURL: "pkg:github/actions/cache@v3", + }, + } + + var expectedRelationships []artifact.Relationship + pkgtest.TestFileParser(t, fixture, parseCompositeActionForActionUsage, expected, expectedRelationships) +} diff --git a/syft/pkg/cataloger/githubactions/parse_workflow.go b/syft/pkg/cataloger/githubactions/parse_workflow.go new file mode 100644 index 000000000..7460d87a7 --- /dev/null +++ b/syft/pkg/cataloger/githubactions/parse_workflow.go @@ -0,0 +1,91 @@ +package githubactions + +import ( + "fmt" + "io" + + "gopkg.in/yaml.v3" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" +) + +var ( + _ generic.Parser = parseWorkflowForActionUsage + _ generic.Parser = parseWorkflowForWorkflowUsage +) + +type workflowDef struct { + Jobs map[string]workflowJobDef `yaml:"jobs"` +} + +type workflowJobDef struct { + Uses string `yaml:"uses"` + Steps []stepDef `yaml:"steps"` +} + +type stepDef struct { + Name string `yaml:"name"` + Uses string `yaml:"uses"` + With struct { + Path string `yaml:"path"` + Key string `yaml:"key"` + } `yaml:"with"` +} + +func parseWorkflowForWorkflowUsage(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + contents, err := io.ReadAll(reader) + if err != nil { + return nil, nil, fmt.Errorf("unable to read yaml workflow file: %w", err) + } + + var wf workflowDef + if err = yaml.Unmarshal(contents, &wf); err != nil { + return nil, nil, fmt.Errorf("unable to parse yaml workflow file: %w", err) + } + + // we use a collection to help with deduplication before raising to higher level processing + pkgs := pkg.NewCollection() + + for _, job := range wf.Jobs { + if job.Uses != "" { + p := newPackageFromUsageStatement(job.Uses, reader.Location) + if p != nil { + pkgs.Add(*p) + } + } + } + + return pkgs.Sorted(), nil, nil +} + +func parseWorkflowForActionUsage(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { + contents, err := io.ReadAll(reader) + if err != nil { + return nil, nil, fmt.Errorf("unable to read yaml workflow file: %w", err) + } + + var wf workflowDef + if err = yaml.Unmarshal(contents, &wf); err != nil { + return nil, nil, fmt.Errorf("unable to parse yaml workflow file: %w", err) + } + + // we use a collection to help with deduplication before raising to higher level processing + pkgs := pkg.NewCollection() + + for _, job := range wf.Jobs { + for _, step := range job.Steps { + if step.Uses == "" { + continue + } + p := newPackageFromUsageStatement(step.Uses, reader.Location) + if p != nil { + pkgs.Add(*p) + } + } + } + + return pkgs.Sorted(), nil, nil +} diff --git a/syft/pkg/cataloger/githubactions/parse_workflow_test.go b/syft/pkg/cataloger/githubactions/parse_workflow_test.go new file mode 100644 index 000000000..f5e5128b4 --- /dev/null +++ b/syft/pkg/cataloger/githubactions/parse_workflow_test.go @@ -0,0 +1,88 @@ +package githubactions + +import ( + "testing" + + "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" +) + +func Test_parseWorkflowForActionUsage(t *testing.T) { + fixture := "test-fixtures/workflow-multi-job.yaml" + fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) + + expected := []pkg.Package{ + { + Name: "./.github/actions/bootstrap", + Version: "", + Type: pkg.GithubActionPkg, + Locations: fixtureLocationSet, + PURL: "", // don't have enough context without parsing the git origin, which still may not be accurate + }, + { + Name: "actions/cache", + Version: "v3", + Type: pkg.GithubActionPkg, + Locations: fixtureLocationSet, + PURL: "pkg:github/actions/cache@v3", + }, + { + Name: "actions/cache/restore", + Version: "v3", + Type: pkg.GithubActionPkg, + Locations: fixtureLocationSet, + PURL: "pkg:github/actions/cache@v3#restore", + }, + { + Name: "actions/cache/save", + Version: "v3", + Type: pkg.GithubActionPkg, + Locations: fixtureLocationSet, + PURL: "pkg:github/actions/cache@v3#save", + }, + { + Name: "actions/checkout", + Version: "v4", + Type: pkg.GithubActionPkg, + Locations: fixtureLocationSet, + PURL: "pkg:github/actions/checkout@v4", + }, + } + + var expectedRelationships []artifact.Relationship + pkgtest.TestFileParser(t, fixture, parseWorkflowForActionUsage, expected, expectedRelationships) +} + +func Test_parseWorkflowForWorkflowUsage(t *testing.T) { + fixture := "test-fixtures/call-shared-workflow.yaml" + fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture).WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)) + + expected := []pkg.Package{ + { + Name: "octo-org/this-repo/.github/workflows/workflow-1.yml", + Version: "172239021f7ba04fe7327647b213799853a9eb89", + Type: pkg.GithubActionWorkflowPkg, + Locations: fixtureLocationSet, + PURL: "pkg:github/octo-org/this-repo@172239021f7ba04fe7327647b213799853a9eb89#.github/workflows/workflow-1.yml", + }, + { + Name: "./.github/workflows/workflow-2.yml", + Version: "", + Type: pkg.GithubActionWorkflowPkg, + Locations: fixtureLocationSet, + PURL: "", // don't have enough context without parsing the git origin, which still may not be accurate + }, + { + Name: "octo-org/another-repo/.github/workflows/workflow.yml", + Version: "v1", + Type: pkg.GithubActionWorkflowPkg, + Locations: fixtureLocationSet, + PURL: "pkg:github/octo-org/another-repo@v1#.github/workflows/workflow.yml", + }, + } + + var expectedRelationships []artifact.Relationship + pkgtest.TestFileParser(t, fixture, parseWorkflowForWorkflowUsage, expected, expectedRelationships) +} diff --git a/syft/pkg/cataloger/githubactions/test-fixtures/call-shared-workflow.yaml b/syft/pkg/cataloger/githubactions/test-fixtures/call-shared-workflow.yaml new file mode 100644 index 000000000..69061958b --- /dev/null +++ b/syft/pkg/cataloger/githubactions/test-fixtures/call-shared-workflow.yaml @@ -0,0 +1,19 @@ +jobs: + + call-workflow-1-in-local-repo: + uses: octo-org/this-repo/.github/workflows/workflow-1.yml@172239021f7ba04fe7327647b213799853a9eb89 + + call-workflow-2-in-local-repo: + uses: ./.github/workflows/workflow-2.yml + + call-workflow-in-another-repo: + uses: octo-org/another-repo/.github/workflows/workflow.yml@v1 + + + unit-test: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + + - name: Bootstrap environment + run: make unit diff --git a/syft/pkg/cataloger/githubactions/test-fixtures/composite-action.yaml b/syft/pkg/cataloger/githubactions/test-fixtures/composite-action.yaml new file mode 100644 index 000000000..b294c90ae --- /dev/null +++ b/syft/pkg/cataloger/githubactions/test-fixtures/composite-action.yaml @@ -0,0 +1,81 @@ +name: "Bootstrap" +description: "Bootstrap all tools and dependencies" +inputs: + go-version: + description: "Go version to install" + required: true + default: "1.21.x" + use-go-cache: + description: "Restore go cache" + required: true + default: "true" + cache-key-prefix: + description: "Prefix all cache keys with this value" + required: true + default: "831180ac25" + build-cache-key-prefix: + description: "Prefix build cache key with this value" + required: true + default: "f8b6d31dea" + bootstrap-apt-packages: + description: "Space delimited list of tools to install via apt" + default: "libxml2-utils" + +runs: + using: "composite" + steps: + - uses: actions/setup-go@v4 + with: + go-version: ${{ inputs.go-version }} + + - name: Restore tool cache + id: tool-cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/.tmp + key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('Makefile') }} + + # note: we need to keep restoring the go mod cache before bootstrapping tools since `go install` is used in + # some installations of project tools. + - name: Restore go module cache + id: go-mod-cache + if: inputs.use-go-cache == 'true' + uses: actions/cache@v3 + with: + path: | + ~/go/pkg/mod + key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}- + + - name: (cache-miss) Bootstrap project tools + shell: bash + if: steps.tool-cache.outputs.cache-hit != 'true' + run: make bootstrap-tools + + - name: Restore go build cache + id: go-cache + if: inputs.use-go-cache == 'true' + uses: actions/cache@v3 + with: + path: | + ~/.cache/go-build + key: ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} + restore-keys: | + ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}- + + - name: (cache-miss) Bootstrap go dependencies + shell: bash + if: steps.go-mod-cache.outputs.cache-hit != 'true' && inputs.use-go-cache == 'true' + run: make bootstrap-go + + - name: Install apt packages + if: inputs.bootstrap-apt-packages != '' + shell: bash + run: | + DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y ${{ inputs.bootstrap-apt-packages }} + + - name: Create all cache fingerprints + shell: bash + run: make fingerprints + diff --git a/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/actions/bootstrap/action.yaml b/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/actions/bootstrap/action.yaml new file mode 100644 index 000000000..095e73c82 --- /dev/null +++ b/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/actions/bootstrap/action.yaml @@ -0,0 +1 @@ +# fake \ No newline at end of file diff --git a/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/actions/unbootstrap/action.yml b/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/actions/unbootstrap/action.yml new file mode 100644 index 000000000..095e73c82 --- /dev/null +++ b/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/actions/unbootstrap/action.yml @@ -0,0 +1 @@ +# fake \ No newline at end of file diff --git a/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/workflows/release.yml b/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/workflows/release.yml new file mode 100644 index 000000000..095e73c82 --- /dev/null +++ b/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/workflows/release.yml @@ -0,0 +1 @@ +# fake \ No newline at end of file diff --git a/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/workflows/validations.yaml b/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/workflows/validations.yaml new file mode 100644 index 000000000..095e73c82 --- /dev/null +++ b/syft/pkg/cataloger/githubactions/test-fixtures/glob/.github/workflows/validations.yaml @@ -0,0 +1 @@ +# fake \ No newline at end of file diff --git a/syft/pkg/cataloger/githubactions/test-fixtures/workflow-multi-job.yaml b/syft/pkg/cataloger/githubactions/test-fixtures/workflow-multi-job.yaml new file mode 100644 index 000000000..36502c133 --- /dev/null +++ b/syft/pkg/cataloger/githubactions/test-fixtures/workflow-multi-job.yaml @@ -0,0 +1,210 @@ +name: "Validations" + +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + +jobs: + + Static-Analysis: + # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline + name: "Static analysis" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + + - name: Bootstrap environment + uses: ./.github/actions/bootstrap + + - name: Run static analysis + run: make static-analysis + + + Unit-Test: + # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline + name: "Unit tests" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + + - name: Bootstrap environment + uses: ./.github/actions/bootstrap + + - name: Restore Java test-fixture cache + uses: actions/cache@v3 + with: + path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages + key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint' ) }} + + - name: Restore RPM test-fixture cache + uses: actions/cache@v3 + with: + path: syft/pkg/cataloger/rpm/test-fixtures/rpms + key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/rpm/test-fixtures/rpms.fingerprint' ) }} + + - name: Restore go binary test-fixture cache + uses: actions/cache@v3 + with: + path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries + key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }} + + - name: Restore binary cataloger test-fixture cache + uses: actions/cache@v3 + with: + path: syft/pkg/cataloger/binary/test-fixtures/classifiers/dynamic + key: ${{ runner.os }}-unit-binary-cataloger-cache-${{ hashFiles( 'syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint' ) }} + + - name: Restore Kernel test-fixture cache + uses: actions/cache@v3 + with: + path: syft/pkg/cataloger/kernel/test-fixtures/cache + key: ${{ runner.os }}-unit-kernel-cache-${{ hashFiles( 'syft/pkg/cataloger/kernel/test-fixtures/cache.fingerprint' ) }} + + - name: Run unit tests + run: make unit + + + Integration-Test: + # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline + name: "Integration tests" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + + - name: Bootstrap environment + uses: ./.github/actions/bootstrap + + - name: Validate syft output against the CycloneDX schema + run: make validate-cyclonedx-schema + + - name: Restore integration test cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/test/integration/test-fixtures/cache + key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }} + + - name: Run integration tests + run: make integration + + + Build-Snapshot-Artifacts: + name: "Build snapshot artifacts" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + + - name: Bootstrap environment + uses: ./.github/actions/bootstrap + with: + # why have another build cache key? We don't want unit/integration/etc test build caches to replace + # the snapshot build cache, which includes builds for all OSs and architectures. As long as this key is + # unique from the build-cache-key-prefix in other CI jobs, we should be fine. + # + # note: ideally this value should match what is used in release (just to help with build times). + build-cache-key-prefix: "snapshot" + bootstrap-apt-packages: "" + + - name: Build snapshot artifacts + run: make snapshot + + # why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach). + # see https://github.com/actions/upload-artifact/issues/199 for more info + - name: Upload snapshot artifacts + uses: actions/cache/save@v3 + with: + path: snapshot + key: snapshot-build-${{ github.run_id }} + + + Acceptance-Linux: + # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline + name: "Acceptance tests (Linux)" + needs: [Build-Snapshot-Artifacts] + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + + - name: Download snapshot build + uses: actions/cache/restore@v3 + with: + path: snapshot + key: snapshot-build-${{ github.run_id }} + + - name: Run comparison tests (Linux) + run: make compare-linux + + - name: Restore install.sh test image cache + id: install-test-image-cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/test/install/cache + key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }} + + - name: Load test image cache + if: steps.install-test-image-cache.outputs.cache-hit == 'true' + run: make install-test-cache-load + + - name: Run install.sh tests (Linux) + run: make install-test + + - name: (cache-miss) Create test image cache + if: steps.install-test-image-cache.outputs.cache-hit != 'true' + run: make install-test-cache-save + + + Acceptance-Mac: + # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline + name: "Acceptance tests (Mac)" + needs: [Build-Snapshot-Artifacts] + runs-on: macos-latest + steps: + - uses: actions/checkout@v4 + + - name: Download snapshot build + uses: actions/cache/restore@v3 + with: + path: snapshot + key: snapshot-build-${{ github.run_id }} + + - name: Restore docker image cache for compare testing + id: mac-compare-testing-cache + uses: actions/cache@v3 + with: + path: image.tar + key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }} + + - name: Run comparison tests (Mac) + run: make compare-mac + + - name: Run install.sh tests (Mac) + run: make install-test-ci-mac + + + Cli-Linux: + # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline + name: "CLI tests (Linux)" + needs: [Build-Snapshot-Artifacts] + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 + + - name: Bootstrap environment + uses: ./.github/actions/bootstrap + + - name: Restore CLI test-fixture cache + uses: actions/cache@v3 + with: + path: ${{ github.workspace }}/test/cli/test-fixtures/cache + key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }} + + - name: Download snapshot build + uses: actions/cache/restore@v3 + with: + path: snapshot + key: snapshot-build-${{ github.run_id }} + + - name: Run CLI Tests (Linux) + run: make cli diff --git a/syft/pkg/type.go b/syft/pkg/type.go index e3ed3f4c1..788e584a5 100644 --- a/syft/pkg/type.go +++ b/syft/pkg/type.go @@ -9,34 +9,36 @@ type Type string const ( // the full set of supported packages - UnknownPkg Type = "UnknownPackage" - AlpmPkg Type = "alpm" - ApkPkg Type = "apk" - BinaryPkg Type = "binary" - CocoapodsPkg Type = "pod" - ConanPkg Type = "conan" - DartPubPkg Type = "dart-pub" - DebPkg Type = "deb" - DotnetPkg Type = "dotnet" - GemPkg Type = "gem" - GoModulePkg Type = "go-module" - GraalVMNativeImagePkg Type = "graalvm-native-image" - HackagePkg Type = "hackage" - HexPkg Type = "hex" - JavaPkg Type = "java-archive" - JenkinsPluginPkg Type = "jenkins-plugin" - KbPkg Type = "msrc-kb" - LinuxKernelPkg Type = "linux-kernel" - LinuxKernelModulePkg Type = "linux-kernel-module" - NixPkg Type = "nix" - NpmPkg Type = "npm" - PhpComposerPkg Type = "php-composer" - PortagePkg Type = "portage" - PythonPkg Type = "python" - Rpkg Type = "R-package" - RpmPkg Type = "rpm" - RustPkg Type = "rust-crate" - SwiftPkg Type = "swift" + UnknownPkg Type = "UnknownPackage" + AlpmPkg Type = "alpm" + ApkPkg Type = "apk" + BinaryPkg Type = "binary" + CocoapodsPkg Type = "pod" + ConanPkg Type = "conan" + DartPubPkg Type = "dart-pub" + DebPkg Type = "deb" + DotnetPkg Type = "dotnet" + GemPkg Type = "gem" + GithubActionPkg Type = "github-action" + GithubActionWorkflowPkg Type = "github-action-workflow" + GoModulePkg Type = "go-module" + GraalVMNativeImagePkg Type = "graalvm-native-image" + HackagePkg Type = "hackage" + HexPkg Type = "hex" + JavaPkg Type = "java-archive" + JenkinsPluginPkg Type = "jenkins-plugin" + KbPkg Type = "msrc-kb" + LinuxKernelPkg Type = "linux-kernel" + LinuxKernelModulePkg Type = "linux-kernel-module" + NixPkg Type = "nix" + NpmPkg Type = "npm" + PhpComposerPkg Type = "php-composer" + PortagePkg Type = "portage" + PythonPkg Type = "python" + Rpkg Type = "R-package" + RpmPkg Type = "rpm" + RustPkg Type = "rust-crate" + SwiftPkg Type = "swift" ) // AllPkgs represents all supported package types @@ -50,6 +52,8 @@ var AllPkgs = []Type{ DebPkg, DotnetPkg, GemPkg, + GithubActionPkg, + GithubActionWorkflowPkg, GoModulePkg, HackagePkg, HexPkg, @@ -70,6 +74,8 @@ var AllPkgs = []Type{ } // PackageURLType returns the PURL package type for the current package. +// +//nolint:funlen func (t Type) PackageURLType() string { switch t { case AlpmPkg: @@ -90,6 +96,9 @@ func (t Type) PackageURLType() string { return packageurl.TypeGem case HexPkg: return packageurl.TypeHex + case GithubActionPkg, GithubActionWorkflowPkg: + // note: this is not a real purl type, but it is the closest thing we have for now + return packageurl.TypeGithub case GoModulePkg: return packageurl.TypeGolang case HackagePkg: diff --git a/syft/pkg/type_test.go b/syft/pkg/type_test.go index 3da73184d..64d8e87c3 100644 --- a/syft/pkg/type_test.go +++ b/syft/pkg/type_test.go @@ -114,6 +114,7 @@ func TestTypeFromPURL(t *testing.T) { expectedTypes.Remove(string(PortagePkg)) expectedTypes.Remove(string(BinaryPkg)) expectedTypes.Remove(string(LinuxKernelModulePkg)) + expectedTypes.Remove(string(GithubActionPkg), string(GithubActionWorkflowPkg)) for _, test := range tests { t.Run(string(test.expected), func(t *testing.T) { diff --git a/test/cli/packages_cmd_test.go b/test/cli/packages_cmd_test.go index dc100ebeb..81ad258b1 100644 --- a/test/cli/packages_cmd_test.go +++ b/test/cli/packages_cmd_test.go @@ -6,6 +6,12 @@ import ( "testing" ) +const ( + // this is the number of packages that should be found in the image-pkg-coverage fixture image + // when analyzed with the squashed scope. + coverageImageSquashedPackageCount = 24 +) + func TestPackagesCmdFlags(t *testing.T) { hiddenPackagesImage := "docker-archive:" + getFixtureImage(t, "image-hidden-packages") coverageImage := "docker-archive:" + getFixtureImage(t, "image-pkg-coverage") @@ -114,7 +120,7 @@ func TestPackagesCmdFlags(t *testing.T) { name: "squashed-scope-flag", args: []string{"packages", "-o", "json", "-s", "squashed", coverageImage}, assertions: []traitAssertion{ - assertPackageCount(24), + assertPackageCount(coverageImageSquashedPackageCount), assertSuccessfulReturnCode, }, }, @@ -231,7 +237,7 @@ func TestPackagesCmdFlags(t *testing.T) { // the application config in the log matches that of what we expect to have been configured. assertInOutput("parallelism: 2"), assertInOutput("parallelism=2"), - assertPackageCount(24), + assertPackageCount(coverageImageSquashedPackageCount), assertSuccessfulReturnCode, }, }, @@ -242,7 +248,7 @@ func TestPackagesCmdFlags(t *testing.T) { // the application config in the log matches that of what we expect to have been configured. assertInOutput("parallelism: 1"), assertInOutput("parallelism=1"), - assertPackageCount(24), + assertPackageCount(coverageImageSquashedPackageCount), assertSuccessfulReturnCode, }, }, @@ -256,7 +262,7 @@ func TestPackagesCmdFlags(t *testing.T) { assertions: []traitAssertion{ assertNotInOutput("secret_password"), assertNotInOutput("secret_key_path"), - assertPackageCount(24), + assertPackageCount(coverageImageSquashedPackageCount), assertSuccessfulReturnCode, }, }, diff --git a/test/integration/catalog_packages_cases_test.go b/test/integration/catalog_packages_cases_test.go index 48b280a56..c88b2d559 100644 --- a/test/integration/catalog_packages_cases_test.go +++ b/test/integration/catalog_packages_cases_test.go @@ -368,6 +368,20 @@ var dirOnlyTestCases = []testCase{ "swift-numerics": "1.0.2", }, }, + { + name: "find github action packages (from usage in workflow files and composite actions)", + pkgType: pkg.GithubActionPkg, + pkgInfo: map[string]string{ + "actions/checkout": "v4", + }, + }, + { + name: "find github shared workflow calls (from usage in workflow files)", + pkgType: pkg.GithubActionWorkflowPkg, + pkgInfo: map[string]string{ + "octo-org/this-repo/.github/workflows/workflow-1.yml": "172239021f7ba04fe7327647b213799853a9eb89", + }, + }, } var commonTestCases = []testCase{ diff --git a/test/integration/catalog_packages_test.go b/test/integration/catalog_packages_test.go index 0b6ca54f0..50a8b06ba 100644 --- a/test/integration/catalog_packages_test.go +++ b/test/integration/catalog_packages_test.go @@ -96,6 +96,8 @@ func TestPkgCoverageImage(t *testing.T) { definedPkgs.Remove(string(pkg.LinuxKernelPkg)) definedPkgs.Remove(string(pkg.LinuxKernelModulePkg)) definedPkgs.Remove(string(pkg.SwiftPkg)) + definedPkgs.Remove(string(pkg.GithubActionPkg)) + definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg)) var cases []testCase cases = append(cases, commonTestCases...) diff --git a/test/integration/test-fixtures/image-pkg-coverage/pkgs/github-actions/.github/workflows/validations.yaml b/test/integration/test-fixtures/image-pkg-coverage/pkgs/github-actions/.github/workflows/validations.yaml new file mode 100644 index 000000000..2c8c17526 --- /dev/null +++ b/test/integration/test-fixtures/image-pkg-coverage/pkgs/github-actions/.github/workflows/validations.yaml @@ -0,0 +1,18 @@ +name: "Validations" + +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + +jobs: + call-workflow-1-in-local-repo: + uses: octo-org/this-repo/.github/workflows/workflow-1.yml@172239021f7ba04fe7327647b213799853a9eb89 + + Unit-Test: + name: "Unit tests" + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v4 From 594ba5f2951504dd85d8ad0f5d7610108c73dbe8 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Fri, 15 Sep 2023 16:00:15 -0400 Subject: [PATCH 061/106] chore: pin workflow checkout for cpe update-cpe-dictionary-index (#2141) Signed-off-by: Christopher Phillips --- .github/workflows/update-cpe-dictionary-index.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/update-cpe-dictionary-index.yml b/.github/workflows/update-cpe-dictionary-index.yml index c956a80f5..e9f333808 100644 --- a/.github/workflows/update-cpe-dictionary-index.yml +++ b/.github/workflows/update-cpe-dictionary-index.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'anchore/syft' # only run for main repo steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 - uses: actions/setup-go@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 with: From 23e3de75e3bebcb09f5ef901b16aa66d2ba9fa3a Mon Sep 17 00:00:00 2001 From: Shane Dell <32347414+shanedell@users.noreply.github.com> Date: Mon, 18 Sep 2023 11:33:43 -0400 Subject: [PATCH 062/106] Add containerd support (#1793) * [wip] add containerd UI handlers Signed-off-by: Alex Goodman * Add containerd support - Add UI handlers (done by @wagoodman) - Add containerd types and wrappers (done by @wagoodman) - Add flag for specifying containerd address Closes #201 Signed-off-by: Shane Dell * Fix lint Signed-off-by: Shane Dell * add containerd ui handler Signed-off-by: Alex Goodman * add containerd scheme to readme Signed-off-by: Alex Goodman * add test for scheme detection Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman Signed-off-by: Shane Dell Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman Co-authored-by: Alex Goodman --- README.md | 3 +- .../cli/ui/handle_pull_containerd_image.go | 190 ++++++++++++++++++ cmd/syft/cli/ui/handler.go | 1 + go.mod | 39 +++- go.sum | 76 ++++++- syft/source/detection.go | 2 + syft/source/detection_test.go | 20 ++ 7 files changed, 317 insertions(+), 14 deletions(-) create mode 100644 cmd/syft/cli/ui/handle_pull_containerd_image.go diff --git a/README.md b/README.md index 95d724039..ef6969c82 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,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 +containerd:yourrepo/yourimage:tag use images from the Containerd 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) @@ -671,7 +672,7 @@ source: # the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512") digests: ["sha256"] -# options when pulling directly from a registry via the "registry:" scheme +# options when pulling directly from a registry via the "registry:" or "containerd:" scheme registry: # skip TLS verification when communicating with the registry # SYFT_REGISTRY_INSECURE_SKIP_TLS_VERIFY env var diff --git a/cmd/syft/cli/ui/handle_pull_containerd_image.go b/cmd/syft/cli/ui/handle_pull_containerd_image.go new file mode 100644 index 000000000..0b3929deb --- /dev/null +++ b/cmd/syft/cli/ui/handle_pull_containerd_image.go @@ -0,0 +1,190 @@ +package ui + +import ( + "fmt" + "strings" + + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/dustin/go-humanize" + "github.com/wagoodman/go-partybus" + "github.com/wagoodman/go-progress" + + "github.com/anchore/bubbly/bubbles/taskprogress" + stereoscopeParsers "github.com/anchore/stereoscope/pkg/event/parsers" + "github.com/anchore/stereoscope/pkg/image/containerd" + "github.com/anchore/syft/internal/log" +) + +var _ interface { + progress.Stager + progress.Progressable +} = (*containerdPullProgressAdapter)(nil) + +type containerdPullStatus interface { + Complete() bool + Layers() []containerd.LayerID + Current(containerd.LayerID) progress.Progressable +} + +type containerdPullProgressAdapter struct { + status containerdPullStatus + formatter containerdPullStatusFormatter +} + +type containerdPullStatusFormatter struct { + auxInfoStyle lipgloss.Style + pullCompletedStyle lipgloss.Style + pullDownloadStyle lipgloss.Style + pullStageChars []string + layerCaps []string +} + +func (m *Handler) handlePullContainerdImage(e partybus.Event) []tea.Model { + _, pullStatus, err := stereoscopeParsers.ParsePullContainerdImage(e) + if err != nil { + log.WithFields("error", err).Warn("unable to parse event") + return nil + } + + if pullStatus == nil { + return nil + } + + tsk := m.newTaskProgress( + taskprogress.Title{ + Default: "Pull image", + Running: "Pulling image", + Success: "Pulled image", + }, + taskprogress.WithStagedProgressable( + newContainerdPullProgressAdapter(pullStatus), + ), + ) + + tsk.HintStyle = lipgloss.NewStyle() + tsk.HintEndCaps = nil + + return []tea.Model{tsk} +} + +func newContainerdPullProgressAdapter(status *containerd.PullStatus) *containerdPullProgressAdapter { + return &containerdPullProgressAdapter{ + status: status, + formatter: newContainerdPullStatusFormatter(), + } +} + +func newContainerdPullStatusFormatter() containerdPullStatusFormatter { + return containerdPullStatusFormatter{ + auxInfoStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")), + pullCompletedStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#fcba03")), + pullDownloadStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")), + pullStageChars: strings.Split("▁▃▄▅▆▇█", ""), + layerCaps: strings.Split("▕▏", ""), + } +} + +func (d containerdPullProgressAdapter) Size() int64 { + return -1 +} + +func (d containerdPullProgressAdapter) Current() int64 { + return 1 +} + +func (d containerdPullProgressAdapter) Error() error { + if d.status.Complete() { + return progress.ErrCompleted + } + // TODO: return intermediate error indications + return nil +} + +func (d containerdPullProgressAdapter) Stage() string { + return d.formatter.Render(d.status) +} + +// Render crafts the given docker image pull status summarized into a single line. +func (f containerdPullStatusFormatter) Render(pullStatus containerdPullStatus) string { + var size, current uint64 + + layers := pullStatus.Layers() + status := make(map[containerd.LayerID]progress.Progressable) + completed := make([]string, len(layers)) + + // fetch the current state + for idx, layer := range layers { + completed[idx] = " " + status[layer] = pullStatus.Current(layer) + } + + numCompleted := 0 + for idx, layer := range layers { + prog := status[layer] + curN := prog.Current() + curSize := prog.Size() + + if progress.IsCompleted(prog) { + input := f.pullStageChars[len(f.pullStageChars)-1] + completed[idx] = f.formatPullPhase(prog.Error() != nil, input) + } else if curN != 0 { + var ratio float64 + switch { + case curN == 0 || curSize < 0: + ratio = 0 + case curN >= curSize: + ratio = 1 + default: + ratio = float64(curN) / float64(curSize) + } + + i := int(ratio * float64(len(f.pullStageChars)-1)) + input := f.pullStageChars[i] + completed[idx] = f.formatPullPhase(status[layer].Error() != nil, input) + } + + if progress.IsErrCompleted(status[layer].Error()) { + numCompleted++ + } + } + + for _, layer := range layers { + prog := status[layer] + size += uint64(prog.Size()) + current += uint64(prog.Current()) + } + + var progStr, auxInfo string + if len(layers) > 0 { + render := strings.Join(completed, "") + prefix := f.pullCompletedStyle.Render(fmt.Sprintf("%d Layers", len(layers))) + auxInfo = f.auxInfoStyle.Render(fmt.Sprintf("[%s / %s]", humanize.Bytes(current), humanize.Bytes(size))) + if len(layers) == numCompleted { + auxInfo = f.auxInfoStyle.Render(fmt.Sprintf("[%s] Extracting...", humanize.Bytes(size))) + } + + progStr = fmt.Sprintf("%s%s%s%s", prefix, f.layerCap(false), render, f.layerCap(true)) + } + + return progStr + auxInfo +} + +// formatPullPhase returns a single character that represents the status of a layer pull. +func (f containerdPullStatusFormatter) formatPullPhase(completed bool, inputStr string) string { + if completed { + return f.pullCompletedStyle.Render(f.pullStageChars[len(f.pullStageChars)-1]) + } + return f.pullDownloadStyle.Render(inputStr) +} + +func (f containerdPullStatusFormatter) layerCap(end bool) string { + l := len(f.layerCaps) + if l == 0 { + return "" + } + if end { + return f.layerCaps[l-1] + } + return f.layerCaps[0] +} diff --git a/cmd/syft/cli/ui/handler.go b/cmd/syft/cli/ui/handler.go index 96d078c0c..b2054467f 100644 --- a/cmd/syft/cli/ui/handler.go +++ b/cmd/syft/cli/ui/handler.go @@ -49,6 +49,7 @@ func New(cfg HandlerConfig) *Handler { // register all supported event types with the respective handler functions d.AddHandlers(map[partybus.EventType]bubbly.EventHandlerFn{ stereoscopeEvent.PullDockerImage: h.handlePullDockerImage, + stereoscopeEvent.PullContainerdImage: h.handlePullContainerdImage, stereoscopeEvent.ReadImage: h.handleReadImage, stereoscopeEvent.FetchImage: h.handleFetchImage, syftEvent.PackageCatalogerStarted: h.handlePackageCatalogerStarted, diff --git a/go.mod b/go.mod index f8a3ef97f..775c83254 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,6 @@ require ( github.com/Masterminds/sprig/v3 v3.2.3 github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d github.com/acobaugh/osrelease v0.1.0 - github.com/adrg/xdg v0.4.0 // indirect github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 github.com/anchore/clio v0.0.0-20230823172630-c42d666061af github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe @@ -17,10 +16,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.1.1-0.20230104203445-02e0a6721501 - github.com/anchore/stereoscope v0.0.0-20230911191510-2fc2d6c2503b + github.com/anchore/stereoscope v0.0.0-20230918144411-03f400b02f73 // 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/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 github.com/bmatcuk/doublestar/v4 v4.6.0 github.com/charmbracelet/bubbletea v0.24.2 @@ -47,7 +45,6 @@ require ( github.com/jinzhu/copier v0.4.0 github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b - github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mholt/archiver/v3 v3.5.1 github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5 github.com/mitchellh/go-homedir v1.1.0 @@ -56,19 +53,15 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/opencontainers/go-digest v1.0.0 github.com/pelletier/go-toml v1.9.5 - github.com/pkg/errors v0.9.1 // indirect github.com/saferwall/pe v1.4.5 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/sassoftware/go-rpmutils v0.2.0 // pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5 github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e github.com/sergi/go-diff v1.3.1 - github.com/sirupsen/logrus v1.9.3 // indirect github.com/spdx/tools-golang v0.5.3 github.com/spf13/afero v1.9.5 github.com/spf13/cobra v1.7.0 - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.16.0 // indirect github.com/stretchr/testify v1.8.4 github.com/vbatts/go-mtree v0.5.3 github.com/vifraa/gopom v1.0.0 @@ -76,7 +69,6 @@ require ( github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 - golang.org/x/crypto v0.13.0 // indirect golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 golang.org/x/mod v0.12.0 golang.org/x/net v0.15.0 @@ -87,28 +79,39 @@ require ( require ( dario.cat/mergo v1.0.0 // indirect + github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect + github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 // indirect github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/DataDog/zstd v1.4.5 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.2.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/Microsoft/hcsshim v0.10.0-rc.7 // indirect github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect github.com/acomagu/bufpipe v1.0.4 // indirect + github.com/adrg/xdg v0.4.0 // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect + github.com/andybalholm/brotli v1.0.4 // indirect github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/becheran/wildmatch-go v1.0.0 // indirect github.com/charmbracelet/bubbles v0.16.1 // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/cloudflare/circl v1.3.3 // indirect + github.com/containerd/cgroups v1.1.0 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/containerd/containerd v1.7.0 // indirect + github.com/containerd/continuity v0.3.0 // indirect + github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect + github.com/containerd/ttrpc v1.2.1 // indirect + github.com/containerd/typeurl/v2 v2.1.0 // indirect github.com/cyphar/filepath-securejoin v0.2.4 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/docker/cli v24.0.0+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.4.0 // indirect + github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect @@ -119,6 +122,8 @@ require ( github.com/gkampitakis/ciinfo v0.2.5 // indirect github.com/gkampitakis/go-diff v1.3.2 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect + github.com/go-logr/logr v1.2.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-restruct/restruct v1.2.0-alpha // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -145,27 +150,39 @@ require ( github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/sys/mountinfo v0.6.2 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/signal v0.7.0 // indirect github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/nwaples/rardecode v1.1.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc3 // indirect + github.com/opencontainers/runc v1.1.5 // indirect + github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect + github.com/opencontainers/selinux v1.11.0 // indirect github.com/pborman/indent v1.2.1 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pierrec/lz4/v4 v4.1.15 // indirect github.com/pjbgf/sha1cd v0.3.0 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pkg/profile v1.7.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.2.0 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.2.0 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.16.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/sylabs/sif/v2 v2.11.5 // indirect github.com/sylabs/squashfs v0.6.1 // indirect @@ -182,6 +199,10 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect + go.opencensus.io v0.24.0 // indirect + go.opentelemetry.io/otel v1.14.0 // indirect + go.opentelemetry.io/otel/trace v1.14.0 // indirect + golang.org/x/crypto v0.13.0 // indirect golang.org/x/sync v0.3.0 // indirect golang.org/x/sys v0.12.0 // indirect golang.org/x/text v0.13.0 // indirect diff --git a/go.sum b/go.sum index 4039a4a91..3fcb30d97 100644 --- a/go.sum +++ b/go.sum @@ -51,6 +51,10 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 h1:+vTEFqeoeur6XSq06bs+roX3YiT49gUniJK7Zky7Xjg= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -73,6 +77,8 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8= +github.com/Microsoft/hcsshim v0.10.0-rc.7/go.mod h1:ILuwjA+kNW+MrN/w5un7n3mTqkwsFu4Bp05/okFUZlE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg= github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= @@ -106,8 +112,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.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230911191510-2fc2d6c2503b h1:TFqPfhf7Q+59gW1RuZixnEhh8lYY/FdqTRpSHYKcOsw= -github.com/anchore/stereoscope v0.0.0-20230911191510-2fc2d6c2503b/go.mod h1:CpgmJLpFsMBSq4WnWpBIfk2etTvv/jx00q15MWnzKDY= +github.com/anchore/stereoscope v0.0.0-20230918144411-03f400b02f73 h1:Y+WXZJUam51t3sI8faSaRlxM7uenhCKGC1DO3JEWugU= +github.com/anchore/stereoscope v0.0.0-20230918144411-03f400b02f73/go.mod h1:LMgMrisskjQzdp+T0FgkzhIcYjNEiHZQcq2njZUCE+c= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= @@ -152,9 +158,11 @@ github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU= github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU= +github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= @@ -170,18 +178,30 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= +github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg= github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc= +github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= +github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= +github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= +github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= +github.com/containerd/ttrpc v1.2.1 h1:VWv/Rzx023TBLv4WQ+9WPXlBG/s3rsRjY3i9AJ2BJdE= +github.com/containerd/ttrpc v1.2.1/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak= +github.com/containerd/typeurl/v2 v2.1.0 h1:yNAhJvbNEANt7ck48IlEGOxP7YAp6LLpGn5jZACDNIE= +github.com/containerd/typeurl/v2 v2.1.0/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE= @@ -202,6 +222,9 @@ github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryef github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= @@ -236,6 +259,7 @@ github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA= github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI= github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g= github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -271,6 +295,11 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2 github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= +github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc= github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= @@ -278,6 +307,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= @@ -463,6 +493,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -530,6 +561,15 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= +github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -539,6 +579,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA= github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= @@ -558,6 +599,14 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= +github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= +github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w= +github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= +github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM= @@ -597,6 +646,9 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= @@ -623,6 +675,7 @@ github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd7 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y= github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= @@ -633,6 +686,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -685,6 +739,7 @@ github.com/sylabs/sif/v2 v2.11.5 h1:7ssPH3epSonsTrzbS1YxeJ9KuqAN7ISlSM61a7j/mQM= github.com/sylabs/sif/v2 v2.11.5/go.mod h1:GBoZs9LU3e4yJH1dcZ3Akf/jsqYgy5SeguJQC+zd75Y= github.com/sylabs/squashfs v0.6.1 h1:4hgvHnD9JGlYWwT0bPYNt9zaz23mAV3Js+VEgQoRGYQ= github.com/sylabs/squashfs v0.6.1/go.mod h1:ZwpbPCj0ocIvMy2br6KZmix6Gzh6fsGQcCnydMF+Kx8= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo= github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= @@ -704,6 +759,7 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8= github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/vbatts/go-mtree v0.5.3 h1:S/jYlfG8rZ+a0bhZd+RANXejy7M4Js8fq9U+XoWTd5w= @@ -712,6 +768,8 @@ github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RV github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0= github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20= github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c h1:mM8T8YhiD19d2wYv3vqZn8xpe1ZFJrUJCGlK4IV05xM= @@ -749,10 +807,17 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM= +go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU= +go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M= +go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -800,7 +865,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= @@ -913,6 +977,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -920,6 +985,7 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -963,10 +1029,12 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/syft/source/detection.go b/syft/source/detection.go index f96dc0023..84c622a16 100644 --- a/syft/source/detection.go +++ b/syft/source/detection.go @@ -199,6 +199,8 @@ func parseDefaultImageSource(defaultImageSource string) image.Source { return image.DockerDaemonSource case "podman": return image.PodmanDaemonSource + case "containerd": + return image.ContainerdDaemonSource default: return image.UnknownSource } diff --git a/syft/source/detection_test.go b/syft/source/detection_test.go index 380ca8e65..dd8a0276e 100644 --- a/syft/source/detection_test.go +++ b/syft/source/detection_test.go @@ -247,6 +247,26 @@ func Test_Detect(t *testing.T) { expectedScheme: unknownType, expectedLocation: "", }, + { + name: "podman-image", + userInput: "containerd:anchore/syft", + detection: detectorResult{ + src: image.PodmanDaemonSource, + ref: "anchore/syft", + }, + expectedScheme: containerImageType, + expectedLocation: "anchore/syft", + }, + { + name: "containerd-image", + userInput: "containerd:anchore/syft", + detection: detectorResult{ + src: image.ContainerdDaemonSource, + ref: "anchore/syft", + }, + expectedScheme: containerImageType, + expectedLocation: "anchore/syft", + }, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { From 51243aa65ff20a5f9ce9897e7341360bf067d155 Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 09:07:15 -0400 Subject: [PATCH 063/106] chore(deps): update stereoscope to 41288870305034fade27388afa7326c44eb8ff17 (#2149) Signed-off-by: GitHub Co-authored-by: kzantow --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 775c83254..819566cce 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ 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.1.1-0.20230104203445-02e0a6721501 - github.com/anchore/stereoscope v0.0.0-20230918144411-03f400b02f73 + github.com/anchore/stereoscope v0.0.0-20230918172905-412888703050 // 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/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 diff --git a/go.sum b/go.sum index 3fcb30d97..3c541a95b 100644 --- a/go.sum +++ b/go.sum @@ -112,8 +112,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.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230918144411-03f400b02f73 h1:Y+WXZJUam51t3sI8faSaRlxM7uenhCKGC1DO3JEWugU= -github.com/anchore/stereoscope v0.0.0-20230918144411-03f400b02f73/go.mod h1:LMgMrisskjQzdp+T0FgkzhIcYjNEiHZQcq2njZUCE+c= +github.com/anchore/stereoscope v0.0.0-20230918172905-412888703050 h1:PNt1lyLf2rFqMLiBrlK6pWsVSVkpG/REY1fdQEAJ9T8= +github.com/anchore/stereoscope v0.0.0-20230918172905-412888703050/go.mod h1:75uRHom5WV5ENe3iSK2B37BQvB+47o9HYjpCYaSwlPQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= From 30885ed92efd866af195a8d08e4399501ddfd4f9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 19 Sep 2023 14:37:54 -0400 Subject: [PATCH 064/106] chore(deps): bump github/codeql-action from 2.21.7 to 2.21.8 (#2150) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.7 to 2.21.8. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/04daf014b50eaf774287bf3f0f1869d4b4c4b913...6a28655e3dcb49cb0840ea372fd6d17733edd8a4) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index eb351aa1a..66fe77822 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -45,7 +45,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@04daf014b50eaf774287bf3f0f1869d4b4c4b913 #v2.21.7 + uses: github/codeql-action/init@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 #v2.21.8 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@04daf014b50eaf774287bf3f0f1869d4b4c4b913 #v2.21.7 + uses: github/codeql-action/autobuild@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 #v2.21.8 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@04daf014b50eaf774287bf3f0f1869d4b4c4b913 #v2.21.7 + uses: github/codeql-action/analyze@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 #v2.21.8 From 650f71cbe03f0749680f54b058ac7b581ab11fc5 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Tue, 19 Sep 2023 15:22:10 -0400 Subject: [PATCH 065/106] chore: update to latest stereoscope (#2151) * chore: update to latest stereoscope Signed-off-by: Christopher Phillips * chore: go mod tidy Signed-off-by: Christopher Phillips --------- Signed-off-by: Christopher Phillips --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 819566cce..9463330b0 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ 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.1.1-0.20230104203445-02e0a6721501 - github.com/anchore/stereoscope v0.0.0-20230918172905-412888703050 + github.com/anchore/stereoscope v0.0.0-20230919183137-5841b53a0375 // 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/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 diff --git a/go.sum b/go.sum index 3c541a95b..f11c0d437 100644 --- a/go.sum +++ b/go.sum @@ -112,8 +112,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.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230918172905-412888703050 h1:PNt1lyLf2rFqMLiBrlK6pWsVSVkpG/REY1fdQEAJ9T8= -github.com/anchore/stereoscope v0.0.0-20230918172905-412888703050/go.mod h1:75uRHom5WV5ENe3iSK2B37BQvB+47o9HYjpCYaSwlPQ= +github.com/anchore/stereoscope v0.0.0-20230919183137-5841b53a0375 h1:lpeR4D5PMFXg80QVJgwlJs0QyS8FTBVuFW45Y+3Tat0= +github.com/anchore/stereoscope v0.0.0-20230919183137-5841b53a0375/go.mod h1:75uRHom5WV5ENe3iSK2B37BQvB+47o9HYjpCYaSwlPQ= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= From 40899adb87949aa7351cdbc257f91a43146c3119 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 20 Sep 2023 10:11:44 -0400 Subject: [PATCH 066/106] use annotated tags, update chronicle, fix cache keys (#2154) Signed-off-by: Alex Goodman --- .github/actions/bootstrap/action.yaml | 8 ++++---- .github/workflows/release.yaml | 7 +++---- Makefile | 2 +- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index 8a68cd497..2372a8577 100644 --- a/.github/actions/bootstrap/action.yaml +++ b/.github/actions/bootstrap/action.yaml @@ -45,9 +45,9 @@ runs: with: path: | ~/go/pkg/mod - key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} + key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-mod-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}- + ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-mod-${{ inputs.go-version }}- - name: (cache-miss) Bootstrap project tools shell: bash @@ -61,9 +61,9 @@ runs: with: path: | ~/.cache/go-build - key: ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} + key: ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-build-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}- + ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-build-${{ inputs.go-version }}- - name: (cache-miss) Bootstrap go dependencies shell: bash diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 4596748e8..330ba4eda 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -10,9 +10,6 @@ on: description: tag the latest commit on main with the given version (prefixed with v) required: true -env: - GO_VERSION: "1.21.x" - jobs: quality-gate: environment: release @@ -123,7 +120,9 @@ jobs: - name: Tag release run: | - git tag ${{ github.event.inputs.version }} + git config --global user.name "anchoreci" + git config --global user.email "anchoreci@users.noreply.github.com" + git tag -a ${{ github.event.inputs.version }} -m "Release ${{ github.event.inputs.version }}" git push origin --tags env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/Makefile b/Makefile index edd3732af..55df25a23 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ GLOW_CMD = $(TEMP_DIR)/glow GOLANGCILINT_VERSION := v1.54.2 GOSIMPORTS_VERSION := v0.3.8 BOUNCER_VERSION := v0.4.0 -CHRONICLE_VERSION := v0.7.0 +CHRONICLE_VERSION := v0.8.0 GORELEASER_VERSION := v1.20.0 YAJSV_VERSION := v1.4.1 COSIGN_VERSION := v2.2.0 From 962ff1ec49999fe7f7022df3d73258651b7b12e7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:12:13 -0400 Subject: [PATCH 067/106] chore(deps): bump tibdex/github-app-token from 2.0.0 to 2.1.0 (#2157) Bumps [tibdex/github-app-token](https://github.com/tibdex/github-app-token) from 2.0.0 to 2.1.0. - [Release notes](https://github.com/tibdex/github-app-token/releases) - [Commits](https://github.com/tibdex/github-app-token/compare/0914d50df753bbc42180d982a6550f195390069f...3beb63f4bd073e61482598c45c71c1019b59b73a) --- updated-dependencies: - dependency-name: tibdex/github-app-token dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/update-bootstrap-tools.yml | 2 +- .github/workflows/update-cpe-dictionary-index.yml | 2 +- .github/workflows/update-stereoscope-release.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/update-bootstrap-tools.yml b/.github/workflows/update-bootstrap-tools.yml index 7396258f9..20026bcf9 100644 --- a/.github/workflows/update-bootstrap-tools.yml +++ b/.github/workflows/update-bootstrap-tools.yml @@ -58,7 +58,7 @@ jobs: echo "GLOW=GLOW_LATEST_VERSION" >> $GITHUB_OUTPUT id: latest-versions - - uses: tibdex/github-app-token@0914d50df753bbc42180d982a6550f195390069f #v2.0.0 + - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0 id: generate-token with: app_id: ${{ secrets.TOKEN_APP_ID }} diff --git a/.github/workflows/update-cpe-dictionary-index.yml b/.github/workflows/update-cpe-dictionary-index.yml index e9f333808..a297e710a 100644 --- a/.github/workflows/update-cpe-dictionary-index.yml +++ b/.github/workflows/update-cpe-dictionary-index.yml @@ -27,7 +27,7 @@ jobs: - run: | make generate-cpe-dictionary-index - - uses: tibdex/github-app-token@0914d50df753bbc42180d982a6550f195390069f #v2.0.0 + - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0 id: generate-token with: app_id: ${{ secrets.TOKEN_APP_ID }} diff --git a/.github/workflows/update-stereoscope-release.yml b/.github/workflows/update-stereoscope-release.yml index f1fe98831..4d8098767 100644 --- a/.github/workflows/update-stereoscope-release.yml +++ b/.github/workflows/update-stereoscope-release.yml @@ -35,7 +35,7 @@ jobs: echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_OUTPUT id: latest-version - - uses: tibdex/github-app-token@0914d50df753bbc42180d982a6550f195390069f #v2.0.0 + - uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0 id: generate-token with: app_id: ${{ secrets.TOKEN_APP_ID }} From ba00f3328da36786dc040020893d0f7f635e7ab9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 10:12:33 -0400 Subject: [PATCH 068/106] chore(deps): bump github.com/github/go-spdx/v2 from 2.1.2 to 2.2.0 (#2158) Bumps [github.com/github/go-spdx/v2](https://github.com/github/go-spdx) from 2.1.2 to 2.2.0. - [Release notes](https://github.com/github/go-spdx/releases) - [Commits](https://github.com/github/go-spdx/compare/v2.1.2...v2.2.0) --- updated-dependencies: - dependency-name: github.com/github/go-spdx/v2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9463330b0..7cd37ddea 100644 --- a/go.mod +++ b/go.mod @@ -29,7 +29,7 @@ require ( github.com/docker/docker v24.0.6+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 - github.com/github/go-spdx/v2 v2.1.2 + github.com/github/go-spdx/v2 v2.2.0 github.com/gkampitakis/go-snaps v0.4.10 github.com/go-git/go-billy/v5 v5.5.0 github.com/go-git/go-git/v5 v5.9.0 diff --git a/go.sum b/go.sum index f11c0d437..2a0f71f19 100644 --- a/go.sum +++ b/go.sum @@ -268,8 +268,8 @@ github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbS github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/github/go-spdx/v2 v2.1.2 h1:p+Tv0yMgcuO0/vnMe9Qh4tmUgYhI6AsLVlakZ/Sx+DM= -github.com/github/go-spdx/v2 v2.1.2/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w= +github.com/github/go-spdx/v2 v2.2.0 h1:yBBLMasHA70Ujd35OpL/OjJOWWVNXcJGbars0GinGRI= +github.com/github/go-spdx/v2 v2.2.0/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w= github.com/gkampitakis/ciinfo v0.2.5 h1:K0mac90lGguc1conc46l0YEsB7/nioWCqSnJp/6z8Eo= github.com/gkampitakis/ciinfo v0.2.5/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= From b8f52d570eec15a665d1167825c084ae1766dcd0 Mon Sep 17 00:00:00 2001 From: William Murphy Date: Wed, 20 Sep 2023 11:45:13 -0400 Subject: [PATCH 069/106] chore: stop unit test switch on host arch (#2156) Now that the test fixture pins to a particular digest, there's no need for platform specific architecture switches in this test. Signed-off-by: Will Murphy --- .../fileresolver/container_image_all_layers_test.go | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/syft/internal/fileresolver/container_image_all_layers_test.go b/syft/internal/fileresolver/container_image_all_layers_test.go index 7fb04d56b..a76082d85 100644 --- a/syft/internal/fileresolver/container_image_all_layers_test.go +++ b/syft/internal/fileresolver/container_image_all_layers_test.go @@ -1,9 +1,7 @@ package fileresolver import ( - "fmt" "io" - "runtime" "sort" "testing" @@ -523,11 +521,6 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) { func TestAllLayersResolver_AllLocations(t *testing.T) { img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted") - arch := "x86_64" - if runtime.GOARCH == "arm64" { - arch = "aarch64" - } - resolver, err := NewFromContainerImageAllLayers(img) assert.NoError(t, err) @@ -638,9 +631,9 @@ func TestAllLayersResolver_AllLocations(t *testing.T) { "/lib/apk/db/triggers", "/lib/apk/exec", "/lib/firmware", - fmt.Sprintf("/lib/ld-musl-%s.so.1", arch), + "/lib/ld-musl-x86_64.so.1", "/lib/libapk.so.3.12.0", - fmt.Sprintf("/lib/libc.musl-%s.so.1", arch), + "/lib/libc.musl-x86_64.so.1", "/lib/libcrypto.so.3", "/lib/libssl.so.3", "/lib/libz.so.1", From 58f8c852df6a07a17325d00e27275e9a0d16f227 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 20 Sep 2023 13:39:18 -0400 Subject: [PATCH 070/106] Require ordering of relationships when comparing parser output (#2160) * require ordering of relationships when comparing parser output Signed-off-by: Alex Goodman * [wip] fix cataloger test Signed-off-by: Alex Goodman * change method of relationship sort to simple string dump Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- .gitignore | 1 + go.mod | 2 ++ go.sum | 5 +++++ .../pkg/cataloger/cpp/parse_conanlock_test.go | 5 +++++ .../internal/pkgtest/test_generic_parser.go | 19 +++++++++++++++++++ 5 files changed, 32 insertions(+) diff --git a/.gitignore b/.gitignore index e6fde93b5..6d9a30386 100644 --- a/.gitignore +++ b/.gitignore @@ -30,6 +30,7 @@ VERSION /test/results coverage.txt *.log +test/integration/test-fixtures/**/go.sum # probable archives .images diff --git a/go.mod b/go.mod index 7cd37ddea..fff54e627 100644 --- a/go.mod +++ b/go.mod @@ -77,6 +77,8 @@ require ( modernc.org/sqlite v1.25.0 ) +require github.com/sanity-io/litter v1.5.5 + require ( dario.cat/mergo v1.0.0 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect diff --git a/go.sum b/go.sum index 2a0f71f19..1a62bed16 100644 --- a/go.sum +++ b/go.sum @@ -206,6 +206,7 @@ github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53E github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE= github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= +github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -630,6 +631,7 @@ github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA= github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -668,6 +670,8 @@ github.com/saferwall/pe v1.4.5/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7G github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= +github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo= +github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U= github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE= github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI= github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ= @@ -719,6 +723,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/syft/pkg/cataloger/cpp/parse_conanlock_test.go b/syft/pkg/cataloger/cpp/parse_conanlock_test.go index 5d94b570b..8f6954701 100644 --- a/syft/pkg/cataloger/cpp/parse_conanlock_test.go +++ b/syft/pkg/cataloger/cpp/parse_conanlock_test.go @@ -271,6 +271,11 @@ func TestParseConanlock(t *testing.T) { }, } + // relationships require IDs to be set to be sorted similarly + for i := range expected { + expected[i].SetID() + } + var expectedRelationships = []artifact.Relationship{ { From: expected[1], // boost diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index 573cc5bee..9182ce333 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -9,6 +9,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" + "github.com/sanity-io/litter" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -252,6 +253,23 @@ func (p *CatalogTester) TestCataloger(t *testing.T, cataloger pkg.Cataloger) { } } +var relationshipStringer = litter.Options{ + Compact: true, + StripPackageNames: false, + HidePrivateFields: true, // we want to ignore package IDs + HideZeroValues: true, + StrictGo: true, + //FieldExclusions: ... // these can be added for future values that need to be ignored + //FieldFilter: ... +} + +func relationshipLess(x, y artifact.Relationship) bool { + // we just need a stable sort, the ordering does not need to be sensible + xStr := relationshipStringer.Sdump(x) + yStr := relationshipStringer.Sdump(y) + return xStr < yStr +} + // nolint:funlen func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) { t.Helper() @@ -259,6 +277,7 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi p.compareOptions = append(p.compareOptions, cmpopts.IgnoreFields(pkg.Package{}, "id"), // note: ID is not deterministic for test purposes cmpopts.SortSlices(pkg.Less), + cmpopts.SortSlices(relationshipLess), cmp.Comparer( func(x, y file.LocationSet) bool { xs := x.ToSlice() From 7d0d3e1977deafd40224eea28f0347c0a608d781 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Wed, 20 Sep 2023 14:35:41 -0400 Subject: [PATCH 071/106] fix: prevent errors from clobbering terminal (#2161) Signed-off-by: Keith Zantow --- cmd/syft/cli/cli.go | 8 ++++-- cmd/syft/internal/ui/select.go | 36 ----------------------- cmd/syft/internal/ui/select_windows.go | 15 ---------- cmd/syft/internal/ui/ui.go | 40 +++++++++++++++++++++----- go.mod | 2 +- 5 files changed, 39 insertions(+), 62 deletions(-) delete mode 100644 cmd/syft/internal/ui/select.go delete mode 100644 cmd/syft/internal/ui/select_windows.go diff --git a/cmd/syft/cli/cli.go b/cmd/syft/cli/cli.go index 91b7c5f80..4f4608a66 100644 --- a/cmd/syft/cli/cli.go +++ b/cmd/syft/cli/cli.go @@ -46,10 +46,10 @@ func create(id clio.Identification) (clio.Application, *cobra.Command) { return []clio.UI{noUI}, nil } - h := handler.New(handler.DefaultHandlerConfig()) - return []clio.UI{ - ui.New(h, false, cfg.Log.Quiet), + ui.New(cfg.Log.Quiet, + handler.New(handler.DefaultHandlerConfig()), + ), noUI, }, nil }, @@ -60,7 +60,9 @@ func create(id clio.Identification) (clio.Application, *cobra.Command) { // we can hoist them into the internal packages for global use. stereoscope.SetBus(state.Bus) bus.Set(state.Bus) + redact.Set(state.RedactStore) + log.Set(state.Logger) stereoscope.SetLogger(state.Logger) diff --git a/cmd/syft/internal/ui/select.go b/cmd/syft/internal/ui/select.go deleted file mode 100644 index 27b536192..000000000 --- a/cmd/syft/internal/ui/select.go +++ /dev/null @@ -1,36 +0,0 @@ -//go:build linux || darwin || netbsd -// +build linux darwin netbsd - -package ui - -import ( - "os" - "runtime" - - "golang.org/x/term" - - "github.com/anchore/clio" - handler "github.com/anchore/syft/cmd/syft/cli/ui" -) - -// Select is responsible for determining the specific UI function given select user option, the current platform -// config values, and environment status (such as a TTY being present). The first UI in the returned slice of UIs -// is intended to be used and the UIs that follow are meant to be attempted only in a fallback posture when there -// are environmental problems (e.g. cannot write to the terminal). A writer is provided to capture the output of -// the final SBOM report. -func Select(verbose, quiet bool) (uis []clio.UI) { - isStdoutATty := term.IsTerminal(int(os.Stdout.Fd())) - isStderrATty := term.IsTerminal(int(os.Stderr.Fd())) - notATerminal := !isStderrATty && !isStdoutATty - - switch { - case runtime.GOOS == "windows" || verbose || quiet || notATerminal || !isStderrATty: - uis = append(uis, None(quiet)) - default: - // TODO: it may make sense in the future to pass handler options into select - h := handler.New(handler.DefaultHandlerConfig()) - uis = append(uis, New(h, verbose, quiet)) - } - - return uis -} diff --git a/cmd/syft/internal/ui/select_windows.go b/cmd/syft/internal/ui/select_windows.go deleted file mode 100644 index 0408be53b..000000000 --- a/cmd/syft/internal/ui/select_windows.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build windows -// +build windows - -package ui - -import "github.com/anchore/clio" - -// Select is responsible for determining the specific UI function given select user option, the current platform -// config values, and environment status (such as a TTY being present). The first UI in the returned slice of UIs -// is intended to be used and the UIs that follow are meant to be attempted only in a fallback posture when there -// are environmental problems (e.g. cannot write to the terminal). A writer is provided to capture the output of -// the final SBOM report. -func Select(verbose, quiet bool) (uis []clio.UI) { - return append(uis, None(quiet)) -} diff --git a/cmd/syft/internal/ui/ui.go b/cmd/syft/internal/ui/ui.go index f66454f23..cd31cc4e1 100644 --- a/cmd/syft/internal/ui/ui.go +++ b/cmd/syft/internal/ui/ui.go @@ -1,16 +1,18 @@ package ui import ( + "fmt" "os" "sync" + "time" tea "github.com/charmbracelet/bubbletea" "github.com/wagoodman/go-partybus" + "github.com/anchore/bubbly" "github.com/anchore/bubbly/bubbles/frame" "github.com/anchore/clio" "github.com/anchore/go-logger" - handler "github.com/anchore/syft/cmd/syft/cli/ui" "github.com/anchore/syft/internal/bus" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/event" @@ -29,13 +31,13 @@ type UI struct { subscription partybus.Unsubscribable finalizeEvents []partybus.Event - handler *handler.Handler + handler *bubbly.HandlerCollection frame tea.Model } -func New(h *handler.Handler, _, quiet bool) *UI { +func New(quiet bool, handlers ...bubbly.EventHandler) *UI { return &UI{ - handler: h, + handler: bubbly.NewHandlerCollection(handlers...), frame: frame.New(), running: &sync.WaitGroup{}, quiet: quiet, @@ -72,7 +74,7 @@ func (m *UI) Handle(e partybus.Event) error { func (m *UI) Teardown(force bool) error { if !force { - m.handler.Running.Wait() + m.handler.Wait() m.program.Quit() // typically in all cases we would want to wait for the UI to finish. However there are still error cases // that are not accounted for, resulting in hangs. For now, we'll just wait for the UI to finish in the @@ -80,9 +82,19 @@ func (m *UI) Teardown(force bool) error { // string from the worker (outside of the UI after teardown). m.running.Wait() } else { + _ = runWithTimeout(250*time.Millisecond, func() error { + m.handler.Wait() + return nil + }) + // it may be tempting to use Kill() however it has been found that this can cause the terminal to be left in // a bad state (where Ctrl+C and other control characters no longer works for future processes in that terminal). m.program.Quit() + + _ = runWithTimeout(250*time.Millisecond, func() error { + m.running.Wait() + return nil + }) } // TODO: allow for writing out the full log output to the screen (only a partial log is shown currently) @@ -119,7 +131,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { - // today we treat esc and ctrl+c the same, but in the future when the syft worker has a graceful way to + // today we treat esc and ctrl+c the same, but in the future when the worker has a graceful way to // cancel in-flight work via a context, we can wire up esc to this path with bus.Exit() case "esc", "ctrl+c": bus.ExitWithInterrupt() @@ -135,7 +147,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.finalizeEvents = append(m.finalizeEvents, msg) // why not return tea.Quit here for exit events? because there may be UI components that still need the update-render loop. - // for this reason we'll let the syft event loop call Teardown() which will explicitly wait for these components + // for this reason we'll let the event loop call Teardown() which will explicitly wait for these components return m, nil } @@ -159,3 +171,17 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) { func (m UI) View() string { return m.frame.View() } + +func runWithTimeout(timeout time.Duration, fn func() error) (err error) { + c := make(chan struct{}, 1) + go func() { + err = fn() + c <- struct{}{} + }() + select { + case <-c: + case <-time.After(timeout): + return fmt.Errorf("timed out after %v", timeout) + } + return err +} diff --git a/go.mod b/go.mod index fff54e627..519cc967f 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 golang.org/x/mod v0.12.0 golang.org/x/net v0.15.0 - golang.org/x/term v0.12.0 + golang.org/x/term v0.12.0 // indirect gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.25.0 ) From b7fa75d7f82a6816d307805ac07e6965c799e938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C4=90=E1=BB=97=20Tr=E1=BB=8Dng=20H=E1=BA=A3i?= <41283691+hainenber@users.noreply.github.com> Date: Thu, 21 Sep 2023 01:38:37 +0700 Subject: [PATCH 072/106] chore: switch to stdlib's slices pkg (#2148) * chore: switch to stdlib's slices pkg Signed-off-by: hainenber * fix linting Signed-off-by: Alex Goodman --------- Signed-off-by: hainenber Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman --- cmd/syft/cli/options/output.go | 3 +-- go.mod | 1 - go.sum | 2 -- syft/formats/common/cyclonedxhelpers/format.go | 2 +- syft/formats/common/spdxhelpers/to_format_model.go | 9 ++++++--- syft/formats/formats.go | 3 +-- syft/internal/fileresolver/unindexed_directory.go | 2 +- .../common/cpe/dictionary/index-generator/generate.go | 2 +- syft/pkg/cataloger/package_exclusions.go | 2 +- syft/sbom/sbom.go | 3 +-- 10 files changed, 13 insertions(+), 16 deletions(-) diff --git a/cmd/syft/cli/options/output.go b/cmd/syft/cli/options/output.go index 1c321fd47..034742757 100644 --- a/cmd/syft/cli/options/output.go +++ b/cmd/syft/cli/options/output.go @@ -2,8 +2,7 @@ package options import ( "fmt" - - "golang.org/x/exp/slices" + "slices" "github.com/anchore/clio" "github.com/anchore/syft/syft/formats" diff --git a/go.mod b/go.mod index 519cc967f..afec93164 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,6 @@ require ( github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 - golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 golang.org/x/mod v0.12.0 golang.org/x/net v0.15.0 golang.org/x/term v0.12.0 // indirect diff --git a/go.sum b/go.sum index 1a62bed16..d29f143f7 100644 --- a/go.sum +++ b/go.sum @@ -855,8 +855,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= -golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/syft/formats/common/cyclonedxhelpers/format.go b/syft/formats/common/cyclonedxhelpers/format.go index 4c06a4333..f99e826d3 100644 --- a/syft/formats/common/cyclonedxhelpers/format.go +++ b/syft/formats/common/cyclonedxhelpers/format.go @@ -1,12 +1,12 @@ package cyclonedxhelpers import ( + "slices" "strings" "time" "github.com/CycloneDX/cyclonedx-go" "github.com/google/uuid" - "golang.org/x/exp/slices" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index e15d78d4f..ca6a4f730 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -5,14 +5,13 @@ import ( "crypto/sha1" "fmt" "path" + "slices" "sort" "strings" "time" "github.com/docker/distribution/reference" "github.com/spdx/tools-golang/spdx" - "golang.org/x/exp/maps" - "golang.org/x/exp/slices" "github.com/anchore/packageurl-go" "github.com/anchore/syft/internal" @@ -722,7 +721,11 @@ func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense { var result []*spdx.OtherLicense - ids := maps.Keys(licenses) + var ids []string + for licenseID := range licenses { + ids = append(ids, licenseID) + } + slices.Sort(ids) for _, id := range ids { license := licenses[id] diff --git a/syft/formats/formats.go b/syft/formats/formats.go index b61027ed4..e18b1a852 100644 --- a/syft/formats/formats.go +++ b/syft/formats/formats.go @@ -6,10 +6,9 @@ import ( "fmt" "io" "regexp" + "slices" "strings" - "golang.org/x/exp/slices" - "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/formats/cyclonedxjson" "github.com/anchore/syft/syft/formats/cyclonedxxml" diff --git a/syft/internal/fileresolver/unindexed_directory.go b/syft/internal/fileresolver/unindexed_directory.go index 31e888913..68fdd7965 100644 --- a/syft/internal/fileresolver/unindexed_directory.go +++ b/syft/internal/fileresolver/unindexed_directory.go @@ -6,6 +6,7 @@ import ( "io/fs" "os" "path" + "slices" "sort" "strings" "time" @@ -13,7 +14,6 @@ import ( "github.com/bmatcuk/doublestar/v4" "github.com/mitchellh/go-homedir" "github.com/spf13/afero" - "golang.org/x/exp/slices" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/file" diff --git a/syft/pkg/cataloger/common/cpe/dictionary/index-generator/generate.go b/syft/pkg/cataloger/common/cpe/dictionary/index-generator/generate.go index 30ba79ffc..c0de14ba5 100644 --- a/syft/pkg/cataloger/common/cpe/dictionary/index-generator/generate.go +++ b/syft/pkg/cataloger/common/cpe/dictionary/index-generator/generate.go @@ -7,10 +7,10 @@ import ( "fmt" "io" "log" + "slices" "strings" "github.com/facebookincubator/nvdtools/wfn" - "golang.org/x/exp/slices" "github.com/anchore/syft/syft/pkg/cataloger/common/cpe/dictionary" ) diff --git a/syft/pkg/cataloger/package_exclusions.go b/syft/pkg/cataloger/package_exclusions.go index f99e088fc..c9d67dd61 100644 --- a/syft/pkg/cataloger/package_exclusions.go +++ b/syft/pkg/cataloger/package_exclusions.go @@ -1,7 +1,7 @@ package cataloger import ( - "golang.org/x/exp/slices" + "slices" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/pkg" diff --git a/syft/sbom/sbom.go b/syft/sbom/sbom.go index f95df0955..18058255b 100644 --- a/syft/sbom/sbom.go +++ b/syft/sbom/sbom.go @@ -1,10 +1,9 @@ package sbom import ( + "slices" "sort" - "golang.org/x/exp/slices" - "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" From 8314c0d2cbc0732ce79b383ab260af4bd31ae1ad Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 20 Sep 2023 16:18:44 -0400 Subject: [PATCH 073/106] Correcting behavior based on Syft release v0.91.0 run (#2162) * dont show the title in the release notes Signed-off-by: Alex Goodman * dont upload assets on the release pipeline Signed-off-by: Alex Goodman * bump action-slack action to v3.15.1 Signed-off-by: Alex Goodman * remove custom go mod and build cache Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- .chronicle.yaml | 1 + .github/actions/bootstrap/action.yaml | 32 ++------------------------- .github/workflows/release.yaml | 8 +------ 3 files changed, 4 insertions(+), 37 deletions(-) diff --git a/.chronicle.yaml b/.chronicle.yaml index 400437c89..b74726c41 100644 --- a/.chronicle.yaml +++ b/.chronicle.yaml @@ -1 +1,2 @@ enforce-v0: true # don't make breaking-change label bump major version before 1.0. +title: "" \ No newline at end of file diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index 2372a8577..d8308336b 100644 --- a/.github/actions/bootstrap/action.yaml +++ b/.github/actions/bootstrap/action.yaml @@ -6,10 +6,6 @@ inputs: description: "Go version to install" required: true default: "1.21.x" - use-go-cache: - description: "Restore go cache" - required: true - default: "true" cache-key-prefix: description: "Prefix all cache keys with this value" required: true @@ -25,6 +21,7 @@ inputs: runs: using: "composite" steps: + # note: go mod and build is automatically cached on default with v4+ - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 with: go-version: ${{ inputs.go-version }} @@ -36,38 +33,13 @@ runs: path: ${{ github.workspace }}/.tmp key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('Makefile') }} - # note: we need to keep restoring the go mod cache before bootstrapping tools since `go install` is used in - # some installations of project tools. - - name: Restore go module cache - id: go-mod-cache - if: inputs.use-go-cache == 'true' - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 - with: - path: | - ~/go/pkg/mod - key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-mod-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-mod-${{ inputs.go-version }}- - - name: (cache-miss) Bootstrap project tools shell: bash if: steps.tool-cache.outputs.cache-hit != 'true' run: make bootstrap-tools - - name: Restore go build cache - id: go-cache - if: inputs.use-go-cache == 'true' - uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 - with: - path: | - ~/.cache/go-build - key: ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-build-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-build-${{ inputs.go-version }}- - - - name: (cache-miss) Bootstrap go dependencies + - name: Bootstrap go dependencies shell: bash - if: steps.go-mod-cache.outputs.cache-hit != 'true' && inputs.use-go-cache == 'true' run: make bootstrap-go - name: Install apt packages diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 330ba4eda..6c295de81 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -149,7 +149,7 @@ jobs: with: artifact-name: sbom.spdx.json - - uses: 8398a7/action-slack@49aa08e51e8782f49c601d79ec72d07fd39a4612 #v3.15.1 + - uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #v3.15.1 continue-on-error: true with: status: ${{ job.status }} @@ -158,9 +158,3 @@ jobs: env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }} if: ${{ success() }} - - - uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 #v3.1.3 - with: - name: artifacts - path: dist/**/* - From e34adea749ddb81fcb1242029be55d39f5cf95c3 Mon Sep 17 00:00:00 2001 From: William Murphy Date: Mon, 25 Sep 2023 09:28:18 -0400 Subject: [PATCH 074/106] fix: deterministic java purls (#2170) Previously, which PURL was generated depended on the order of key iteration in maps. Also update an integ test that was apparently only passing because of the previous issue. Signed-off-by: Will Murphy --- syft/pkg/cataloger/common/cpe/java.go | 2 ++ syft/pkg/cataloger/common/cpe/java_groupid_map.go | 1 + test/integration/java_purl_test.go | 6 +++--- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/syft/pkg/cataloger/common/cpe/java.go b/syft/pkg/cataloger/common/cpe/java.go index 7a1a8f7b8..c8bde9f77 100644 --- a/syft/pkg/cataloger/common/cpe/java.go +++ b/syft/pkg/cataloger/common/cpe/java.go @@ -1,6 +1,7 @@ package cpe import ( + "sort" "strings" "github.com/scylladb/go-set/strset" @@ -287,6 +288,7 @@ func GetManifestFieldGroupIDs(manifest *pkg.JavaManifest, fields []string) (grou } } } + sort.Strings(groupIDs) return groupIDs } diff --git a/syft/pkg/cataloger/common/cpe/java_groupid_map.go b/syft/pkg/cataloger/common/cpe/java_groupid_map.go index ece0b004b..634b4dce0 100644 --- a/syft/pkg/cataloger/common/cpe/java_groupid_map.go +++ b/syft/pkg/cataloger/common/cpe/java_groupid_map.go @@ -37,6 +37,7 @@ var DefaultArtifactIDToGroupID = map[string]string{ "ant-weblogic": "org.apache.ant", "ant-xz": "org.apache.ant", "commons-codec": "commons-codec", + "commons-logging": "commons-logging", // see e.g. https://mvnrepository.com/artifact/commons-logging/commons-logging/1.1.1 "okhttp": "com.squareup.okhttp3", "okio": "com.squareup.okio", "spring": "org.springframework", diff --git a/test/integration/java_purl_test.go b/test/integration/java_purl_test.go index 06c54d66d..15ed6dd0d 100644 --- a/test/integration/java_purl_test.go +++ b/test/integration/java_purl_test.go @@ -76,9 +76,9 @@ var expectedPURLs = map[string]string{ "commons-jexl@1.1-hudson-20090508": "pkg:maven/org.jvnet.hudson/commons-jexl@1.1-hudson-20090508", "commons-lang@2.4": "pkg:maven/commons-lang/commons-lang@2.4", "commons-lang@2.5": "pkg:maven/commons-lang/commons-lang@2.5", - "commons-logging@1.0.4": "pkg:maven/org.apache.commons.logging/commons-logging@1.0.4", - "commons-logging@1.1": "pkg:maven/org.apache.commons.logging/commons-logging@1.1", - "commons-logging@1.1.1": "pkg:maven/commons-logging/commons-logging@1.1.1", + "commons-logging@1.0.4": "pkg:maven/commons-logging/commons-logging@1.0.4", // see https://mvnrepository.com/artifact/commons-logging/commons-logging/1.0.4 + "commons-logging@1.1": "pkg:maven/commons-logging/commons-logging@1.1", // see https://mvnrepository.com/artifact/commons-logging/commons-logging/1.1 + "commons-logging@1.1.1": "pkg:maven/commons-logging/commons-logging@1.1.1", // see https://mvnrepository.com/artifact/commons-logging/commons-logging/1.1.1 "commons-pool@1.3": "pkg:maven/commons-pool/commons-pool@1.3", "crypto-util@1.0": "pkg:maven/org.jvnet.hudson/crypto-util@1.0", "cvs@1.2": "pkg:maven/org.jvnet.hudson.plugins/cvs@1.2", From 8a414b5366fb81333fa8e8888880134d1b70badc Mon Sep 17 00:00:00 2001 From: William Murphy Date: Mon, 25 Sep 2023 10:29:56 -0400 Subject: [PATCH 075/106] chore: bump stereoscope to fix data race in UI code (#2173) Pulls in a fix in go-progress that prevents a race in the UI code when scanning large images. Signed-off-by: Will Murphy --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index afec93164..6758f2ce3 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ 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.1.1-0.20230104203445-02e0a6721501 - github.com/anchore/stereoscope v0.0.0-20230919183137-5841b53a0375 + github.com/anchore/stereoscope v0.0.0-20230925132944-bf05af58eb44 // 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/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 @@ -66,7 +66,7 @@ require ( github.com/vbatts/go-mtree v0.5.3 github.com/vifraa/gopom v1.0.0 github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 - github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c + github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 golang.org/x/mod v0.12.0 diff --git a/go.sum b/go.sum index d29f143f7..944ef0c3e 100644 --- a/go.sum +++ b/go.sum @@ -112,8 +112,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.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8= github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4= -github.com/anchore/stereoscope v0.0.0-20230919183137-5841b53a0375 h1:lpeR4D5PMFXg80QVJgwlJs0QyS8FTBVuFW45Y+3Tat0= -github.com/anchore/stereoscope v0.0.0-20230919183137-5841b53a0375/go.mod h1:75uRHom5WV5ENe3iSK2B37BQvB+47o9HYjpCYaSwlPQ= +github.com/anchore/stereoscope v0.0.0-20230925132944-bf05af58eb44 h1:dKMvcpgqsRrX1ZWyqG53faVW+BahlaAO1RUEc7/rOjA= +github.com/anchore/stereoscope v0.0.0-20230925132944-bf05af58eb44/go.mod h1:RtbeDCho0pxkPqrB1QNf/Jlxfc9juLmtYZAf2UbpJfk= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y= github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY= @@ -777,8 +777,8 @@ github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYp github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA= github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20= -github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c h1:mM8T8YhiD19d2wYv3vqZn8xpe1ZFJrUJCGlK4IV05xM= -github.com/wagoodman/go-progress v0.0.0-20230911172108-cf810b7e365c/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA= +github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 h1:0KGbf+0SMg+UFy4e1A/CPVvXn21f1qtWdeJwxZFoQG8= +github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= From 534a5f54b037df898475b6fe9b6a65844fba5ab5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 25 Sep 2023 15:07:19 -0400 Subject: [PATCH 076/106] chore(deps): bump github.com/spf13/afero from 1.9.5 to 1.10.0 (#2174) Bumps [github.com/spf13/afero](https://github.com/spf13/afero) from 1.9.5 to 1.10.0. - [Release notes](https://github.com/spf13/afero/releases) - [Commits](https://github.com/spf13/afero/compare/v1.9.5...v1.10.0) --- updated-dependencies: - dependency-name: github.com/spf13/afero dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6758f2ce3..4f7c21593 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e github.com/sergi/go-diff v1.3.1 github.com/spdx/tools-golang v0.5.3 - github.com/spf13/afero v1.9.5 + github.com/spf13/afero v1.10.0 github.com/spf13/cobra v1.7.0 github.com/stretchr/testify v1.8.4 github.com/vbatts/go-mtree v0.5.3 diff --git a/go.sum b/go.sum index 944ef0c3e..128e4cb6f 100644 --- a/go.sum +++ b/go.sum @@ -702,8 +702,8 @@ github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQr github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= +github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= From 351c683cb40e0016d4b2eb68849536729a7b6ba7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 07:49:13 -0400 Subject: [PATCH 077/106] chore(deps): bump actions/checkout from 4.0.0 to 4.1.0 (#2172) Bumps [actions/checkout](https://github.com/actions/checkout) from 4.0.0 to 4.1.0. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/3df4ab11eba7bda6032a0b82a6bb43b11571feac...8ade135a41bc03ea155e62e844d188df1ea18608) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/benchmark-testing.yaml | 2 +- .github/workflows/codeql-analysis.yml | 2 +- .github/workflows/release.yaml | 4 ++-- .github/workflows/update-bootstrap-tools.yml | 2 +- .github/workflows/update-cpe-dictionary-index.yml | 2 +- .github/workflows/update-stereoscope-release.yml | 2 +- .github/workflows/validations.yaml | 14 +++++++------- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.github/workflows/benchmark-testing.yaml b/.github/workflows/benchmark-testing.yaml index 556fc5ab8..3f50679d9 100644 --- a/.github/workflows/benchmark-testing.yaml +++ b/.github/workflows/benchmark-testing.yaml @@ -17,7 +17,7 @@ jobs: # the job by event. steps: - name: Checkout code - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac # v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 66fe77822..34bf8b842 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - name: Install Go uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 6c295de81..fc958856e 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -15,7 +15,7 @@ jobs: environment: release runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - name: Check if tag already exists # note: this will fail if the tag already exists @@ -95,7 +95,7 @@ jobs: contents: write packages: write steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 with: fetch-depth: 0 diff --git a/.github/workflows/update-bootstrap-tools.yml b/.github/workflows/update-bootstrap-tools.yml index 20026bcf9..997aa961a 100644 --- a/.github/workflows/update-bootstrap-tools.yml +++ b/.github/workflows/update-bootstrap-tools.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'anchore/syft' # only run for main repo steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 with: diff --git a/.github/workflows/update-cpe-dictionary-index.yml b/.github/workflows/update-cpe-dictionary-index.yml index a297e710a..571b06e7e 100644 --- a/.github/workflows/update-cpe-dictionary-index.yml +++ b/.github/workflows/update-cpe-dictionary-index.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'anchore/syft' # only run for main repo steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - uses: actions/setup-go@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 with: diff --git a/.github/workflows/update-stereoscope-release.yml b/.github/workflows/update-stereoscope-release.yml index 4d8098767..b5fad265f 100644 --- a/.github/workflows/update-stereoscope-release.yml +++ b/.github/workflows/update-stereoscope-release.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest if: github.repository == 'anchore/syft' # only run for main repo steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0 with: diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 8a20bae14..081079896 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -16,7 +16,7 @@ jobs: name: "Static analysis" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -30,7 +30,7 @@ jobs: name: "Unit tests" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -74,7 +74,7 @@ jobs: name: "Integration tests" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -96,7 +96,7 @@ jobs: name: "Build snapshot artifacts" runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap @@ -127,7 +127,7 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - name: Download snapshot build uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 @@ -163,7 +163,7 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: macos-latest steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - name: Download snapshot build uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2 @@ -191,7 +191,7 @@ jobs: needs: [Build-Snapshot-Artifacts] runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0 + - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0 - name: Bootstrap environment uses: ./.github/actions/bootstrap From 8f57d22f639e132cbd87324abacd864232c611ce Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Tue, 26 Sep 2023 11:14:25 -0400 Subject: [PATCH 078/106] chore(deps): update bootstrap tools to latest versions (#2171) Signed-off-by: GitHub Co-authored-by: spiffcs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 55df25a23..436131e80 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ GOLANGCILINT_VERSION := v1.54.2 GOSIMPORTS_VERSION := v0.3.8 BOUNCER_VERSION := v0.4.0 CHRONICLE_VERSION := v0.8.0 -GORELEASER_VERSION := v1.20.0 +GORELEASER_VERSION := v1.21.1 YAJSV_VERSION := v1.4.1 COSIGN_VERSION := v2.2.0 QUILL_VERSION := v0.4.1 From 7b1af8721d6eca49a7241f79f54ed8778a8ff68c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:04:52 -0400 Subject: [PATCH 079/106] chore(deps): bump github.com/saferwall/pe from 1.4.5 to 1.4.6 (#2180) Bumps [github.com/saferwall/pe](https://github.com/saferwall/pe) from 1.4.5 to 1.4.6. - [Release notes](https://github.com/saferwall/pe/releases) - [Changelog](https://github.com/saferwall/pe/blob/main/CHANGELOG.md) - [Commits](https://github.com/saferwall/pe/compare/v1.4.5...v1.4.6) --- updated-dependencies: - dependency-name: github.com/saferwall/pe dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4f7c21593..39aa6369c 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/opencontainers/go-digest v1.0.0 github.com/pelletier/go-toml v1.9.5 - github.com/saferwall/pe v1.4.5 + github.com/saferwall/pe v1.4.6 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/sassoftware/go-rpmutils v0.2.0 // pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5 diff --git a/go.sum b/go.sum index 128e4cb6f..ec6f0ce96 100644 --- a/go.sum +++ b/go.sum @@ -665,8 +665,8 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/saferwall/pe v1.4.5 h1:ACIe9QnLTdiRIbuN3BbEUI8SqCQmNrPBb7O2lJTmsK4= -github.com/saferwall/pe v1.4.5/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s= +github.com/saferwall/pe v1.4.6 h1:HmUyGh70q8Q2hPNT3ksBYy5l+nV920AyzzmHKbYaJ9Y= +github.com/saferwall/pe v1.4.6/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= From 44e54802380f949a1476c25c1b48c5e3488e89bc Mon Sep 17 00:00:00 2001 From: "anchore-actions-token-generator[bot]" <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 15:05:15 -0400 Subject: [PATCH 080/106] chore(deps): update bootstrap tools to latest versions (#2178) Signed-off-by: GitHub Co-authored-by: spiffcs --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 436131e80..1441c30c6 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ GOLANGCILINT_VERSION := v1.54.2 GOSIMPORTS_VERSION := v0.3.8 BOUNCER_VERSION := v0.4.0 CHRONICLE_VERSION := v0.8.0 -GORELEASER_VERSION := v1.21.1 +GORELEASER_VERSION := v1.21.2 YAJSV_VERSION := v1.4.1 COSIGN_VERSION := v2.2.0 QUILL_VERSION := v0.4.1 From 38d5ef2c84e9bb8245c657daf2a3f97facde0a8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 28 Sep 2023 10:56:08 -0400 Subject: [PATCH 081/106] chore(deps): bump github/codeql-action from 2.21.8 to 2.21.9 (#2182) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.8 to 2.21.9. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/6a28655e3dcb49cb0840ea372fd6d17733edd8a4...ddccb873888234080b77e9bc2d4764d5ccaaccf9) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 34bf8b842..f750cb94f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -45,7 +45,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 #v2.21.8 + uses: github/codeql-action/init@ddccb873888234080b77e9bc2d4764d5ccaaccf9 #v2.21.9 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 #v2.21.8 + uses: github/codeql-action/autobuild@ddccb873888234080b77e9bc2d4764d5ccaaccf9 #v2.21.9 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@6a28655e3dcb49cb0840ea372fd6d17733edd8a4 #v2.21.8 + uses: github/codeql-action/analyze@ddccb873888234080b77e9bc2d4764d5ccaaccf9 #v2.21.9 From 45625dae94b15f64a861abde7f272940aa311db7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 12:33:42 -0400 Subject: [PATCH 082/106] chore(deps): bump github.com/gkampitakis/go-snaps from 0.4.10 to 0.4.11 (#2191) Bumps [github.com/gkampitakis/go-snaps](https://github.com/gkampitakis/go-snaps) from 0.4.10 to 0.4.11. - [Release notes](https://github.com/gkampitakis/go-snaps/releases) - [Commits](https://github.com/gkampitakis/go-snaps/compare/v0.4.10...v0.4.11) --- updated-dependencies: - dependency-name: github.com/gkampitakis/go-snaps dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 39aa6369c..36ad5f3b1 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,7 @@ require ( github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 github.com/github/go-spdx/v2 v2.2.0 - github.com/gkampitakis/go-snaps v0.4.10 + github.com/gkampitakis/go-snaps v0.4.11 github.com/go-git/go-billy/v5 v5.5.0 github.com/go-git/go-git/v5 v5.9.0 github.com/go-test/deep v1.1.0 diff --git a/go.sum b/go.sum index ec6f0ce96..f921523ec 100644 --- a/go.sum +++ b/go.sum @@ -275,8 +275,8 @@ github.com/gkampitakis/ciinfo v0.2.5 h1:K0mac90lGguc1conc46l0YEsB7/nioWCqSnJp/6z github.com/gkampitakis/ciinfo v0.2.5/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= -github.com/gkampitakis/go-snaps v0.4.10 h1:rUcTH4k6+rzw6ylDALMifzw2c/f9cG3NZe/n+7Ygdr4= -github.com/gkampitakis/go-snaps v0.4.10/go.mod h1:N4TpqxI4CqKUfHzDFqrqZ5UP0I0ESz2g2NMslh7MiJw= +github.com/gkampitakis/go-snaps v0.4.11 h1:7qKaozbTQEvHeG0bt6osdjdTDTnWYdIrLx43a7DEDu4= +github.com/gkampitakis/go-snaps v0.4.11/go.mod h1:N4TpqxI4CqKUfHzDFqrqZ5UP0I0ESz2g2NMslh7MiJw= github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4= github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0= github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY= From 86005d1593c2c73f96e2958dde8624e2b897b47e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Oct 2023 12:34:59 -0400 Subject: [PATCH 083/106] chore(deps): bump modernc.org/sqlite from 1.25.0 to 1.26.0 (#2189) Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.25.0 to 1.26.0. - [Commits](https://gitlab.com/cznic/sqlite/compare/v1.25.0...v1.26.0) --- updated-dependencies: - dependency-name: modernc.org/sqlite dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 36ad5f3b1..1ce97375b 100644 --- a/go.mod +++ b/go.mod @@ -73,7 +73,7 @@ require ( golang.org/x/net v0.15.0 golang.org/x/term v0.12.0 // indirect gopkg.in/yaml.v3 v3.0.1 - modernc.org/sqlite v1.25.0 + modernc.org/sqlite v1.26.0 ) require github.com/sanity-io/litter v1.5.5 diff --git a/go.sum b/go.sum index f921523ec..5510e485e 100644 --- a/go.sum +++ b/go.sum @@ -1349,8 +1349,8 @@ modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o= modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= -modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA= -modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU= +modernc.org/sqlite v1.26.0 h1:SocQdLRSYlA8W99V8YH0NES75thx19d9sB/aFc4R8Lw= +modernc.org/sqlite v1.26.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU= modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= From 21878784a8babc3931a3ecc394c4ced7d81aabfd Mon Sep 17 00:00:00 2001 From: Mohammad Sharief Baig <56253776+sharief007@users.noreply.github.com> Date: Tue, 3 Oct 2023 20:43:07 +0530 Subject: [PATCH 084/106] chore: improve --output help text and deprecate --file (#2187) Signed-off-by: Mohammad Sharief Baig --- cmd/syft/cli/options/output.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/cmd/syft/cli/options/output.go b/cmd/syft/cli/options/output.go index 034742757..09c226f3e 100644 --- a/cmd/syft/cli/options/output.go +++ b/cmd/syft/cli/options/output.go @@ -5,6 +5,7 @@ import ( "slices" "github.com/anchore/clio" + "github.com/anchore/fangs" "github.com/anchore/syft/syft/formats" "github.com/anchore/syft/syft/formats/table" "github.com/anchore/syft/syft/formats/template" @@ -30,7 +31,7 @@ func DefaultOutput() MultiOutput { func (o *MultiOutput) AddFlags(flags clio.FlagSet) { flags.StringArrayVarP(&o.Outputs, "output", "o", - fmt.Sprintf("report output format, options=%v", formats.AllIDs())) + fmt.Sprintf("report output format (= to output to a file), formats=%v", formats.AllIDs())) flags.StringVarP(&o.OutputTemplatePath, "template", "t", "specify the path to a Go template file") @@ -63,7 +64,7 @@ func (o *SingleOutput) SBOMWriter(file string) (sbom.Writer, error) { return makeSBOMWriter([]string{o.Output}, file, o.OutputTemplatePath) } -// OutputFile is only the --file argument +// Deprecated: OutputFile is only the --file argument type OutputFile struct { File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to } @@ -76,6 +77,11 @@ var _ interface { func (o *OutputFile) AddFlags(flags clio.FlagSet) { flags.StringVarP(&o.File, "file", "", "file to write the default report output to (default is STDOUT)") + + if pfp, ok := flags.(fangs.PFlagSetProvider); ok { + flagSet := pfp.PFlagSet() + flagSet.Lookup("file").Deprecated = "use: output" + } } func (o *OutputFile) PostLoad() error { From 1067dc2ce9304c8c5b094a6e1b4ad3beabb91746 Mon Sep 17 00:00:00 2001 From: chavacava Date: Wed, 4 Oct 2023 20:06:12 +0200 Subject: [PATCH 085/106] chore: removes unnecessary conditional (#2194) Signed-off-by: chavacava --- cmd/syft/cli/options/writer_test.go | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cmd/syft/cli/options/writer_test.go b/cmd/syft/cli/options/writer_test.go index f71d2b9d3..dfdc60a39 100644 --- a/cmd/syft/cli/options/writer_test.go +++ b/cmd/syft/cli/options/writer_test.go @@ -183,11 +183,7 @@ func Test_newSBOMMultiWriter(t *testing.T) { switch w := mw.writers[i].(type) { case *sbomStreamWriter: assert.Equal(t, string(w.format.ID()), e.format) - if e.file != "" { - assert.NotNil(t, w.out) - } else { - assert.NotNil(t, w.out) - } + assert.NotNil(t, w.out) if e.file != "" { assert.FileExists(t, tmp+e.file) } From 37bb95f5c90ad7fa7a1aa83fe8ed4c95b2815c7a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 11:50:05 -0400 Subject: [PATCH 086/106] chore(deps): bump golang.org/x/mod from 0.12.0 to 0.13.0 (#2199) Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.12.0 to 0.13.0. - [Commits](https://github.com/golang/mod/compare/v0.12.0...v0.13.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 1ce97375b..b1d644b7a 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 - golang.org/x/mod v0.12.0 + golang.org/x/mod v0.13.0 golang.org/x/net v0.15.0 golang.org/x/term v0.12.0 // indirect gopkg.in/yaml.v3 v3.0.1 diff --git a/go.sum b/go.sum index 5510e485e..fbf3a8fba 100644 --- a/go.sum +++ b/go.sum @@ -883,8 +883,8 @@ golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= -golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From 127fac8ca9ee268e636c105ce84e0ff4ab256cd8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Oct 2023 13:02:30 -0400 Subject: [PATCH 087/106] chore(deps): bump github.com/saferwall/pe from 1.4.6 to 1.4.7 (#2198) Bumps [github.com/saferwall/pe](https://github.com/saferwall/pe) from 1.4.6 to 1.4.7. - [Release notes](https://github.com/saferwall/pe/releases) - [Changelog](https://github.com/saferwall/pe/blob/main/CHANGELOG.md) - [Commits](https://github.com/saferwall/pe/compare/v1.4.6...v1.4.7) --- updated-dependencies: - dependency-name: github.com/saferwall/pe dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b1d644b7a..f97c56e94 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/olekukonko/tablewriter v0.0.5 github.com/opencontainers/go-digest v1.0.0 github.com/pelletier/go-toml v1.9.5 - github.com/saferwall/pe v1.4.6 + github.com/saferwall/pe v1.4.7 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d github.com/sassoftware/go-rpmutils v0.2.0 // pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5 diff --git a/go.sum b/go.sum index fbf3a8fba..c8bfac96a 100644 --- a/go.sum +++ b/go.sum @@ -665,8 +665,8 @@ github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUz github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/saferwall/pe v1.4.6 h1:HmUyGh70q8Q2hPNT3ksBYy5l+nV920AyzzmHKbYaJ9Y= -github.com/saferwall/pe v1.4.6/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s= +github.com/saferwall/pe v1.4.7 h1:A+G3DxX49paJ5OsxBfHKskhyDtmTjShlDmBd81IsHlQ= +github.com/saferwall/pe v1.4.7/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= From 235ad2e749ae66d37e7fe15612a264d8ad0e151d Mon Sep 17 00:00:00 2001 From: Marc-Etienne Vargenau Date: Thu, 5 Oct 2023 20:48:30 +0200 Subject: [PATCH 088/106] Add exact syntax of the conversion formats (#2196) Signed-off-by: Marc-Etienne Vargenau --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ef6969c82..4aa685b5a 100644 --- a/README.md +++ b/README.md @@ -390,11 +390,11 @@ syft convert -o [=] This feature is experimental and data might be lost when converting formats. Packages are the main SBOM component easily transferable across formats, whereas files and relationships, as well as other information Syft doesn't support, are more likely to be lost. We support formats with wide community usage AND good encode/decode support by Syft. The supported formats are: -- Syft JSON -- SPDX 2.2 JSON -- SPDX 2.2 tag-value -- CycloneDX 1.4 JSON -- CycloneDX 1.4 XML +- Syft JSON (```-o syft-json```) +- SPDX 2.2 JSON (```-o spdx-json```) +- SPDX 2.2 tag-value (```-o spdx-tag-value```) +- CycloneDX 1.4 JSON (```-o cyclonedx-json```) +- CycloneDX 1.4 XML (```-o cyclonedx-xml```) Conversion example: ```sh From 30f0686353aa86bf6d44028174fa01d2ca055752 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Fri, 6 Oct 2023 10:56:47 -0400 Subject: [PATCH 089/106] chore: update license list to 3.22 (#2201) --- internal/spdxlicense/license_list.go | 55 +++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/internal/spdxlicense/license_list.go b/internal/spdxlicense/license_list.go index 8ffa768c1..d7fa6645e 100644 --- a/internal/spdxlicense/license_list.go +++ b/internal/spdxlicense/license_list.go @@ -1,9 +1,9 @@ // Code generated by go generate; DO NOT EDIT. -// This file was generated by robots at 2023-07-11 11:55:35.533815 -0400 EDT m=+0.695308614 +// This file was generated by robots at 2023-10-05 22:45:16.827263 -0400 EDT m=+0.138031521 // using data from https://spdx.org/licenses/licenses.json package spdxlicense -const Version = "3.21" +const Version = "3.22" var licenseIDs = map[string]string{ "0bsd": "0BSD", @@ -14,6 +14,7 @@ var licenseIDs = map[string]string{ "adobe2006.0": "Adobe-2006", "adobe2006.0.0": "Adobe-2006", "adobeglyph": "Adobe-Glyph", + "adobeutopia": "Adobe-Utopia", "adsl": "ADSL", "afl1": "AFL-1.1", "afl1.1": "AFL-1.1", @@ -145,6 +146,8 @@ var licenseIDs = map[string]string{ "bsd3.0.0clause": "BSD-3-Clause", "bsd3.0.0clauseattribution": "BSD-3-Clause-Attribution", "bsd3.0.0clauseclear": "BSD-3-Clause-Clear", + "bsd3.0.0clauseflex": "BSD-3-Clause-flex", + "bsd3.0.0clausehp": "BSD-3-Clause-HP", "bsd3.0.0clauselbnl": "BSD-3-Clause-LBNL", "bsd3.0.0clausemodification": "BSD-3-Clause-Modification", "bsd3.0.0clausenomilitarylicense": "BSD-3-Clause-No-Military-License", @@ -152,9 +155,12 @@ var licenseIDs = map[string]string{ "bsd3.0.0clausenonuclearlicense2014": "BSD-3-Clause-No-Nuclear-License-2014", "bsd3.0.0clausenonuclearwarranty": "BSD-3-Clause-No-Nuclear-Warranty", "bsd3.0.0clauseopenmpi": "BSD-3-Clause-Open-MPI", + "bsd3.0.0clausesun": "BSD-3-Clause-Sun", "bsd3.0clause": "BSD-3-Clause", "bsd3.0clauseattribution": "BSD-3-Clause-Attribution", "bsd3.0clauseclear": "BSD-3-Clause-Clear", + "bsd3.0clauseflex": "BSD-3-Clause-flex", + "bsd3.0clausehp": "BSD-3-Clause-HP", "bsd3.0clauselbnl": "BSD-3-Clause-LBNL", "bsd3.0clausemodification": "BSD-3-Clause-Modification", "bsd3.0clausenomilitarylicense": "BSD-3-Clause-No-Military-License", @@ -162,9 +168,12 @@ var licenseIDs = map[string]string{ "bsd3.0clausenonuclearlicense2014": "BSD-3-Clause-No-Nuclear-License-2014", "bsd3.0clausenonuclearwarranty": "BSD-3-Clause-No-Nuclear-Warranty", "bsd3.0clauseopenmpi": "BSD-3-Clause-Open-MPI", + "bsd3.0clausesun": "BSD-3-Clause-Sun", "bsd3clause": "BSD-3-Clause", "bsd3clauseattribution": "BSD-3-Clause-Attribution", "bsd3clauseclear": "BSD-3-Clause-Clear", + "bsd3clauseflex": "BSD-3-Clause-flex", + "bsd3clausehp": "BSD-3-Clause-HP", "bsd3clauselbnl": "BSD-3-Clause-LBNL", "bsd3clausemodification": "BSD-3-Clause-Modification", "bsd3clausenomilitarylicense": "BSD-3-Clause-No-Military-License", @@ -172,6 +181,7 @@ var licenseIDs = map[string]string{ "bsd3clausenonuclearlicense2014": "BSD-3-Clause-No-Nuclear-License-2014", "bsd3clausenonuclearwarranty": "BSD-3-Clause-No-Nuclear-Warranty", "bsd3clauseopenmpi": "BSD-3-Clause-Open-MPI", + "bsd3clausesun": "BSD-3-Clause-Sun", "bsd4.0.0clause": "BSD-4-Clause", "bsd4.0.0clauseshortened": "BSD-4-Clause-Shortened", "bsd4.0.0clauseuc": "BSD-4-Clause-UC", @@ -189,8 +199,10 @@ var licenseIDs = map[string]string{ "bsd4tahoe": "BSD-4.3TAHOE", "bsdadvertisingacknowledgement": "BSD-Advertising-Acknowledgement", "bsdattributionhpnddisclaimer": "BSD-Attribution-HPND-disclaimer", + "bsdinfernonettverk": "BSD-Inferno-Nettverk", "bsdprotection": "BSD-Protection", "bsdsourcecode": "BSD-Source-Code", + "bsdsystemics": "BSD-Systemics", "bsl1": "BSL-1.0", "bsl1.0": "BSL-1.0", "bsl1.0.0": "BSL-1.0", @@ -403,6 +415,7 @@ var licenseIDs = map[string]string{ "cernohlw2.0": "CERN-OHL-W-2.0", "cernohlw2.0.0": "CERN-OHL-W-2.0", "cfitsio": "CFITSIO", + "checkcvs": "check-cvs", "checkmk": "checkmk", "clartistic": "ClArtistic", "clips": "Clips", @@ -432,6 +445,7 @@ var licenseIDs = map[string]string{ "cpol1": "CPOL-1.02", "cpol1.02": "CPOL-1.02", "cpol1.02.0": "CPOL-1.02", + "cronyx": "Cronyx", "crossword": "Crossword", "crystalstacker": "CrystalStacker", "cuaopl1": "CUA-OPL-1.0", @@ -449,6 +463,9 @@ var licenseIDs = map[string]string{ "dldeby2": "DL-DE-BY-2.0", "dldeby2.0": "DL-DE-BY-2.0", "dldeby2.0.0": "DL-DE-BY-2.0", + "dldezero2": "DL-DE-ZERO-2.0", + "dldezero2.0": "DL-DE-ZERO-2.0", + "dldezero2.0.0": "DL-DE-ZERO-2.0", "doc": "DOC", "dotseqn": "Dotseqn", "drl1": "DRL-1.0", @@ -500,7 +517,9 @@ var licenseIDs = map[string]string{ "eupl1.2.0": "EUPL-1.2", "eurosym": "Eurosym", "fair": "Fair", + "fbm": "FBM", "fdkaac": "FDK-AAC", + "fergusontwofish": "Ferguson-Twofish", "frameworx1": "Frameworx-1.0", "frameworx1.0": "Frameworx-1.0", "frameworx1.0.0": "Frameworx-1.0", @@ -511,6 +530,8 @@ var licenseIDs = map[string]string{ "fsfullr": "FSFULLR", "fsfullrwd": "FSFULLRWD", "ftl": "FTL", + "furuseth": "Furuseth", + "fwlw": "fwlw", "gd": "GD", "gfdl1": "GFDL-1.1-only", "gfdl1+": "GFDL-1.1-or-later", @@ -658,11 +679,21 @@ var licenseIDs = map[string]string{ "hp1986": "HP-1986", "hp1986.0": "HP-1986", "hp1986.0.0": "HP-1986", + "hp1989": "HP-1989", + "hp1989.0": "HP-1989", + "hp1989.0.0": "HP-1989", "hpnd": "HPND", + "hpnddec": "HPND-DEC", + "hpnddoc": "HPND-doc", + "hpnddocsell": "HPND-doc-sell", "hpndexportus": "HPND-export-US", + "hpndexportusmodify": "HPND-export-US-modify", "hpndmarkuskuhn": "HPND-Markus-Kuhn", + "hpndpbmplus": "HPND-Pbmplus", + "hpndsellregexpr": "HPND-sell-regexpr", "hpndsellvariant": "HPND-sell-variant", "hpndsellvariantmitdisclaimer": "HPND-sell-variant-MIT-disclaimer", + "hpnduc": "HPND-UC", "htmltidy": "HTMLTIDY", "ibmpibs": "IBM-pibs", "icu": "ICU", @@ -695,6 +726,7 @@ var licenseIDs = map[string]string{ "jplimage": "JPL-image", "jpnic": "JPNIC", "json": "JSON", + "kastrup": "Kastrup", "kazlib": "Kazlib", "knuthctan": "Knuth-CTAN", "lal1": "LAL-1.2", @@ -788,14 +820,18 @@ var licenseIDs = map[string]string{ "lppl1.3c": "LPPL-1.3c", "lppl1a": "LPPL-1.3a", "lppl1c": "LPPL-1.3c", + "lsof": "lsof", + "lucidabitmapfonts": "Lucida-Bitmap-Fonts", "lzmasdk9": "LZMA-SDK-9.22", "lzmasdk9.11.0to9.20": "LZMA-SDK-9.11-to-9.20", "lzmasdk9.11to9.20": "LZMA-SDK-9.11-to-9.20", "lzmasdk9.22": "LZMA-SDK-9.22", "lzmasdk9.22.0": "LZMA-SDK-9.22", "lzmasdk9to9.20": "LZMA-SDK-9.11-to-9.20", + "magaz": "magaz", "makeindex": "MakeIndex", "martinbirgmeier": "Martin-Birgmeier", + "mcpheeslideshow": "McPhee-slideshow", "metamail": "metamail", "minpack": "Minpack", "miros": "MirOS", @@ -809,8 +845,11 @@ var licenseIDs = map[string]string{ "mitmodernvariant": "MIT-Modern-Variant", "mitnfa": "MITNFA", "mitopengroup": "MIT-open-group", + "mittestregex": "MIT-testregex", "mitwu": "MIT-Wu", + "mmixware": "MMIXware", "motosoto": "Motosoto", + "mpegssg": "MPEG-SSG", "mpich2": "mpich2", "mpich2.0": "mpich2", "mpich2.0.0": "mpich2", @@ -997,6 +1036,7 @@ var licenseIDs = map[string]string{ "ouda1": "O-UDA-1.0", "ouda1.0": "O-UDA-1.0", "ouda1.0.0": "O-UDA-1.0", + "padl": "PADL", "parity6": "Parity-6.0.0", "parity6.0": "Parity-6.0.0", "parity6.0.0": "Parity-6.0.0", @@ -1012,6 +1052,7 @@ var licenseIDs = map[string]string{ "php3.01": "PHP-3.01", "php3.01.0": "PHP-3.01", "plexus": "Plexus", + "pnmstitch": "pnmstitch", "polyformnoncommercial1": "PolyForm-Noncommercial-1.0.0", "polyformnoncommercial1.0": "PolyForm-Noncommercial-1.0.0", "polyformnoncommercial1.0.0": "PolyForm-Noncommercial-1.0.0", @@ -1028,6 +1069,7 @@ var licenseIDs = map[string]string{ "python2.0": "Python-2.0", "python2.0.0": "Python-2.0", "python2.0.1": "Python-2.0.1", + "pythonldap": "python-ldap", "qhull": "Qhull", "qpl1": "QPL-1.0", "qpl1.0": "QPL-1.0", @@ -1066,6 +1108,7 @@ var licenseIDs = map[string]string{ "sgib2": "SGI-B-2.0", "sgib2.0": "SGI-B-2.0", "sgib2.0.0": "SGI-B-2.0", + "sgiopengl": "SGI-OpenGL", "sgp4": "SGP4", "sgp4.0": "SGP4", "sgp4.0.0": "SGP4", @@ -1080,11 +1123,13 @@ var licenseIDs = map[string]string{ "sissl1": "SISSL-1.2", "sissl1.2": "SISSL-1.2", "sissl1.2.0": "SISSL-1.2", + "sl": "SL", "sleepycat": "Sleepycat", "smlnj": "SMLNJ", "smppl": "SMPPL", "snia": "SNIA", "snprintf": "snprintf", + "soundex": "Soundex", "spencer86": "Spencer-86", "spencer86.0": "Spencer-86", "spencer86.0.0": "Spencer-86", @@ -1097,6 +1142,7 @@ var licenseIDs = map[string]string{ "spl1": "SPL-1.0", "spl1.0": "SPL-1.0", "spl1.0.0": "SPL-1.0", + "sshkeyscan": "ssh-keyscan", "sshopenssh": "SSH-OpenSSH", "sshshort": "SSH-short", "sspl1": "SSPL-1.0", @@ -1108,6 +1154,7 @@ var licenseIDs = map[string]string{ "sugarcrm1.1.3": "SugarCRM-1.1.3", "sunpro": "SunPro", "swl": "SWL", + "swrule": "swrule", "symlinks": "Symlinks", "taprohl1": "TAPR-OHL-1.0", "taprohl1.0": "TAPR-OHL-1.0", @@ -1125,6 +1172,7 @@ var licenseIDs = map[string]string{ "tpl1.0": "TPL-1.0", "tpl1.0.0": "TPL-1.0", "ttwl": "TTWL", + "ttyp0": "TTYP0", "tuberlin1": "TU-Berlin-1.0", "tuberlin1.0": "TU-Berlin-1.0", "tuberlin1.0.0": "TU-Berlin-1.0", @@ -1135,6 +1183,7 @@ var licenseIDs = map[string]string{ "ucl1": "UCL-1.0", "ucl1.0": "UCL-1.0", "ucl1.0.0": "UCL-1.0", + "ulem": "ulem", "unicodedfs2015": "Unicode-DFS-2015", "unicodedfs2015.0": "Unicode-DFS-2015", "unicodedfs2015.0.0": "Unicode-DFS-2015", @@ -1147,6 +1196,7 @@ var licenseIDs = map[string]string{ "upl1": "UPL-1.0", "upl1.0": "UPL-1.0", "upl1.0.0": "UPL-1.0", + "urtrle": "URT-RLE", "vim": "Vim", "vostrom": "VOSTROM", "vsl1": "VSL-1.0", @@ -1196,6 +1246,7 @@ var licenseIDs = map[string]string{ "ypl1.1": "YPL-1.1", "ypl1.1.0": "YPL-1.1", "zed": "Zed", + "zeeff": "Zeeff", "zend2": "Zend-2.0", "zend2.0": "Zend-2.0", "zend2.0.0": "Zend-2.0", From b23879fd37d5f61544dc0f52ec3cce33aea9b54b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 12:01:38 -0400 Subject: [PATCH 090/106] chore(deps): bump golang.org/x/net from 0.15.0 to 0.16.0 (#2204) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.16.0. - [Commits](https://github.com/golang/net/compare/v0.15.0...v0.16.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 8 ++++---- go.sum | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index f97c56e94..54f750871 100644 --- a/go.mod +++ b/go.mod @@ -70,8 +70,8 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 golang.org/x/mod v0.13.0 - golang.org/x/net v0.15.0 - golang.org/x/term v0.12.0 // indirect + golang.org/x/net v0.16.0 + golang.org/x/term v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.26.0 ) @@ -203,9 +203,9 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/otel v1.14.0 // indirect go.opentelemetry.io/otel/trace v1.14.0 // indirect - golang.org/x/crypto v0.13.0 // indirect + golang.org/x/crypto v0.14.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.12.0 // indirect + golang.org/x/sys v0.13.0 // indirect golang.org/x/text v0.13.0 // indirect golang.org/x/tools v0.13.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect diff --git a/go.sum b/go.sum index c8bfac96a..6d6ef049b 100644 --- a/go.sum +++ b/go.sum @@ -843,8 +843,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -933,8 +933,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= +golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1052,16 +1052,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= From eed35ec9ce03eb935aee638bf393ac8c477df8a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 12:02:34 -0400 Subject: [PATCH 091/106] chore(deps): bump github/codeql-action from 2.21.9 to 2.22.0 (#2202) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.21.9 to 2.22.0. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/ddccb873888234080b77e9bc2d4764d5ccaaccf9...2cb752a87e96af96708ab57187ab6372ee1973ab) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f750cb94f..09e4b72f7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -45,7 +45,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@ddccb873888234080b77e9bc2d4764d5ccaaccf9 #v2.21.9 + uses: github/codeql-action/init@2cb752a87e96af96708ab57187ab6372ee1973ab #v2.22.0 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@ddccb873888234080b77e9bc2d4764d5ccaaccf9 #v2.21.9 + uses: github/codeql-action/autobuild@2cb752a87e96af96708ab57187ab6372ee1973ab #v2.22.0 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@ddccb873888234080b77e9bc2d4764d5ccaaccf9 #v2.21.9 + uses: github/codeql-action/analyze@2cb752a87e96af96708ab57187ab6372ee1973ab #v2.22.0 From 87e57aa925043e797b710d9b11a37b9d6c077060 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Oct 2023 12:54:19 -0400 Subject: [PATCH 092/106] chore(deps): bump github.com/docker/distribution from 2.8.2+incompatible to 2.8.3+incompatible (#2193) * chore(deps): bump github.com/docker/distribution Bumps [github.com/docker/distribution](https://github.com/docker/distribution) from 2.8.2+incompatible to 2.8.3+incompatible. - [Release notes](https://github.com/docker/distribution/releases) - [Commits](https://github.com/docker/distribution/compare/v2.8.2...v2.8.3) --- updated-dependencies: - dependency-name: github.com/docker/distribution dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * chore: update reference import Signed-off-by: Christopher Phillips --------- Signed-off-by: dependabot[bot] Signed-off-by: Christopher Phillips Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Christopher Phillips --- go.mod | 7 +++++-- go.sum | 6 ++++-- syft/formats/common/spdxhelpers/to_format_model.go | 2 +- syft/source/stereoscope_image_source.go | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 54f750871..c00928b16 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/charmbracelet/lipgloss v0.8.0 github.com/dave/jennifer v1.7.0 github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da - github.com/docker/distribution v2.8.2+incompatible + github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker v24.0.6+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 @@ -76,7 +76,10 @@ require ( modernc.org/sqlite v1.26.0 ) -require github.com/sanity-io/litter v1.5.5 +require ( + github.com/distribution/reference v0.5.0 + github.com/sanity-io/litter v1.5.5 +) require ( dario.cat/mergo v1.0.0 // indirect diff --git a/go.sum b/go.sum index 6d6ef049b..22bc5a6c7 100644 --- a/go.sum +++ b/go.sum @@ -213,10 +213,12 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M= github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk= github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= +github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM= github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= +github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE= github.com/docker/docker v24.0.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= diff --git a/syft/formats/common/spdxhelpers/to_format_model.go b/syft/formats/common/spdxhelpers/to_format_model.go index ca6a4f730..62f809644 100644 --- a/syft/formats/common/spdxhelpers/to_format_model.go +++ b/syft/formats/common/spdxhelpers/to_format_model.go @@ -10,7 +10,7 @@ import ( "strings" "time" - "github.com/docker/distribution/reference" + "github.com/distribution/reference" "github.com/spdx/tools-golang/spdx" "github.com/anchore/packageurl-go" diff --git a/syft/source/stereoscope_image_source.go b/syft/source/stereoscope_image_source.go index 2f45de7de..fc5defeed 100644 --- a/syft/source/stereoscope_image_source.go +++ b/syft/source/stereoscope_image_source.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/bmatcuk/doublestar/v4" - "github.com/docker/distribution/reference" + "github.com/distribution/reference" "github.com/opencontainers/go-digest" "github.com/anchore/stereoscope" From f6c805797715b3befbf65c291f466f6fbd94fc0b Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Fri, 6 Oct 2023 13:15:50 -0400 Subject: [PATCH 093/106] feat: add package for go compiler given binary detection (#2195) adds a unique synthetic package to the SBOM output that represents the go compiler when it is detected as a part of a package discovered by the go binary cataloger. When using an SBOM generated by syft - downstream vulnerability scanners now have the opportunity to detect/report on the PURL/CPEs attached to the new stdlib package. --------- Signed-off-by: Christopher Phillips --- syft/pkg/cataloger/golang/cataloger.go | 79 ++++++++++++++++++- syft/pkg/cataloger/golang/cataloger_test.go | 30 +++++++ syft/pkg/cataloger/golang/parse_go_binary.go | 1 + .../integration/go_compiler_detection_test.go | 62 +++++++++++++++ .../regression_go_bin_scanner_arch_test.go | 6 +- .../image-golang-compiler/Dockerfile | 1 + 6 files changed, 175 insertions(+), 4 deletions(-) create mode 100644 test/integration/go_compiler_detection_test.go create mode 100644 test/integration/test-fixtures/image-golang-compiler/Dockerfile diff --git a/syft/pkg/cataloger/golang/cataloger.go b/syft/pkg/cataloger/golang/cataloger.go index ee936da96..d1d117157 100644 --- a/syft/pkg/cataloger/golang/cataloger.go +++ b/syft/pkg/cataloger/golang/cataloger.go @@ -4,14 +4,21 @@ Package golang provides a concrete Cataloger implementation for go.mod files. package golang import ( + "fmt" + "regexp" + "strings" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/artifact" + "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/event/monitor" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/generic" ) +var versionCandidateGroups = regexp.MustCompile(`(?P\d+(\.\d+)?(\.\d+)?)(?P\w*)`) + // NewGoModFileCataloger returns a new Go module cataloger object. func NewGoModFileCataloger(opts GoCatalogerOpts) pkg.Cataloger { c := goModCataloger{ @@ -47,5 +54,75 @@ func (p *progressingCataloger) Name() string { func (p *progressingCataloger) Catalog(resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) { defer p.progress.SetCompleted() - return p.cataloger.Catalog(resolver) + pkgs, relationships, err := p.cataloger.Catalog(resolver) + goCompilerPkgs := []pkg.Package{} + totalLocations := file.NewLocationSet() + for _, goPkg := range pkgs { + mValue, ok := goPkg.Metadata.(pkg.GolangBinMetadata) + if !ok { + continue + } + // go binary packages should only contain a single location + for _, location := range goPkg.Locations.ToSlice() { + if !totalLocations.Contains(location) { + stdLibPkg := newGoStdLib(mValue.GoCompiledVersion, goPkg.Locations) + if stdLibPkg != nil { + goCompilerPkgs = append(goCompilerPkgs, *stdLibPkg) + totalLocations.Add(location) + } + } + } + } + pkgs = append(pkgs, goCompilerPkgs...) + return pkgs, relationships, err +} +func newGoStdLib(version string, location file.LocationSet) *pkg.Package { + stdlibCpe, err := generateStdlibCpe(version) + if err != nil { + return nil + } + goCompilerPkg := &pkg.Package{ + Name: "stdlib", + Version: version, + PURL: packageURL("stdlib", strings.TrimPrefix(version, "go")), + CPEs: []cpe.CPE{stdlibCpe}, + Locations: location, + Language: pkg.Go, + Type: pkg.GoModulePkg, + MetadataType: pkg.GolangBinMetadataType, + Metadata: pkg.GolangBinMetadata{ + GoCompiledVersion: version, + }, + } + goCompilerPkg.SetID() + + return goCompilerPkg +} + +func generateStdlibCpe(version string) (stdlibCpe cpe.CPE, err error) { + // GoCompiledVersion when pulled from a binary is prefixed by go + version = strings.TrimPrefix(version, "go") + + // we also need to trim starting from the first + to + // correctly extract potential rc candidate information for cpe generation + // ex: 2.0.0-rc.1+build.123 -> 2.0.0-rc.1; if no + is found then + is returned + after, _, found := strings.Cut("+", version) + if found { + version = after + } + + // extracting and + // https://regex101.com/r/985GsI/1 + captureGroups := internal.MatchNamedCaptureGroups(versionCandidateGroups, version) + vr, ok := captureGroups["version"] + if !ok || vr == "" { + return stdlibCpe, fmt.Errorf("could not match candidate version for: %s", version) + } + + cpeString := fmt.Sprintf("cpe:2.3:a:golang:go:%s:-:*:*:*:*:*:*", captureGroups["version"]) + if candidate, ok := captureGroups["candidate"]; ok && candidate != "" { + cpeString = fmt.Sprintf("cpe:2.3:a:golang:go:%s:%s:*:*:*:*:*:*", vr, candidate) + } + + return cpe.New(cpeString) } diff --git a/syft/pkg/cataloger/golang/cataloger_test.go b/syft/pkg/cataloger/golang/cataloger_test.go index 7323e9fa8..b1e26ba35 100644 --- a/syft/pkg/cataloger/golang/cataloger_test.go +++ b/syft/pkg/cataloger/golang/cataloger_test.go @@ -3,6 +3,9 @@ package golang import ( "testing" + "github.com/stretchr/testify/assert" + + "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" ) @@ -56,3 +59,30 @@ func Test_Binary_Cataloger_Globs(t *testing.T) { }) } } + +func Test_Binary_Cataloger_Stdlib_Cpe(t *testing.T) { + tests := []struct { + name string + candidate string + want string + }{ + { + name: "generateStdlibCpe generates a cpe with a - for a major version", + candidate: "go1.21.0", + want: "cpe:2.3:a:golang:go:1.21.0:-:*:*:*:*:*:*", + }, + { + name: "generateStdlibCpe generates a cpe with an rc candidate for a major rc version", + candidate: "go1.21rc2", + want: "cpe:2.3:a:golang:go:1.21:rc2:*:*:*:*:*:*", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := generateStdlibCpe(tc.candidate) + assert.NoError(t, err, "expected no err; got %v", err) + assert.Equal(t, cpe.String(got), tc.want) + }) + } +} diff --git a/syft/pkg/cataloger/golang/parse_go_binary.go b/syft/pkg/cataloger/golang/parse_go_binary.go index 5286270e1..5ccaaae3d 100644 --- a/syft/pkg/cataloger/golang/parse_go_binary.go +++ b/syft/pkg/cataloger/golang/parse_go_binary.go @@ -62,6 +62,7 @@ func (c *goBinaryCataloger) parseGoBinary(resolver file.Resolver, _ *generic.Env for _, mod := range mods { pkgs = append(pkgs, c.buildGoPkgInfo(resolver, reader.Location, mod, mod.arch)...) } + return pkgs, nil, nil } diff --git a/test/integration/go_compiler_detection_test.go b/test/integration/go_compiler_detection_test.go new file mode 100644 index 000000000..269944823 --- /dev/null +++ b/test/integration/go_compiler_detection_test.go @@ -0,0 +1,62 @@ +package integration + +import ( + "testing" + + "github.com/anchore/syft/syft/cpe" + "github.com/anchore/syft/syft/source" +) + +func TestGolangCompilerDetection(t *testing.T) { + tests := []struct { + name string + image string + expectedCompilers []string + expectedCPE []cpe.CPE + expectedPURL []string + }{ + { + name: "syft can detect a single golang compiler given the golang base image", + image: "image-golang-compiler", + expectedCompilers: []string{"go1.18.10"}, + expectedCPE: []cpe.CPE{cpe.Must("cpe:2.3:a:golang:go:1.18.10:-:*:*:*:*:*:*")}, + expectedPURL: []string{"pkg:golang/stdlib@1.18.10"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + sbom, _ := catalogFixtureImage(t, tt.image, source.SquashedScope, nil) + packages := sbom.Artifacts.Packages.PackagesByName("stdlib") + + foundCompilerVersions := make(map[string]struct{}) + foundCPE := make(map[cpe.CPE]struct{}) + foundPURL := make(map[string]struct{}) + + for _, pkg := range packages { + foundCompilerVersions[pkg.Version] = struct{}{} + foundPURL[pkg.PURL] = struct{}{} + for _, cpe := range pkg.CPEs { + foundCPE[cpe] = struct{}{} + } + } + + for _, expectedCompiler := range tt.expectedCompilers { + if _, ok := foundCompilerVersions[expectedCompiler]; !ok { + t.Fatalf("expected %s version; not found in found compilers: %v", expectedCompiler, foundCompilerVersions) + } + } + + for _, expectedPURL := range tt.expectedPURL { + if _, ok := foundPURL[expectedPURL]; !ok { + t.Fatalf("expected %s purl; not found in found purl: %v", expectedPURL, expectedPURLs) + } + } + + for _, expectedCPE := range tt.expectedCPE { + if _, ok := foundCPE[expectedCPE]; !ok { + t.Fatalf("expected %s version; not found in found cpe: %v", expectedCPE, expectedCPE) + } + } + }) + } +} diff --git a/test/integration/regression_go_bin_scanner_arch_test.go b/test/integration/regression_go_bin_scanner_arch_test.go index 8a51a9a77..d88ee6c7c 100644 --- a/test/integration/regression_go_bin_scanner_arch_test.go +++ b/test/integration/regression_go_bin_scanner_arch_test.go @@ -10,9 +10,9 @@ import ( func TestRegressionGoArchDiscovery(t *testing.T) { const ( - expectedELFPkg = 4 - expectedWINPkg = 4 - expectedMACOSPkg = 4 + expectedELFPkg = 5 + expectedWINPkg = 5 + expectedMACOSPkg = 5 ) // This is a regression test to make sure the way we detect go binary packages // stays consistent and reproducible as the tool chain evolves diff --git a/test/integration/test-fixtures/image-golang-compiler/Dockerfile b/test/integration/test-fixtures/image-golang-compiler/Dockerfile new file mode 100644 index 000000000..2d8e6bbdc --- /dev/null +++ b/test/integration/test-fixtures/image-golang-compiler/Dockerfile @@ -0,0 +1 @@ +FROM golang:1.18.10-alpine \ No newline at end of file From 68cf57ed03d3aedfce194348406ec14caf86a1dd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 13:05:57 -0400 Subject: [PATCH 094/106] chore(deps): bump github/codeql-action from 2.22.0 to 2.22.1 (#2208) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.22.0 to 2.22.1. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/2cb752a87e96af96708ab57187ab6372ee1973ab...fdcae64e1484d349b3366718cdfef3d404390e85) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 09e4b72f7..e09d6dcae 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -45,7 +45,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@2cb752a87e96af96708ab57187ab6372ee1973ab #v2.22.0 + uses: github/codeql-action/init@fdcae64e1484d349b3366718cdfef3d404390e85 #v2.22.1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@2cb752a87e96af96708ab57187ab6372ee1973ab #v2.22.0 + uses: github/codeql-action/autobuild@fdcae64e1484d349b3366718cdfef3d404390e85 #v2.22.1 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@2cb752a87e96af96708ab57187ab6372ee1973ab #v2.22.0 + uses: github/codeql-action/analyze@fdcae64e1484d349b3366718cdfef3d404390e85 #v2.22.1 From d16ecdf715c76ebdc5cb992b098b0bc409d2ef4e Mon Sep 17 00:00:00 2001 From: Mohammad Sharief Baig <56253776+sharief007@users.noreply.github.com> Date: Mon, 9 Oct 2023 22:37:18 +0530 Subject: [PATCH 095/106] Fix algo lookup by converting key to lower case (#2207) Signed-off-by: Mohammad Sharief Baig --- .../cyclonedxhelpers/external_references.go | 2 +- .../external_references_test.go | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/syft/formats/common/cyclonedxhelpers/external_references.go b/syft/formats/common/cyclonedxhelpers/external_references.go index 29770070a..4ba99587c 100644 --- a/syft/formats/common/cyclonedxhelpers/external_references.go +++ b/syft/formats/common/cyclonedxhelpers/external_references.go @@ -97,7 +97,7 @@ func toCycloneDXAlgorithm(algorithm string) cyclonedx.HashAlgorithm { "sha256": cyclonedx.HashAlgorithm("SHA-256"), } - return validMap[algorithm] + return validMap[strings.ToLower(algorithm)] } func decodeExternalReferences(c *cyclonedx.Component, metadata interface{}) { diff --git a/syft/formats/common/cyclonedxhelpers/external_references_test.go b/syft/formats/common/cyclonedxhelpers/external_references_test.go index 67fd73778..72b983122 100644 --- a/syft/formats/common/cyclonedxhelpers/external_references_test.go +++ b/syft/formats/common/cyclonedxhelpers/external_references_test.go @@ -173,3 +173,27 @@ func Test_isValidExternalRef(t *testing.T) { }) } } + +func Test_toCycloneDXAlgorithm(t *testing.T) { + tests := []struct { + name string + input string + expected cyclonedx.HashAlgorithm + }{ + { + name: "valid algorithm name in upper case", + input: "SHA1", + expected: cyclonedx.HashAlgorithm("SHA-1"), + }, + { + name: "valid algorithm name in lower case", + input: "sha1", + expected: cyclonedx.HashAlgorithm("SHA-1"), + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + assert.Equal(t, test.expected, toCycloneDXAlgorithm(test.input)) + }) + } +} From 185d0d1bfae3c59302dce46e256f2053bb3e407b Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 9 Oct 2023 13:44:38 -0400 Subject: [PATCH 096/106] Refine the docs for building a cataloger (#2175) * refine the docs for building a cataloger Signed-off-by: Alex Goodman * incorporate comments Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- DEVELOPING.md | 63 ++++++++++++++++++++++----------------------------- 1 file changed, 27 insertions(+), 36 deletions(-) diff --git a/DEVELOPING.md b/DEVELOPING.md index 3d35f4607..e8a5e6f1f 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -153,56 +153,47 @@ sequenceDiagram ### Syft Catalogers -##### Summary - -Catalogers are the way in which syft is able to identify and construct packages given some amount of source metadata. -For example, Syft can locate and process `package-lock.json` files when performing filesystem scans. -See: [how to specify file globs](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) -and an implementation of the [package-lock.json parser](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) for a quick review. +Catalogers are the way in which syft is able to identify and construct packages given a set a targeted list of files. +For example, a cataloger can ask syft for all `package-lock.json` files in order to parse and raise up javascript packages +(see [how file globs](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) and +[file parser functions](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) are used +for a quick example). From a high level catalogers have the following properties: -- They are independent from one another. The java cataloger has no idea of the processes, assumptions, or results of the python cataloger, for example. +- _They are independent from one another_. The java cataloger has no idea of the processes, assumptions, or results of the python cataloger, for example. -- They do not know what source is being analyzed. Are we analyzing a local directory? an image? if so, the squashed representation or all layers? The catalogers do not know the answers to these questions. Only that there is an interface to query for file paths and contents from an underlying "source" being scanned. +- _They do not know what source is being analyzed_. Are we analyzing a local directory? an image? if so, the squashed representation or all layers? The catalogers do not know the answers to these questions. Only that there is an interface to query for file paths and contents from an underlying "source" being scanned. + +- _Packages created by the cataloger should not be mutated after they are created_. There is one exception made for adding CPEs to a package after the cataloging phase, but that will most likely be moved back into the cataloger in the future. -- Packages created by the cataloger should not be mutated after they are created. There is one exception made for adding CPEs to a package after the cataloging phase, but that will most likely be moved back into the cataloger in the future. #### Building a new Cataloger -Catalogers must fulfill the interface [found here](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger.go). -This means that when building a new cataloger, the new struct must implement both method signatures of `Catalog` and `Name`. +Catalogers must fulfill the [`pkg.Cataloger` interface](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger.go) in order to add packages to the SBOM. +All catalogers should be added to: +- the [global list of catalogers](https://github.com/anchore/syft/blob/9995950c70e849f9921919faffbfcf46401f71f3/syft/pkg/cataloger/cataloger.go#L92-L125) +- at least one source-specific list, today the two lists are [directory catalogers and image catalogers](https://github.com/anchore/syft/blob/9995950c70e849f9921919faffbfcf46401f71f3/syft/pkg/cataloger/cataloger.go#L39-L89) -A top level view of the functions that construct all the catalogers can be found [here](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/cataloger.go). -When an author has finished writing a new cataloger this is the spot to plug in the new catalog constructor. +For reference, catalogers are [invoked within syft](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/catalog.go#L41-L100) one after the other, and can be invoked in parallel. -For a top level view of how the catalogers are used see [this function](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/catalog.go#L41-L100) as a reference. It ranges over all catalogers passed as an argument and invokes the `Catalog` method: +`generic.NewCataloger` is an abstraction syft used to make writing common components easier (see the [apkdb cataloger](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/cataloger.go) for example usage). +It takes the following information as input: +- A `catalogerName` to identify the cataloger uniquely among all other catalogers. +- Pairs of file globs as well as parser functions to parse those files. These parser functions return a slice of [`pkg.Package`](https://github.com/anchore/syft/blob/9995950c70e849f9921919faffbfcf46401f71f3/syft/pkg/package.go#L19) as well as a slice of [`artifact.Relationship`](https://github.com/anchore/syft/blob/9995950c70e849f9921919faffbfcf46401f71f3/syft/artifact/relationship.go#L31) to describe how the returned packages are related. See this [the apkdb cataloger parser function](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L22-L102) as an example. -Each cataloger has its own `Catalog` method, but this does not mean that they are all vastly different. -Take a look at the `apkdb` cataloger for alpine to see how it [constructs a generic.NewCataloger](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/cataloger.go). - -`generic.NewCataloger` is an abstraction syft uses to make writing common components easier. First, it takes the `catalogerName` to identify the cataloger. -On the other side of the call it uses two key pieces which inform the cataloger how to identify and return packages, the `globPatterns` and the `parseFunction`: -- The first piece is a `parseByGlob` matching pattern used to identify the files that contain the package metadata. -See [here for the APK example](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/apk_metadata.go#L16-L41). -- The other is a `parseFunction` which informs the cataloger what to do when it has found one of the above matches files. -See this [link for an example](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L22-L102). - -If you're unsure about using the `Generic Cataloger` and think the use case being filled requires something more custom -just file an issue or ask in our slack, and we'd be more than happy to help on the design. - -Identified packages share a common struct so be sure that when the new cataloger is constructing a new package it is using the [`Package` struct](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/package.go#L16-L31). - -Metadata Note: Identified packages are also assigned specific metadata that can be unique to their environment. -See [this folder](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg) for examples of the different metadata types. +Identified packages share a common `pkg.Package` struct so be sure that when the new cataloger is constructing a new package it is using the [`Package` struct](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/package.go#L16-L31). +If you want to return more information than what is available on the `pkg.Package` struct then you can do so in the `pkg.Package.Metadata` section of the struct, which is unique for each [`pkg.Type`](https://github.com/anchore/syft/blob/v0.70.0/syft/pkg/type.go). +See [the `pkg` package](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg) for examples of the different metadata types that are supported today. These are plugged into the `MetadataType` and `Metadata` fields in the above struct. `MetadataType` informs which type is being used. `Metadata` is an interface converted to that type. -Finally, here is an example of where the package construction is done in the apk cataloger. The first link is where `newPackage` is called in the `parseFunction`. The second link shows the package construction: -- [Call for new package](https://github.com/anchore/syft/blob/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L106) -- [APK Package Constructor](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/package.go#L12-L27) +Finally, here is an example of where the package construction is done within the apk cataloger: +- [Calling the APK package constructor from the parser function](https://github.com/anchore/syft/blob/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L106) +- [The APK package constructor itself](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/package.go#L12-L27) + +Interested in building a new cataloger? Checkout the [list of issues with the `new-cataloger` label](https://github.com/anchore/syft/issues?q=is%3Aopen+is%3Aissue+label%3Anew-cataloger+no%3Aassignee)! +If you have questions about implementing a cataloger feel free to file an issue or reach out to us [on slack](https://anchore.com/slack)! -If you have more questions about implementing a cataloger or questions about one you might be currently working -always feel free to file an issue or reach out to us [on slack](https://anchore.com/slack). #### Searching for files From 0748945c8341a06415ac80e624b84e8d3f548d39 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Tue, 10 Oct 2023 18:09:44 +0100 Subject: [PATCH 097/106] =?UTF-8?q?Parse=20the=20Maven=20license=20from=20?= =?UTF-8?q?the=20pom.xml=20if=20not=20contained=20in=20the=20mani=E2=80=A6?= =?UTF-8?q?=20(#2115)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Parse the Maven license from the pom.xml if not contained in the manifest Signed-off-by: Colm O hEigeartaigh * chore: restore 10.0.2 schema Signed-off-by: Christopher Phillips * chore: generate new 11.0.1 schema Signed-off-by: Christopher Phillips * refactor: remove schema change Signed-off-by: Christopher Phillips * test: update unit tests to align with new pattern Signed-off-by: Christopher Phillips * chore: pr feedback Signed-off-by: Christopher Phillips * chore: remove struct tags Signed-off-by: Christopher Phillips * keep license name and url semantics preserved on the pkg object Signed-off-by: Alex Goodman --------- Signed-off-by: Colm O hEigeartaigh Signed-off-by: Christopher Phillips Signed-off-by: Alex Goodman Co-authored-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Co-authored-by: Christopher Phillips Co-authored-by: Alex Goodman --- syft/pkg/cataloger/java/archive_parser.go | 77 ++- .../pkg/cataloger/java/archive_parser_test.go | 78 ++- syft/pkg/cataloger/java/parse_pom_xml.go | 47 +- syft/pkg/cataloger/java/parse_pom_xml_test.go | 78 ++- .../pom/neo4j-license-maven-plugin.pom.xml | 459 ++++++++++++++++++ syft/pkg/license.go | 34 +- 6 files changed, 691 insertions(+), 82 deletions(-) create mode 100644 syft/pkg/cataloger/java/test-fixtures/pom/neo4j-license-maven-plugin.pom.xml diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index f6570cb79..76de15348 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -171,19 +171,14 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { return nil, nil } - archiveCloser, err := os.Open(j.archivePath) - if err != nil { - return nil, fmt.Errorf("unable to open archive path (%s): %w", j.archivePath, err) - } - defer archiveCloser.Close() - // grab and assign digest for the entire archive - digests, err := intFile.NewDigestsFromFile(archiveCloser, javaArchiveHashes) + digests, err := getDigestsFromArchive(j.archivePath) if err != nil { - log.Warnf("failed to create digest for file=%q: %+v", j.archivePath, err) + return nil, err } // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest + // TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar licenses := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...) /* We should name and version from, in this order: @@ -192,13 +187,20 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { 3. manifest 4. filename */ - name, version := j.guessMainPackageNameAndVersionFromPomInfo() + name, version, pomLicenses := j.guessMainPackageNameAndVersionFromPomInfo() if name == "" { name = selectName(manifest, j.fileInfo) } if version == "" { version = selectVersion(manifest, j.fileInfo) } + if len(licenses) == 0 { + // Today we don't have a way to distinguish between licenses from the manifest and licenses from the pom.xml + // until the file.Location object can support sub-paths (i.e. paths within archives, recursively; issue https://github.com/anchore/syft/issues/2211). + // Until then it's less confusing to use the licenses from the pom.xml only if the manifest did not list any. + licenses = append(licenses, pomLicenses...) + } + return &pkg.Package{ // TODO: maybe select name should just have a pom properties in it? Name: name, @@ -218,11 +220,16 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { }, nil } -func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (string, string) { +type parsedPomProject struct { + *pkg.PomProject + Licenses []pkg.License +} + +func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (name, version string, licenses []pkg.License) { pomPropertyMatches := j.fileManifest.GlobMatch(pomPropertiesGlob) pomMatches := j.fileManifest.GlobMatch(pomXMLGlob) var pomPropertiesObject pkg.PomProperties - var pomProjectObject pkg.PomProject + var pomProjectObject parsedPomProject if len(pomPropertyMatches) == 1 || len(pomMatches) == 1 { // we have exactly 1 pom.properties or pom.xml in the archive; assume it represents the // package we're scanning if the names seem like a plausible match @@ -238,15 +245,15 @@ func (j *archiveParser) guessMainPackageNameAndVersionFromPomInfo() (string, str } } } - name := pomPropertiesObject.ArtifactID - if name == "" { + name = pomPropertiesObject.ArtifactID + if name == "" && pomProjectObject.PomProject != nil { name = pomProjectObject.ArtifactID } - version := pomPropertiesObject.Version - if version == "" { + version = pomPropertiesObject.Version + if version == "" && pomProjectObject.PomProject != nil { version = pomProjectObject.Version } - return name, version + return name, version, pomProjectObject.Licenses } // discoverPkgsFromAllMavenFiles parses Maven POM properties/xml for a given @@ -273,7 +280,7 @@ func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([ } for parentPath, propertiesObj := range properties { - var pomProject *pkg.PomProject + var pomProject *parsedPomProject if proj, exists := projects[parentPath]; exists { pomProject = &proj } @@ -287,6 +294,22 @@ func (j *archiveParser) discoverPkgsFromAllMavenFiles(parentPkg *pkg.Package) ([ return pkgs, nil } +func getDigestsFromArchive(archivePath string) ([]file.Digest, error) { + archiveCloser, err := os.Open(archivePath) + if err != nil { + return nil, fmt.Errorf("unable to open archive path (%s): %w", archivePath, err) + } + defer archiveCloser.Close() + + // grab and assign digest for the entire archive + digests, err := intFile.NewDigestsFromFile(archiveCloser, javaArchiveHashes) + if err != nil { + log.Warnf("failed to create digest for file=%q: %+v", archivePath, err) + } + + return digests, nil +} + func (j *archiveParser) discoverPkgsFromNestedArchives(parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) { // we know that all java archives are zip formatted files, so we can use the shared zip helper return discoverPkgsFromZip(j.location, j.archivePath, j.contentPath, j.fileManifest, parentPkg) @@ -388,15 +411,16 @@ func pomPropertiesByParentPath(archivePath string, location file.Location, extra return propertiesByParentPath, nil } -func pomProjectByParentPath(archivePath string, location file.Location, extractPaths []string) (map[string]pkg.PomProject, error) { +func pomProjectByParentPath(archivePath string, location file.Location, extractPaths []string) (map[string]parsedPomProject, error) { contentsOfMavenProjectFiles, err := intFile.ContentsFromZip(archivePath, extractPaths...) if err != nil { return nil, fmt.Errorf("unable to extract maven files: %w", err) } - projectByParentPath := make(map[string]pkg.PomProject) + projectByParentPath := make(map[string]parsedPomProject) for filePath, fileContents := range contentsOfMavenProjectFiles { - pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents)) + // TODO: when we support locations of paths within archives we should start passing the specific pom.xml location object instead of the top jar + pomProject, err := parsePomXMLProject(filePath, strings.NewReader(fileContents), location) if err != nil { log.WithFields("contents-path", filePath, "location", location.AccessPath()).Warnf("failed to parse pom.xml: %+v", err) continue @@ -418,7 +442,7 @@ func pomProjectByParentPath(archivePath string, location file.Location, extractP // newPackageFromMavenData processes a single Maven POM properties for a given parent package, returning all listed Java packages found and // associating each discovered package to the given parent package. Note the pom.xml is optional, the pom.properties is not. -func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.PomProject, parentPkg *pkg.Package, location file.Location) *pkg.Package { +func newPackageFromMavenData(pomProperties pkg.PomProperties, parsedPomProject *parsedPomProject, parentPkg *pkg.Package, location file.Location) *pkg.Package { // keep the artifact name within the virtual path if this package does not match the parent package vPathSuffix := "" groupID := "" @@ -440,20 +464,27 @@ func newPackageFromMavenData(pomProperties pkg.PomProperties, pomProject *pkg.Po } virtualPath := location.AccessPath() + vPathSuffix - // discovered props = new package + var pkgPomProject *pkg.PomProject + licenses := make([]pkg.License, 0) + if parsedPomProject != nil { + pkgPomProject = parsedPomProject.PomProject + licenses = append(licenses, parsedPomProject.Licenses...) + } + p := pkg.Package{ Name: pomProperties.ArtifactID, Version: pomProperties.Version, Locations: file.NewLocationSet( location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), ), + Licenses: pkg.NewLicenseSet(licenses...), Language: pkg.Java, Type: pomProperties.PkgTypeIndicated(), MetadataType: pkg.JavaMetadataType, Metadata: pkg.JavaMetadata{ VirtualPath: virtualPath, PomProperties: &pomProperties, - PomProject: pomProject, + PomProject: pkgPomProject, Parent: parentPkg, }, } diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index 4635a9c98..78cbb2574 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -6,7 +6,6 @@ import ( "io" "os" "os/exec" - "path" "path/filepath" "strings" "syscall" @@ -17,6 +16,7 @@ import ( "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" ) @@ -83,11 +83,13 @@ func generateJavaBuildFixture(t *testing.T, fixturePath string) { func TestParseJar(t *testing.T) { tests := []struct { + name string fixture string expected map[string]pkg.Package ignoreExtras []string }{ { + name: "example-jenkins-plugin", fixture: "test-fixtures/java-builds/packages/example-jenkins-plugin.hpi", ignoreExtras: []string{ "Plugin-Version", // has dynamic date @@ -146,6 +148,7 @@ func TestParseJar(t *testing.T) { }, }, { + name: "example-java-app-gradle", fixture: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar", expected: map[string]pkg.Package{ "example-java-app-gradle": { @@ -172,6 +175,16 @@ func TestParseJar(t *testing.T) { Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromFields( + "Apache 2", + "http://www.apache.org/licenses/LICENSE-2.0.txt", + func() *file.Location { + l := file.NewLocation("test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar") + return &l + }(), + ), + ), Metadata: pkg.JavaMetadata{ // ensure that nested packages with different names than that of the parent are appended as // a suffix on the virtual path with a colon separator between group name and artifact name @@ -196,6 +209,7 @@ func TestParseJar(t *testing.T) { }, }, { + name: "example-java-app-maven", fixture: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar", ignoreExtras: []string{ "Build-Jdk", // can't guarantee the JDK used at build time @@ -231,9 +245,19 @@ func TestParseJar(t *testing.T) { }, }, "joda-time": { - Name: "joda-time", - Version: "2.9.2", - PURL: "pkg:maven/joda-time/joda-time@2.9.2", + Name: "joda-time", + Version: "2.9.2", + PURL: "pkg:maven/joda-time/joda-time@2.9.2", + Licenses: pkg.NewLicenseSet( + pkg.NewLicenseFromFields( + "Apache 2", + "http://www.apache.org/licenses/LICENSE-2.0.txt", + func() *file.Location { + l := file.NewLocation("test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar") + return &l + }(), + ), + ), Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, @@ -263,7 +287,7 @@ func TestParseJar(t *testing.T) { } for _, test := range tests { - t.Run(path.Base(test.fixture), func(t *testing.T) { + t.Run(test.name, func(t *testing.T) { generateJavaBuildFixture(t, test.fixture) @@ -618,7 +642,7 @@ func Test_newPackageFromMavenData(t *testing.T) { tests := []struct { name string props pkg.PomProperties - project *pkg.PomProject + project *parsedPomProject parent *pkg.Package expectedParent pkg.Package expectedPackage *pkg.Package @@ -687,18 +711,29 @@ func Test_newPackageFromMavenData(t *testing.T) { ArtifactID: "some-artifact-id", Version: "1.0", }, - project: &pkg.PomProject{ - Parent: &pkg.PomParent{ - GroupID: "some-parent-group-id", - ArtifactID: "some-parent-artifact-id", - Version: "1.0-parent", + project: &parsedPomProject{ + PomProject: &pkg.PomProject{ + Parent: &pkg.PomParent{ + GroupID: "some-parent-group-id", + ArtifactID: "some-parent-artifact-id", + Version: "1.0-parent", + }, + Name: "some-name", + GroupID: "some-group-id", + ArtifactID: "some-artifact-id", + Version: "1.0", + Description: "desc", + URL: "aweso.me", + }, + Licenses: []pkg.License{ + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URLs: internal.NewStringSet("https://opensource.org/licenses/MIT"), + Locations: file.NewLocationSet(file.NewLocation("some-license-path")), + }, }, - Name: "some-name", - GroupID: "some-group-id", - ArtifactID: "some-artifact-id", - Version: "1.0", - Description: "desc", - URL: "aweso.me", }, parent: &pkg.Package{ Name: "some-parent-name", @@ -727,6 +762,15 @@ func Test_newPackageFromMavenData(t *testing.T) { Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, + Licenses: pkg.NewLicenseSet( + pkg.License{ + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URLs: internal.NewStringSet("https://opensource.org/licenses/MIT"), + Locations: file.NewLocationSet(file.NewLocation("some-license-path")), + }, + ), Metadata: pkg.JavaMetadata{ VirtualPath: virtualPath + ":" + "some-group-id" + ":" + "some-artifact-id", PomProperties: &pkg.PomProperties{ diff --git a/syft/pkg/cataloger/java/parse_pom_xml.go b/syft/pkg/cataloger/java/parse_pom_xml.go index a6f5340cf..eb0f9d9b2 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml.go +++ b/syft/pkg/cataloger/java/parse_pom_xml.go @@ -49,28 +49,51 @@ func parserPomXML(_ file.Resolver, _ *generic.Environment, reader file.LocationR return pkgs, nil, nil } -func parsePomXMLProject(path string, reader io.Reader) (*pkg.PomProject, error) { +func parsePomXMLProject(path string, reader io.Reader, location file.Location) (*parsedPomProject, error) { project, err := decodePomXML(reader) if err != nil { return nil, err } - return newPomProject(path, project), nil + return newPomProject(path, project, location), nil } -func newPomProject(path string, p gopom.Project) *pkg.PomProject { +func newPomProject(path string, p gopom.Project, location file.Location) *parsedPomProject { artifactID := safeString(p.ArtifactID) name := safeString(p.Name) projectURL := safeString(p.URL) + + var licenses []pkg.License + if p.Licenses != nil { + for _, license := range *p.Licenses { + var licenseName, licenseURL string + if license.Name != nil { + licenseName = *license.Name + } + if license.URL != nil { + licenseURL = *license.URL + } + + if licenseName == "" && licenseURL == "" { + continue + } + + licenses = append(licenses, pkg.NewLicenseFromFields(licenseName, licenseURL, &location)) + } + } + log.WithFields("path", path, "artifactID", artifactID, "name", name, "projectURL", projectURL).Trace("parsing pom.xml") - return &pkg.PomProject{ - Path: path, - Parent: pomParent(p, p.Parent), - GroupID: resolveProperty(p, p.GroupID, "groupId"), - ArtifactID: artifactID, - Version: resolveProperty(p, p.Version, "version"), - Name: name, - Description: cleanDescription(p.Description), - URL: projectURL, + return &parsedPomProject{ + PomProject: &pkg.PomProject{ + Path: path, + Parent: pomParent(p, p.Parent), + GroupID: resolveProperty(p, p.GroupID, "groupId"), + ArtifactID: artifactID, + Version: resolveProperty(p, p.Version, "version"), + Name: name, + Description: cleanDescription(p.Description), + URL: projectURL, + }, + Licenses: licenses, } } diff --git a/syft/pkg/cataloger/java/parse_pom_xml_test.go b/syft/pkg/cataloger/java/parse_pom_xml_test.go index 9e79fe96e..acbadf883 100644 --- a/syft/pkg/cataloger/java/parse_pom_xml_test.go +++ b/syft/pkg/cataloger/java/parse_pom_xml_test.go @@ -11,7 +11,9 @@ import ( "github.com/stretchr/testify/require" "github.com/vifraa/gopom" + "github.com/anchore/syft/internal" "github.com/anchore/syft/syft/file" + "github.com/anchore/syft/syft/license" "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" ) @@ -293,33 +295,79 @@ func Test_parseCommonsTextPomXMLProject(t *testing.T) { } func Test_parsePomXMLProject(t *testing.T) { + // TODO: ideally we would have the path to the contained pom.xml, not the jar + jarLocation := file.NewLocation("path/to/archive.jar") tests := []struct { - expected pkg.PomProject + name string + expected parsedPomProject }{ { - expected: pkg.PomProject{ - Path: "test-fixtures/pom/commons-codec.pom.xml", - Parent: &pkg.PomParent{ - GroupID: "org.apache.commons", - ArtifactID: "commons-parent", - Version: "42", + name: "go case", + expected: parsedPomProject{ + PomProject: &pkg.PomProject{ + Path: "test-fixtures/pom/commons-codec.pom.xml", + Parent: &pkg.PomParent{ + GroupID: "org.apache.commons", + ArtifactID: "commons-parent", + Version: "42", + }, + GroupID: "commons-codec", + ArtifactID: "commons-codec", + Version: "1.11", + Name: "Apache Commons Codec", + Description: "The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.", + URL: "http://commons.apache.org/proper/commons-codec/", + }, + }, + }, + { + name: "with license data", + expected: parsedPomProject{ + PomProject: &pkg.PomProject{ + Path: "test-fixtures/pom/neo4j-license-maven-plugin.pom.xml", + Parent: &pkg.PomParent{ + GroupID: "org.sonatype.oss", + ArtifactID: "oss-parent", + Version: "7", + }, + GroupID: "org.neo4j.build.plugins", + ArtifactID: "license-maven-plugin", + Version: "4-SNAPSHOT", + Name: "${project.artifactId}", // TODO: this is not an ideal answer + Description: "Maven 2 plugin to check and update license headers in source files", + URL: "http://components.neo4j.org/${project.artifactId}/${project.version}", // TODO: this is not an ideal answer + }, + Licenses: []pkg.License{ + { + Value: "The Apache Software License, Version 2.0", + SPDXExpression: "", // TODO: ideally we would parse this title to get Apache-2.0 (created issue #2210 https://github.com/anchore/syft/issues/2210) + Type: license.Declared, + URLs: internal.NewStringSet("http://www.apache.org/licenses/LICENSE-2.0.txt"), + Locations: file.NewLocationSet(jarLocation), + }, + { + Value: "MIT", + SPDXExpression: "MIT", + Type: license.Declared, + URLs: internal.NewStringSet(), + Locations: file.NewLocationSet(jarLocation), + }, + { + Type: license.Declared, + URLs: internal.NewStringSet("https://opensource.org/license/unlicense/"), + Locations: file.NewLocationSet(jarLocation), + }, }, - GroupID: "commons-codec", - ArtifactID: "commons-codec", - Version: "1.11", - Name: "Apache Commons Codec", - Description: "The Apache Commons Codec package contains simple encoder and decoders for various formats such as Base64 and Hexadecimal. In addition to these widely used encoders and decoders, the codec package also maintains a collection of phonetic encoding utilities.", - URL: "http://commons.apache.org/proper/commons-codec/", }, }, } for _, test := range tests { - t.Run(test.expected.Path, func(t *testing.T) { + t.Run(test.name, func(t *testing.T) { fixture, err := os.Open(test.expected.Path) assert.NoError(t, err) - actual, err := parsePomXMLProject(fixture.Name(), fixture) + actual, err := parsePomXMLProject(fixture.Name(), fixture, jarLocation) assert.NoError(t, err) assert.Equal(t, &test.expected, actual) diff --git a/syft/pkg/cataloger/java/test-fixtures/pom/neo4j-license-maven-plugin.pom.xml b/syft/pkg/cataloger/java/test-fixtures/pom/neo4j-license-maven-plugin.pom.xml new file mode 100644 index 000000000..dc8a072ad --- /dev/null +++ b/syft/pkg/cataloger/java/test-fixtures/pom/neo4j-license-maven-plugin.pom.xml @@ -0,0 +1,459 @@ + + + + 4.0.0 + + org.sonatype.oss + oss-parent + 7 + + + + org.neo4j.build.plugins + license-maven-pluginå + 4-SNAPSHOT + maven-plugin + + ${project.artifactId} + Maven 2 plugin to check and update license headers in source files + http://components.neo4j.org/${project.artifactId}/${project.version} + 2008 + + + + + 1.6 + 1.6 + + + + + + scm:git:git://github.com/neo4j/license-maven-plugin.git + scm:git:git@github.com:neo4j/license-maven-plugin.git + https://github.com/neo4j/license-maven-plugin + + + + + + Mathieu Carbou + http://mathieu.carbou.free.fr/ + + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + MIT + + + https://opensource.org/license/unlicense/ + + + + + + mimilowns + Cédric + mimilowns@gmail.com + +1 + + developer + + + + mathieu.carbou + Mathieu Carbou + mathieu.carbou@gmail.com + Mycila + http://mathieu.carbou.free.fr/ + -5 + + project owner + developer + + + + + + Google Code + http://code.google.com/p/${project.artifactId}/issues/list + + + + + + + maven-license-plugin-announce + maven-license-plugin-announce-subscribe@googlegroups.com + maven-license-plugin-announce-unsubscribe@googlegroups.com + http://groups.google.com/group/maven-license-plugin-announce + + + maven-license-plugin-codesite + maven-license-plugin-codesite-subscribe@googlegroups.com + maven-license-plugin-codesite-unsubscribe@googlegroups.com + http://groups.google.com/group/maven-license-plugin-codesite + + + + + + + + sonatype-nexus-staging + Nexus Release Repository + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + snapshots@m2.neo4j.org + snapshots@m2.neo4j.org + http://m2.neo4j.org/content/repositories/snapshots + + + neo4j-site + scpexe://components.neo4j.org/home/neo/components/${project.artifactId}/${project.version} + + + + + + + + + org.apache.maven.wagon + wagon-webdav + 1.0-beta-2 + + + + + + org.apache.felix + maven-bundle-plugin + 2.1.0 + + + org.apache.felix + maven-ipojo-plugin + 1.6.0 + + + com.mycila.maven-license-plugin + maven-license-plugin + 1.8.0 + + + maven-antrun-plugin + 1.4 + + + maven-source-plugin + 2.1.2 + + + maven-jar-plugin + 2.3.1 + + + maven-javadoc-plugin + 2.7 + + + maven-deploy-plugin + 2.5 + + + maven-clean-plugin + 2.4.1 + + + maven-invoker-plugin + 1.5 + + + maven-shade-plugin + 1.3.3 + + + maven-assembly-plugin + 2.2-beta-5 + + + maven-dependency-plugin + 2.1 + + + maven-compiler-plugin + 2.3.1 + + + maven-release-plugin + 2.0 + + + maven-surefire-plugin + 2.6 + + + org.apache.maven.plugins + maven-gpg-plugin + 1.4 + + Neo Technology Build Server + true + + + + + + + + maven-plugin-plugin + 2.6 + + + maven-clean-plugin + + + + .clover + + + test-output + + + + + + + maven-compiler-plugin + + ${jdk} + ${jdk} + + + + + maven-surefire-plugin + + + + maven-jar-plugin + + + + true + true + + + + + + maven-source-plugin + + + attach-sources + + jar + + + + + + maven-remote-resources-plugin + 1.1 + + + + process + + + + org.apache:apache-jar-resource-bundle:1.3 + + + + + + + maven-assembly-plugin + + + project + + + + + + maven-deploy-plugin + + true + + + + maven-release-plugin + + + + maven-dependency-plugin + + + com.mycila.maven-license-plugin + maven-license-plugin + 1.9.0 + +
${basedir}/src/etc/header.txt
+ true + + .gitignore + LICENSE.txt + NOTICE.txt + src/test/data/** + src/test/integration/** + src/test/resources/** + +
+ + + + check + + + +
+ + + maven-clean-plugin + + + + it + + target/** + */target/** + + + + target + + + + + + true + maven-invoker-plugin + + it + true + true + ${ittest.skip} + ${ittest.skip} + + ${target.version} + + + test + + + **/pom.xml + + + + + integration-test + + install + run + + + + +
+
+ + + + + + org.apache.maven + maven-plugin-api + 3.0.1 + compile + + + com.mycila.xmltool + xmltool + 3.3 + compile + + + org.apache.maven + maven-project + 3.0-alpha-2 + compile + + + junit + junit + + + + + org.codehaus.plexus + plexus-utils + 2.0.5 + compile + + + + org.testng + testng + 5.7 + jdk15 + test + + + junit + junit + + + + + org.apache.maven + maven-embedder + 3.0.1 + test + + + org.apache.maven.plugin-testing + maven-plugin-testing-harness + 2.0-alpha-1 + test + + + junit + junit + + + + + +
diff --git a/syft/pkg/license.go b/syft/pkg/license.go index 6e681da63..a6270ec9e 100644 --- a/syft/pkg/license.go +++ b/syft/pkg/license.go @@ -60,24 +60,17 @@ func (l Licenses) Swap(i, j int) { } func NewLicense(value string) License { - spdxExpression, err := license.ParseExpression(value) - if err != nil { - log.Trace("unable to parse license expression for %q: %w", value, err) - } - - return License{ - Value: value, - SPDXExpression: spdxExpression, - Type: license.Declared, - URLs: internal.NewStringSet(), - Locations: file.NewLocationSet(), - } + return NewLicenseFromType(value, license.Declared) } func NewLicenseFromType(value string, t license.Type) License { - spdxExpression, err := license.ParseExpression(value) - if err != nil { - log.Trace("unable to parse license expression: %w", err) + var spdxExpression string + if value != "" { + var err error + spdxExpression, err = license.ParseExpression(value) + if err != nil { + log.Trace("unable to parse license expression: %w", err) + } } return License{ @@ -124,6 +117,17 @@ func NewLicenseFromURLs(value string, urls ...string) License { return l } +func NewLicenseFromFields(value, url string, location *file.Location) License { + l := NewLicense(value) + if location != nil { + l.Locations.Add(*location) + } + if url != "" { + l.URLs.Add(url) + } + return l +} + // this is a bit of a hack to not infinitely recurse when hashing a license func (s License) Merge(l License) (*License, error) { sHash, err := artifact.IDByHash(s) From ef759038f5351dbfd0d731c0f0d07d51d68fd0b3 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 11 Oct 2023 08:56:26 -0400 Subject: [PATCH 098/106] Add relationships for dpkg packages (#2212) * add relationships for deb packages Signed-off-by: Alex Goodman * update snapshots Signed-off-by: Alex Goodman * bump json schema Signed-off-by: Alex Goodman * small refactor to remove duplicate code Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- internal/constants.go | 2 +- schema/json/schema-11.0.1.json | 2003 +++++++++++++++++ syft/formats/cyclonedxxml/encoder_test.go | 2 +- .../TestCycloneDxDirectoryEncoder.golden | 2 +- .../snapshot/TestCycloneDxImageEncoder.golden | 2 +- .../TestSPDXJSONDirectoryEncoder.golden | 4 +- .../snapshot/TestSPDXJSONImageEncoder.golden | 4 +- .../snapshot/TestSPDXRelationshipOrder.golden | 4 +- .../snapshot/TestSPDXRelationshipOrder.golden | 4 +- .../TestSPDXTagValueDirectoryEncoder.golden | 4 +- .../TestSPDXTagValueImageEncoder.golden | 4 +- .../snapshot/TestDirectoryEncoder.golden | 2 +- .../TestEncodeFullJSONDocument.golden | 2 +- .../snapshot/TestImageEncoder.golden | 2 +- syft/pkg/cataloger/deb/cataloger_test.go | 6 + syft/pkg/cataloger/deb/package.go | 14 +- syft/pkg/cataloger/deb/parse_dpkg_db.go | 131 +- syft/pkg/cataloger/deb/parse_dpkg_db_test.go | 157 +- .../status/coreutils-relationships | 114 + .../deb/test-fixtures/status/doc-examples | 46 + .../deb/test-fixtures/status/libpam-runtime | 23 + syft/pkg/dpkg_metadata.go | 51 +- 22 files changed, 2541 insertions(+), 42 deletions(-) create mode 100644 schema/json/schema-11.0.1.json create mode 100644 syft/pkg/cataloger/deb/test-fixtures/status/coreutils-relationships create mode 100644 syft/pkg/cataloger/deb/test-fixtures/status/doc-examples create mode 100644 syft/pkg/cataloger/deb/test-fixtures/status/libpam-runtime diff --git a/internal/constants.go b/internal/constants.go index 794da714a..0f7c9cc0f 100644 --- a/internal/constants.go +++ b/internal/constants.go @@ -3,5 +3,5 @@ package internal const ( // JSONSchemaVersion is the current schema version output by the JSON encoder // This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment. - JSONSchemaVersion = "11.0.0" + JSONSchemaVersion = "11.0.1" ) diff --git a/schema/json/schema-11.0.1.json b/schema/json/schema-11.0.1.json new file mode 100644 index 000000000..cd3e2b8da --- /dev/null +++ b/schema/json/schema-11.0.1.json @@ -0,0 +1,2003 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/syft/json/11.0.1/document", + "$ref": "#/$defs/Document", + "$defs": { + "AlpmFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "type": { + "type": "string" + }, + "uid": { + "type": "string" + }, + "gid": { + "type": "string" + }, + "time": { + "type": "string", + "format": "date-time" + }, + "size": { + "type": "string" + }, + "link": { + "type": "string" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object" + }, + "AlpmMetadata": { + "properties": { + "basepackage": { + "type": "string" + }, + "package": { + "type": "string" + }, + "version": { + "type": "string" + }, + "description": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "packager": { + "type": "string" + }, + "url": { + "type": "string" + }, + "validation": { + "type": "string" + }, + "reason": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + }, + "backup": { + "items": { + "$ref": "#/$defs/AlpmFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "basepackage", + "package", + "version", + "description", + "architecture", + "size", + "packager", + "url", + "validation", + "reason", + "files", + "backup" + ] + }, + "ApkFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "ownerUid": { + "type": "string" + }, + "ownerGid": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "ApkMetadata": { + "properties": { + "package": { + "type": "string" + }, + "originPackage": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "version": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "url": { + "type": "string" + }, + "description": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "installedSize": { + "type": "integer" + }, + "pullDependencies": { + "items": { + "type": "string" + }, + "type": "array" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "pullChecksum": { + "type": "string" + }, + "gitCommitOfApkPort": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/ApkFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "originPackage", + "maintainer", + "version", + "architecture", + "url", + "description", + "size", + "installedSize", + "pullDependencies", + "provides", + "pullChecksum", + "gitCommitOfApkPort", + "files" + ] + }, + "BinaryMetadata": { + "properties": { + "matches": { + "items": { + "$ref": "#/$defs/ClassifierMatch" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "matches" + ] + }, + "CargoPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "type": "string" + }, + "checksum": { + "type": "string" + }, + "dependencies": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "checksum", + "dependencies" + ] + }, + "ClassifierMatch": { + "properties": { + "classifier": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Location" + } + }, + "type": "object", + "required": [ + "classifier", + "location" + ] + }, + "CocoapodsMetadata": { + "properties": { + "checksum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "checksum" + ] + }, + "ConanLockMetadata": { + "properties": { + "ref": { + "type": "string" + }, + "package_id": { + "type": "string" + }, + "prev": { + "type": "string" + }, + "requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "build_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "py_requires": { + "items": { + "type": "string" + }, + "type": "array" + }, + "options": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "path": { + "type": "string" + }, + "context": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "ConanMetadata": { + "properties": { + "ref": { + "type": "string" + } + }, + "type": "object", + "required": [ + "ref" + ] + }, + "Coordinates": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "DartPubMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "hosted_url": { + "type": "string" + }, + "vcs_url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Descriptor": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "configuration": true + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Digest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "Document": { + "properties": { + "artifacts": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + }, + "artifactRelationships": { + "items": { + "$ref": "#/$defs/Relationship" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/File" + }, + "type": "array" + }, + "secrets": { + "items": { + "$ref": "#/$defs/Secrets" + }, + "type": "array" + }, + "source": { + "$ref": "#/$defs/Source" + }, + "distro": { + "$ref": "#/$defs/LinuxRelease" + }, + "descriptor": { + "$ref": "#/$defs/Descriptor" + }, + "schema": { + "$ref": "#/$defs/Schema" + } + }, + "type": "object", + "required": [ + "artifacts", + "artifactRelationships", + "source", + "distro", + "descriptor", + "schema" + ] + }, + "DotnetDepsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "path": { + "type": "string" + }, + "sha512": { + "type": "string" + }, + "hashPath": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "path", + "sha512", + "hashPath" + ] + }, + "DotnetPortableExecutableMetadata": { + "properties": { + "assemblyVersion": { + "type": "string" + }, + "legalCopyright": { + "type": "string" + }, + "comments": { + "type": "string" + }, + "internalName": { + "type": "string" + }, + "companyName": { + "type": "string" + }, + "productName": { + "type": "string" + }, + "productVersion": { + "type": "string" + } + }, + "type": "object", + "required": [ + "assemblyVersion", + "legalCopyright", + "companyName", + "productName", + "productVersion" + ] + }, + "DpkgFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "isConfigFile": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "path", + "isConfigFile" + ] + }, + "DpkgMetadata": { + "properties": { + "package": { + "type": "string" + }, + "source": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "installedSize": { + "type": "integer" + }, + "provides": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "preDepends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "files": { + "items": { + "$ref": "#/$defs/DpkgFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "package", + "source", + "version", + "sourceVersion", + "architecture", + "maintainer", + "installedSize", + "files" + ] + }, + "File": { + "properties": { + "id": { + "type": "string" + }, + "location": { + "$ref": "#/$defs/Coordinates" + }, + "metadata": { + "$ref": "#/$defs/FileMetadataEntry" + }, + "contents": { + "type": "string" + }, + "digests": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + }, + "licenses": { + "items": { + "$ref": "#/$defs/FileLicense" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "location" + ] + }, + "FileLicense": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "evidence": { + "$ref": "#/$defs/FileLicenseEvidence" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type" + ] + }, + "FileLicenseEvidence": { + "properties": { + "confidence": { + "type": "integer" + }, + "offset": { + "type": "integer" + }, + "extent": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "confidence", + "offset", + "extent" + ] + }, + "FileMetadataEntry": { + "properties": { + "mode": { + "type": "integer" + }, + "type": { + "type": "string" + }, + "linkDestination": { + "type": "string" + }, + "userID": { + "type": "integer" + }, + "groupID": { + "type": "integer" + }, + "mimeType": { + "type": "string" + }, + "size": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "mode", + "type", + "userID", + "groupID", + "mimeType", + "size" + ] + }, + "GemMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "type": "string" + }, + "type": "array" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "GolangBinMetadata": { + "properties": { + "goBuildSettings": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "goCompiledVersion": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "h1Digest": { + "type": "string" + }, + "mainModule": { + "type": "string" + }, + "goCryptoSettings": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "goCompiledVersion", + "architecture" + ] + }, + "GolangModMetadata": { + "properties": { + "h1Digest": { + "type": "string" + } + }, + "type": "object" + }, + "HackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "snapshotURL": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "IDLikes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "JavaManifest": { + "properties": { + "main": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "namedSections": { + "patternProperties": { + ".*": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "JavaMetadata": { + "properties": { + "virtualPath": { + "type": "string" + }, + "manifest": { + "$ref": "#/$defs/JavaManifest" + }, + "pomProperties": { + "$ref": "#/$defs/PomProperties" + }, + "pomProject": { + "$ref": "#/$defs/PomProject" + }, + "digest": { + "items": { + "$ref": "#/$defs/Digest" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "virtualPath" + ] + }, + "KbPackageMetadata": { + "properties": { + "product_id": { + "type": "string" + }, + "kb": { + "type": "string" + } + }, + "type": "object", + "required": [ + "product_id", + "kb" + ] + }, + "License": { + "properties": { + "value": { + "type": "string" + }, + "spdxExpression": { + "type": "string" + }, + "type": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "value", + "spdxExpression", + "type", + "urls", + "locations" + ] + }, + "LinuxKernelMetadata": { + "properties": { + "name": { + "type": "string" + }, + "architecture": { + "type": "string" + }, + "version": { + "type": "string" + }, + "extendedVersion": { + "type": "string" + }, + "buildTime": { + "type": "string" + }, + "author": { + "type": "string" + }, + "format": { + "type": "string" + }, + "rwRootFS": { + "type": "boolean" + }, + "swapDevice": { + "type": "integer" + }, + "rootDevice": { + "type": "integer" + }, + "videoMode": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "architecture", + "version" + ] + }, + "LinuxKernelModuleMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "sourceVersion": { + "type": "string" + }, + "path": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "license": { + "type": "string" + }, + "kernelVersion": { + "type": "string" + }, + "versionMagic": { + "type": "string" + }, + "parameters": { + "patternProperties": { + ".*": { + "$ref": "#/$defs/LinuxKernelModuleParameter" + } + }, + "type": "object" + } + }, + "type": "object" + }, + "LinuxKernelModuleParameter": { + "properties": { + "type": { + "type": "string" + }, + "description": { + "type": "string" + } + }, + "type": "object" + }, + "LinuxRelease": { + "properties": { + "prettyName": { + "type": "string" + }, + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "idLike": { + "$ref": "#/$defs/IDLikes" + }, + "version": { + "type": "string" + }, + "versionID": { + "type": "string" + }, + "versionCodename": { + "type": "string" + }, + "buildID": { + "type": "string" + }, + "imageID": { + "type": "string" + }, + "imageVersion": { + "type": "string" + }, + "variant": { + "type": "string" + }, + "variantID": { + "type": "string" + }, + "homeURL": { + "type": "string" + }, + "supportURL": { + "type": "string" + }, + "bugReportURL": { + "type": "string" + }, + "privacyPolicyURL": { + "type": "string" + }, + "cpeName": { + "type": "string" + }, + "supportEnd": { + "type": "string" + } + }, + "type": "object" + }, + "Location": { + "properties": { + "path": { + "type": "string" + }, + "layerID": { + "type": "string" + }, + "annotations": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "MixLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "NixStoreMetadata": { + "properties": { + "outputHash": { + "type": "string" + }, + "output": { + "type": "string" + }, + "files": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "outputHash", + "files" + ] + }, + "NpmPackageJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "private": { + "type": "boolean" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "homepage", + "description", + "url", + "private" + ] + }, + "NpmPackageLockJSONMetadata": { + "properties": { + "resolved": { + "type": "string" + }, + "integrity": { + "type": "string" + } + }, + "type": "object", + "required": [ + "resolved", + "integrity" + ] + }, + "Package": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "foundBy": { + "type": "string" + }, + "locations": { + "items": { + "$ref": "#/$defs/Location" + }, + "type": "array" + }, + "licenses": { + "$ref": "#/$defs/licenses" + }, + "language": { + "type": "string" + }, + "cpes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "purl": { + "type": "string" + }, + "metadataType": { + "type": "string" + }, + "metadata": { + "anyOf": [ + { + "type": "null" + }, + { + "$ref": "#/$defs/AlpmMetadata" + }, + { + "$ref": "#/$defs/ApkMetadata" + }, + { + "$ref": "#/$defs/BinaryMetadata" + }, + { + "$ref": "#/$defs/CargoPackageMetadata" + }, + { + "$ref": "#/$defs/CocoapodsMetadata" + }, + { + "$ref": "#/$defs/ConanLockMetadata" + }, + { + "$ref": "#/$defs/ConanMetadata" + }, + { + "$ref": "#/$defs/DartPubMetadata" + }, + { + "$ref": "#/$defs/DotnetDepsMetadata" + }, + { + "$ref": "#/$defs/DotnetPortableExecutableMetadata" + }, + { + "$ref": "#/$defs/DpkgMetadata" + }, + { + "$ref": "#/$defs/GemMetadata" + }, + { + "$ref": "#/$defs/GolangBinMetadata" + }, + { + "$ref": "#/$defs/GolangModMetadata" + }, + { + "$ref": "#/$defs/HackageMetadata" + }, + { + "$ref": "#/$defs/JavaMetadata" + }, + { + "$ref": "#/$defs/KbPackageMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelMetadata" + }, + { + "$ref": "#/$defs/LinuxKernelModuleMetadata" + }, + { + "$ref": "#/$defs/MixLockMetadata" + }, + { + "$ref": "#/$defs/NixStoreMetadata" + }, + { + "$ref": "#/$defs/NpmPackageJSONMetadata" + }, + { + "$ref": "#/$defs/NpmPackageLockJSONMetadata" + }, + { + "$ref": "#/$defs/PhpComposerJSONMetadata" + }, + { + "$ref": "#/$defs/PortageMetadata" + }, + { + "$ref": "#/$defs/PythonPackageMetadata" + }, + { + "$ref": "#/$defs/PythonPipfileLockMetadata" + }, + { + "$ref": "#/$defs/PythonRequirementsMetadata" + }, + { + "$ref": "#/$defs/RDescriptionFileMetadata" + }, + { + "$ref": "#/$defs/RebarLockMetadata" + }, + { + "$ref": "#/$defs/RpmMetadata" + }, + { + "$ref": "#/$defs/SwiftPackageManagerMetadata" + } + ] + } + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "foundBy", + "locations", + "licenses", + "language", + "cpes", + "purl" + ] + }, + "PhpComposerAuthors": { + "properties": { + "name": { + "type": "string" + }, + "email": { + "type": "string" + }, + "homepage": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name" + ] + }, + "PhpComposerExternalReference": { + "properties": { + "type": { + "type": "string" + }, + "url": { + "type": "string" + }, + "reference": { + "type": "string" + }, + "shasum": { + "type": "string" + } + }, + "type": "object", + "required": [ + "type", + "url", + "reference" + ] + }, + "PhpComposerJSONMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "source": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "dist": { + "$ref": "#/$defs/PhpComposerExternalReference" + }, + "require": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "provide": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "require-dev": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "suggest": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + }, + "license": { + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "type": "string" + }, + "notification-url": { + "type": "string" + }, + "bin": { + "items": { + "type": "string" + }, + "type": "array" + }, + "authors": { + "items": { + "$ref": "#/$defs/PhpComposerAuthors" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "homepage": { + "type": "string" + }, + "keywords": { + "items": { + "type": "string" + }, + "type": "array" + }, + "time": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "source", + "dist" + ] + }, + "PomParent": { + "properties": { + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "groupId", + "artifactId", + "version" + ] + }, + "PomProject": { + "properties": { + "path": { + "type": "string" + }, + "parent": { + "$ref": "#/$defs/PomParent" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "groupId", + "artifactId", + "version", + "name" + ] + }, + "PomProperties": { + "properties": { + "path": { + "type": "string" + }, + "name": { + "type": "string" + }, + "groupId": { + "type": "string" + }, + "artifactId": { + "type": "string" + }, + "version": { + "type": "string" + }, + "scope": { + "type": "string" + }, + "extraFields": { + "patternProperties": { + ".*": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object", + "required": [ + "path", + "name", + "groupId", + "artifactId", + "version" + ] + }, + "PortageFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/Digest" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PortageMetadata": { + "properties": { + "installedSize": { + "type": "integer" + }, + "files": { + "items": { + "$ref": "#/$defs/PortageFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "installedSize", + "files" + ] + }, + "PythonDirectURLOriginInfo": { + "properties": { + "url": { + "type": "string" + }, + "commitId": { + "type": "string" + }, + "vcs": { + "type": "string" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "PythonFileDigest": { + "properties": { + "algorithm": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "algorithm", + "value" + ] + }, + "PythonFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "digest": { + "$ref": "#/$defs/PythonFileDigest" + }, + "size": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path" + ] + }, + "PythonPackageMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "author": { + "type": "string" + }, + "authorEmail": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/PythonFileRecord" + }, + "type": "array" + }, + "sitePackagesRootPath": { + "type": "string" + }, + "topLevelPackages": { + "items": { + "type": "string" + }, + "type": "array" + }, + "directUrlOrigin": { + "$ref": "#/$defs/PythonDirectURLOriginInfo" + } + }, + "type": "object", + "required": [ + "name", + "version", + "author", + "authorEmail", + "platform", + "sitePackagesRootPath" + ] + }, + "PythonPipfileLockMetadata": { + "properties": { + "hashes": { + "items": { + "type": "string" + }, + "type": "array" + }, + "index": { + "type": "string" + } + }, + "type": "object", + "required": [ + "hashes", + "index" + ] + }, + "PythonRequirementsMetadata": { + "properties": { + "name": { + "type": "string" + }, + "extras": { + "items": { + "type": "string" + }, + "type": "array" + }, + "versionConstraint": { + "type": "string" + }, + "url": { + "type": "string" + }, + "markers": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "versionConstraint" + ] + }, + "RDescriptionFileMetadata": { + "properties": { + "title": { + "type": "string" + }, + "description": { + "type": "string" + }, + "author": { + "type": "string" + }, + "maintainer": { + "type": "string" + }, + "url": { + "items": { + "type": "string" + }, + "type": "array" + }, + "repository": { + "type": "string" + }, + "built": { + "type": "string" + }, + "needsCompilation": { + "type": "boolean" + }, + "imports": { + "items": { + "type": "string" + }, + "type": "array" + }, + "depends": { + "items": { + "type": "string" + }, + "type": "array" + }, + "suggests": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "RebarLockMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "pkgHash": { + "type": "string" + }, + "pkgHashExt": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version", + "pkgHash", + "pkgHashExt" + ] + }, + "Relationship": { + "properties": { + "parent": { + "type": "string" + }, + "child": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "parent", + "child", + "type" + ] + }, + "RpmMetadata": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "epoch": { + "oneOf": [ + { + "type": "integer" + }, + { + "type": "null" + } + ] + }, + "architecture": { + "type": "string" + }, + "release": { + "type": "string" + }, + "sourceRpm": { + "type": "string" + }, + "size": { + "type": "integer" + }, + "vendor": { + "type": "string" + }, + "modularityLabel": { + "type": "string" + }, + "files": { + "items": { + "$ref": "#/$defs/RpmdbFileRecord" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "name", + "version", + "epoch", + "architecture", + "release", + "sourceRpm", + "size", + "vendor", + "modularityLabel", + "files" + ] + }, + "RpmdbFileRecord": { + "properties": { + "path": { + "type": "string" + }, + "mode": { + "type": "integer" + }, + "size": { + "type": "integer" + }, + "digest": { + "$ref": "#/$defs/Digest" + }, + "userName": { + "type": "string" + }, + "groupName": { + "type": "string" + }, + "flags": { + "type": "string" + } + }, + "type": "object", + "required": [ + "path", + "mode", + "size", + "digest", + "userName", + "groupName", + "flags" + ] + }, + "Schema": { + "properties": { + "version": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "type": "object", + "required": [ + "version", + "url" + ] + }, + "SearchResult": { + "properties": { + "classification": { + "type": "string" + }, + "lineNumber": { + "type": "integer" + }, + "lineOffset": { + "type": "integer" + }, + "seekPosition": { + "type": "integer" + }, + "length": { + "type": "integer" + }, + "value": { + "type": "string" + } + }, + "type": "object", + "required": [ + "classification", + "lineNumber", + "lineOffset", + "seekPosition", + "length" + ] + }, + "Secrets": { + "properties": { + "location": { + "$ref": "#/$defs/Coordinates" + }, + "secrets": { + "items": { + "$ref": "#/$defs/SearchResult" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "location", + "secrets" + ] + }, + "Source": { + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "version": { + "type": "string" + }, + "type": { + "type": "string" + }, + "metadata": true + }, + "type": "object", + "required": [ + "id", + "name", + "version", + "type", + "metadata" + ] + }, + "SwiftPackageManagerMetadata": { + "properties": { + "revision": { + "type": "string" + } + }, + "type": "object", + "required": [ + "revision" + ] + }, + "licenses": { + "items": { + "$ref": "#/$defs/License" + }, + "type": "array" + } + } +} diff --git a/syft/formats/cyclonedxxml/encoder_test.go b/syft/formats/cyclonedxxml/encoder_test.go index 5b8781aff..17e1eb597 100644 --- a/syft/formats/cyclonedxxml/encoder_test.go +++ b/syft/formats/cyclonedxxml/encoder_test.go @@ -57,7 +57,7 @@ func redactor(values ...string) testutils.Redactor { `sha256:[A-Za-z0-9]{64}`: `sha256:redacted`, // BOM refs - `bom-ref="[a-zA-Z0-9\-:]+"`: `bom-ref:redacted`, + `bom-ref="[a-zA-Z0-9\-:]+"`: `bom-ref="redacted"`, }, ) } diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden index 592072d20..bbba18034 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxDirectoryEncoder.golden @@ -32,7 +32,7 @@ /some/path/pkg1 - + package-2 2.0.1 cpe:2.3:*:some:package:2:*:*:*:*:*:*:* diff --git a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden index 95701d100..914602f17 100644 --- a/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden +++ b/syft/formats/cyclonedxxml/test-fixtures/snapshot/TestCycloneDxImageEncoder.golden @@ -34,7 +34,7 @@ /somefile-1.txt - + package-2 2.0.1 cpe:2.3:*:some:package:2:*:*:*:*:*:*:* diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden index dac57a1da..b5f1c29a7 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONDirectoryEncoder.golden @@ -39,7 +39,7 @@ }, { "name": "package-2", - "SPDXID": "SPDXRef-Package-deb-package-2-db4abfe497c180d3", + "SPDXID": "SPDXRef-Package-deb-package-2-ad5013466727018f", "versionInfo": "2.0.1", "supplier": "NOASSERTION", "downloadLocation": "NOASSERTION", @@ -78,7 +78,7 @@ }, { "spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path", - "relatedSpdxElement": "SPDXRef-Package-deb-package-2-db4abfe497c180d3", + "relatedSpdxElement": "SPDXRef-Package-deb-package-2-ad5013466727018f", "relationshipType": "CONTAINS" }, { diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden index 73e27667e..009333c6d 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXJSONImageEncoder.golden @@ -39,7 +39,7 @@ }, { "name": "package-2", - "SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4", + "SPDXID": "SPDXRef-Package-deb-package-2-f27313b22a5ba330", "versionInfo": "2.0.1", "supplier": "NOASSERTION", "downloadLocation": "NOASSERTION", @@ -92,7 +92,7 @@ }, { "spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input", - "relatedSpdxElement": "SPDXRef-Package-deb-package-2-958443e2d9304af4", + "relatedSpdxElement": "SPDXRef-Package-deb-package-2-f27313b22a5ba330", "relationshipType": "CONTAINS" }, { diff --git a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 80e5b269a..260d4cba0 100644 --- a/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxjson/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -39,7 +39,7 @@ }, { "name": "package-2", - "SPDXID": "SPDXRef-Package-deb-package-2-958443e2d9304af4", + "SPDXID": "SPDXRef-Package-deb-package-2-f27313b22a5ba330", "versionInfo": "2.0.1", "supplier": "NOASSERTION", "downloadLocation": "NOASSERTION", @@ -214,7 +214,7 @@ }, { "spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input", - "relatedSpdxElement": "SPDXRef-Package-deb-package-2-958443e2d9304af4", + "relatedSpdxElement": "SPDXRef-Package-deb-package-2-f27313b22a5ba330", "relationshipType": "CONTAINS" }, { diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden index 764ac9830..8b42da861 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXRelationshipOrder.golden @@ -61,7 +61,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951 ##### Package: package-2 PackageName: package-2 -SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4 +SPDXID: SPDXRef-Package-deb-package-2-f27313b22a5ba330 PackageVersion: 2.0.1 PackageSupplier: NOASSERTION PackageDownloadLocation: NOASSERTION @@ -97,6 +97,6 @@ Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f Relationship: SPDXRef-Package-python-package-1-125840abc1c66dd7 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-125840abc1c66dd7 -Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-958443e2d9304af4 +Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-f27313b22a5ba330 Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden index e50194535..7cfc999cb 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueDirectoryEncoder.golden @@ -20,7 +20,7 @@ FilesAnalyzed: false ##### Package: package-2 PackageName: package-2 -SPDXID: SPDXRef-Package-deb-package-2-db4abfe497c180d3 +SPDXID: SPDXRef-Package-deb-package-2-ad5013466727018f PackageVersion: 2.0.1 PackageSupplier: NOASSERTION PackageDownloadLocation: NOASSERTION @@ -50,6 +50,6 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-2 ##### Relationships Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-9265397e5e15168a -Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-db4abfe497c180d3 +Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-ad5013466727018f Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path diff --git a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden index 3c640ea72..8c3d1f705 100644 --- a/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden +++ b/syft/formats/spdxtagvalue/test-fixtures/snapshot/TestSPDXTagValueImageEncoder.golden @@ -23,7 +23,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951 ##### Package: package-2 PackageName: package-2 -SPDXID: SPDXRef-Package-deb-package-2-958443e2d9304af4 +SPDXID: SPDXRef-Package-deb-package-2-f27313b22a5ba330 PackageVersion: 2.0.1 PackageSupplier: NOASSERTION PackageDownloadLocation: NOASSERTION @@ -53,6 +53,6 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1 ##### Relationships Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-125840abc1c66dd7 -Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-958443e2d9304af4 +Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-f27313b22a5ba330 Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden index 61bb2efe4..11674865f 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestDirectoryEncoder.golden @@ -41,7 +41,7 @@ } }, { - "id": "db4abfe497c180d3", + "id": "ad5013466727018f", "name": "package-2", "version": "2.0.1", "type": "deb", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden index f83789be5..10a5cc12c 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestEncodeFullJSONDocument.golden @@ -36,7 +36,7 @@ } }, { - "id": "9fd0b9f41034991d", + "id": "aa0ca2c331576dfd", "name": "package-2", "version": "2.0.1", "type": "deb", diff --git a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden index 518a90ab5..ba156fb88 100644 --- a/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden +++ b/syft/formats/syftjson/test-fixtures/snapshot/TestImageEncoder.golden @@ -37,7 +37,7 @@ } }, { - "id": "958443e2d9304af4", + "id": "f27313b22a5ba330", "name": "package-2", "version": "2.0.1", "type": "deb", diff --git a/syft/pkg/cataloger/deb/cataloger_test.go b/syft/pkg/cataloger/deb/cataloger_test.go index 6e035be87..e547206e5 100644 --- a/syft/pkg/cataloger/deb/cataloger_test.go +++ b/syft/pkg/cataloger/deb/cataloger_test.go @@ -44,6 +44,11 @@ func TestDpkgCataloger(t *testing.T) { Contains configuration files and directories required for authentication to work on Debian systems. This package is required on almost all installations.`, + Depends: []string{ + "debconf (>= 0.5) | debconf-2.0", + "debconf (>= 1.5.19) | cdebconf", + "libpam-modules (>= 1.0.1-6)", + }, Files: []pkg.DpkgFileRecord{ { Path: "/etc/pam.conf", @@ -112,6 +117,7 @@ func TestDpkgCataloger(t *testing.T) { SQLite is a C library that implements an SQL database engine. Programs that link with the SQLite library can have SQL database access without running a separate RDBMS process.`, + Depends: []string{"libc6 (>= 2.29)"}, Files: []pkg.DpkgFileRecord{ {Path: "/usr/lib/aarch64-linux-gnu/libsqlite3.so.0.8.6", Digest: &file.Digest{ Algorithm: "md5", diff --git a/syft/pkg/cataloger/deb/package.go b/syft/pkg/cataloger/deb/package.go index ebd72a77a..eb9b551c9 100644 --- a/syft/pkg/cataloger/deb/package.go +++ b/syft/pkg/cataloger/deb/package.go @@ -36,13 +36,15 @@ func newDpkgPackage(d pkg.DpkgMetadata, dbLocation file.Location, resolver file. Metadata: d, } - // the current entry only has what may have been listed in the status file, however, there are additional - // files that are listed in multiple other locations. We should retrieve them all and merge the file lists - // together. - mergeFileListing(resolver, dbLocation, &p) + if resolver != nil { + // the current entry only has what may have been listed in the status file, however, there are additional + // files that are listed in multiple other locations. We should retrieve them all and merge the file lists + // together. + mergeFileListing(resolver, dbLocation, &p) - // fetch additional data from the copyright file to derive the license information - addLicenses(resolver, dbLocation, &p) + // fetch additional data from the copyright file to derive the license information + addLicenses(resolver, dbLocation, &p) + } p.SetID() diff --git a/syft/pkg/cataloger/deb/parse_dpkg_db.go b/syft/pkg/cataloger/deb/parse_dpkg_db.go index 0a7dccb2d..03c66c583 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_db.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_db.go @@ -35,7 +35,7 @@ func parseDpkgDB(resolver file.Resolver, env *generic.Environment, reader file.L pkgs = append(pkgs, newDpkgPackage(m, reader.Location, resolver, env.LinuxRelease)) } - return pkgs, nil, nil + return pkgs, associateRelationships(pkgs), nil } // parseDpkgStatus is a parser function for Debian DB status contents, returning all Debian packages listed. @@ -63,6 +63,22 @@ func parseDpkgStatus(reader io.Reader) ([]pkg.DpkgMetadata, error) { return metadata, nil } +// dpkgExtractedMetadata is an adapter struct to capture the fields from the dpkg status file, however, the final +// pkg.DpkgMetadata struct has different types for some fields (e.g. Provides, Depends, and PreDepends is []string, not a string). +type dpkgExtractedMetadata struct { + Package string `mapstructure:"Package"` + Source string `mapstructure:"Source"` + Version string `mapstructure:"Version"` + SourceVersion string `mapstructure:"SourceVersion"` + Architecture string `mapstructure:"Architecture"` + Maintainer string `mapstructure:"Maintainer"` + InstalledSize int `mapstructure:"InstalledSize"` + Description string `mapstructure:"Description"` + Provides string `mapstructure:"Provides"` + Depends string `mapstructure:"Depends"` + PreDepends string `mapstructure:"PreDepends"` // note: original doc is Pre-Depends +} + // parseDpkgStatusEntry returns an individual Dpkg entry, or returns errEndOfPackages if there are no more packages to parse from the reader. func parseDpkgStatusEntry(reader *bufio.Reader) (*pkg.DpkgMetadata, error) { var retErr error @@ -77,22 +93,36 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (*pkg.DpkgMetadata, error) { retErr = err } - entry := pkg.DpkgMetadata{} - err = mapstructure.Decode(dpkgFields, &entry) + raw := dpkgExtractedMetadata{} + err = mapstructure.Decode(dpkgFields, &raw) if err != nil { return nil, err } - sourceName, sourceVersion := extractSourceVersion(entry.Source) + sourceName, sourceVersion := extractSourceVersion(raw.Source) if sourceVersion != "" { - entry.SourceVersion = sourceVersion - entry.Source = sourceName + raw.SourceVersion = sourceVersion + raw.Source = sourceName } - if entry.Package == "" { + if raw.Package == "" { return nil, retErr } + entry := pkg.DpkgMetadata{ + Package: raw.Package, + Source: raw.Source, + Version: raw.Version, + SourceVersion: raw.SourceVersion, + Architecture: raw.Architecture, + Maintainer: raw.Maintainer, + InstalledSize: raw.InstalledSize, + Description: raw.Description, + Provides: splitPkgList(raw.Provides), + Depends: splitPkgList(raw.Depends), + PreDepends: splitPkgList(raw.PreDepends), + } + // there may be an optional conffiles section that we should persist as files if conffilesSection, exists := dpkgFields["Conffiles"]; exists && conffilesSection != nil { if sectionStr, ok := conffilesSection.(string); ok { @@ -108,6 +138,17 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (*pkg.DpkgMetadata, error) { return &entry, retErr } +func splitPkgList(pkgList string) (ret []string) { + fields := strings.Split(pkgList, ",") + for _, field := range fields { + field = strings.TrimSpace(field) + if field != "" { + ret = append(ret, field) + } + } + return ret +} + func extractAllFields(reader *bufio.Reader) (map[string]interface{}, error) { dpkgFields := make(map[string]interface{}) var key string @@ -195,3 +236,79 @@ func handleNewKeyValue(line string) (key string, val interface{}, err error) { return "", nil, fmt.Errorf("cannot parse field from line: '%s'", line) } + +// associateRelationships will create relationships between packages based on the "Depends", "Pre-Depends", and "Provides" +// fields for installed packages. if there is an installed package that has a dependency that is (somehow) not installed, +// then that relationship (between the installed and uninstalled package) will NOT be created. +func associateRelationships(pkgs []pkg.Package) (relationships []artifact.Relationship) { + // map["provides" + "package"] -> packages that provide that package + lookup := make(map[string][]pkg.Package) + + // read provided and add as keys for lookup keys as well as package names + for _, p := range pkgs { + meta, ok := p.Metadata.(pkg.DpkgMetadata) + if !ok { + log.Warnf("cataloger failed to extract dpkg 'provides' metadata for package %+v", p.Name) + continue + } + lookup[p.Name] = append(lookup[p.Name], p) + for _, provides := range meta.Provides { + k := stripVersionSpecifier(provides) + lookup[k] = append(lookup[k], p) + } + } + + // read "Depends" and "Pre-Depends" and match with keys + for _, p := range pkgs { + meta, ok := p.Metadata.(pkg.DpkgMetadata) + if !ok { + log.Warnf("cataloger failed to extract dpkg 'dependency' metadata for package %+v", p.Name) + continue + } + + var allDeps []string + allDeps = append(allDeps, meta.Depends...) + allDeps = append(allDeps, meta.PreDepends...) + + for _, depSpecifier := range allDeps { + deps := splitPackageChoice(depSpecifier) + for _, dep := range deps { + for _, depPkg := range lookup[dep] { + relationships = append(relationships, artifact.Relationship{ + From: depPkg, + To: p, + Type: artifact.DependencyOfRelationship, + }) + } + } + } + } + return relationships +} + +func stripVersionSpecifier(s string) string { + // examples: + // libgmp10 (>= 2:6.2.1+dfsg1) --> libgmp10 + // libgmp10 --> libgmp10 + // foo [i386] --> foo + // default-mta | mail-transport-agent --> default-mta | mail-transport-agent + // kernel-headers-2.2.10 [!hurd-i386] --> kernel-headers-2.2.10 + + items := internal.SplitAny(s, "[(<>=") + if len(items) == 0 { + return s + } + + return strings.TrimSpace(items[0]) +} + +func splitPackageChoice(s string) (ret []string) { + fields := strings.Split(s, "|") + for _, field := range fields { + field = strings.TrimSpace(field) + if field != "" { + ret = append(ret, stripVersionSpecifier(field)) + } + } + return ret +} diff --git a/syft/pkg/cataloger/deb/parse_dpkg_db_test.go b/syft/pkg/cataloger/deb/parse_dpkg_db_test.go index 0a2c58bd8..b2d72f287 100644 --- a/syft/pkg/cataloger/deb/parse_dpkg_db_test.go +++ b/syft/pkg/cataloger/deb/parse_dpkg_db_test.go @@ -11,9 +11,11 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/pkg/cataloger/generic" "github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest" ) @@ -48,6 +50,18 @@ func Test_parseDpkgStatus(t *testing.T) { * apt-cdrom to use removable media as a source for packages * apt-config as an interface to the configuration settings * apt-key as an interface to manage authentication keys`, + Provides: []string{"apt-transport-https (= 1.8.2)"}, + Depends: []string{ + "adduser", + "gpgv | gpgv2 | gpgv1", + "debian-archive-keyring", + "libapt-pkg5.0 (>= 1.7.0~alpha3~)", + "libc6 (>= 2.15)", + "libgcc1 (>= 1:3.0)", + "libgnutls30 (>= 3.6.6)", + "libseccomp2 (>= 1.0.1)", + "libstdc++6 (>= 5.2)", + }, Files: []pkg.DpkgFileRecord{ { Path: "/etc/apt/apt.conf.d/01autoremove", @@ -110,6 +124,18 @@ func Test_parseDpkgStatus(t *testing.T) { * apt-cdrom to use removable media as a source for packages * apt-config as an interface to the configuration settings * apt-key as an interface to manage authentication keys`, + Provides: []string{"apt-transport-https (= 1.8.2)"}, + Depends: []string{ + "adduser", + "gpgv | gpgv2 | gpgv1", + "debian-archive-keyring", + "libapt-pkg5.0 (>= 1.7.0~alpha3~)", + "libc6 (>= 2.15)", + "libgcc1 (>= 1:3.0)", + "libgnutls30 (>= 3.6.6)", + "libseccomp2 (>= 1.0.1)", + "libstdc++6 (>= 5.2)", + }, Files: []pkg.DpkgFileRecord{}, }, }, @@ -135,7 +161,9 @@ func Test_parseDpkgStatus(t *testing.T) { globe. It is updated periodically to reflect changes made by political bodies to time zone boundaries, UTC offsets, and daylight-saving rules.`, - Files: []pkg.DpkgFileRecord{}, + Provides: []string{"tzdata-buster"}, + Depends: []string{"debconf (>= 0.5) | debconf-2.0"}, + Files: []pkg.DpkgFileRecord{}, }, { Package: "util-linux", @@ -149,6 +177,14 @@ func Test_parseDpkgStatus(t *testing.T) { important utilities included in this package allow you to view kernel messages, create new filesystems, view block device information, interface with real time clock, etc.`, + Depends: []string{"fdisk", "login (>= 1:4.5-1.1~)"}, + PreDepends: []string{ + "libaudit1 (>= 1:2.2.1)", "libblkid1 (>= 2.31.1)", "libc6 (>= 2.25)", + "libcap-ng0 (>= 0.7.9)", "libmount1 (>= 2.25)", "libpam0g (>= 0.99.7.1)", + "libselinux1 (>= 2.6-3~)", "libsmartcols1 (>= 2.33)", "libsystemd0", + "libtinfo6 (>= 6)", "libudev1 (>= 183)", "libuuid1 (>= 2.16)", + "zlib1g (>= 1:1.1.4)", + }, Files: []pkg.DpkgFileRecord{ { Path: "/etc/default/hwclock", @@ -397,3 +433,122 @@ func Test_handleNewKeyValue(t *testing.T) { }) } } + +func Test_stripVersionSpecifier(t *testing.T) { + + tests := []struct { + name string + input string + want string + }{ + { + name: "package name only", + input: "test", + want: "test", + }, + { + name: "with version", + input: "test (1.2.3)", + want: "test", + }, + { + name: "multiple packages", + input: "test | other", + want: "test | other", + }, + { + name: "with architecture specifiers", + input: "test [amd64 i386]", + want: "test", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.want, stripVersionSpecifier(tt.input)) + }) + } +} + +func Test_associateRelationships(t *testing.T) { + tests := []struct { + name string + fixture string + wantRelationships map[string][]string + }{ + { + name: "relationships for coreutils", + fixture: "test-fixtures/status/coreutils-relationships", + wantRelationships: map[string][]string{ + "coreutils": {"libacl1", "libattr1", "libc6", "libgmp10", "libselinux1"}, + "libacl1": {"libc6"}, + "libattr1": {"libc6"}, + "libc6": {"libgcc-s1"}, + "libgcc-s1": {"gcc-12-base", "libc6"}, + "libgmp10": {"libc6"}, + "libpcre2-8-0": {"libc6"}, + "libselinux1": {"libc6", "libpcre2-8-0"}, + }, + }, + { + name: "relationships from dpkg example docs", + fixture: "test-fixtures/status/doc-examples", + wantRelationships: map[string][]string{ + "made-up-package-1": {"kernel-headers-2.2.10", "hurd-dev", "gnumach-dev"}, + "made-up-package-2": {"libluajit5.1-dev", "liblua5.1-dev"}, + "made-up-package-3": {"foo", "bar"}, + // note that the "made-up-package-4" depends on "made-up-package-5" but not via the direct + // package name, but through the "provides" virtual package name "virtual-package-5". + "made-up-package-4": {"made-up-package-5"}, + // note that though there is a "default-mta | mail-transport-agent | not-installed" + // dependency choice we raise up the packages that are installed for every choice. + // In this case that means that "default-mta" and "mail-transport-agent". + "mutt": {"libc6", "default-mta", "mail-transport-agent"}, + }, + }, + { + name: "relationships for libpam-runtime", + fixture: "test-fixtures/status/libpam-runtime", + wantRelationships: map[string][]string{ + "libpam-runtime": {"debconf1", "debconf-2.0", "debconf2", "cdebconf", "libpam-modules"}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.fixture) + require.NoError(t, err) + + reader := file.NewLocationReadCloser(file.NewLocation(tt.fixture), f) + + pkgs, relationships, err := parseDpkgDB(nil, &generic.Environment{}, reader) + require.NotEmpty(t, pkgs) + require.NotEmpty(t, relationships) + require.NoError(t, err) + + if d := cmp.Diff(tt.wantRelationships, abstractRelationships(t, relationships)); d != "" { + t.Errorf("unexpected relationships (-want +got):\n%s", d) + } + }) + } +} + +func abstractRelationships(t testing.TB, relationships []artifact.Relationship) map[string][]string { + t.Helper() + + abstracted := make(map[string][]string) + for _, relationship := range relationships { + fromPkg, ok := relationship.From.(pkg.Package) + if !ok { + continue + } + toPkg, ok := relationship.To.(pkg.Package) + if !ok { + continue + } + + // we build this backwards since we use DependencyOfRelationship instead of DependsOn + abstracted[toPkg.Name] = append(abstracted[toPkg.Name], fromPkg.Name) + } + + return abstracted +} diff --git a/syft/pkg/cataloger/deb/test-fixtures/status/coreutils-relationships b/syft/pkg/cataloger/deb/test-fixtures/status/coreutils-relationships new file mode 100644 index 000000000..1097fea56 --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/status/coreutils-relationships @@ -0,0 +1,114 @@ +Package: coreutils +Essential: yes +Status: install ok installed +Priority: required +Section: utils +Installed-Size: 20272 +Maintainer: Michael Stone +Architecture: arm64 +Multi-Arch: foreign +Version: 9.1-1 +Pre-Depends: libacl1 (>= 2.2.23), libattr1 (>= 1:2.4.44), libc6 (>= 2.34), libgmp10 (>= 2:6.2.1+dfsg1), libselinux1 (>= 3.1~) + +Package: libacl1 +Status: install ok installed +Priority: optional +Section: libs +Installed-Size: 101 +Maintainer: Guillem Jover +Architecture: arm64 +Multi-Arch: same +Source: acl +Version: 2.3.1-3 +Depends: libc6 (>= 2.33) + +Package: libc6 +Status: install ok installed +Priority: optional +Section: libs +Installed-Size: 23127 +Maintainer: GNU Libc Maintainers +Architecture: arm64 +Multi-Arch: same +Source: glibc +Version: 2.36-9+deb12u1 +Depends: libgcc-s1 +Recommends: libidn2-0 (>= 2.0.5~) +Suggests: glibc-doc, debconf | debconf-2.0, libc-l10n, locales, libnss-nis, libnss-nisplus +Breaks: aide (<< 0.17.3-4+b3), busybox (<< 1.30.1-6), chrony (<< 4.2-3~), fakechroot (<< 2.19-3.5), firefox (<< 91~), firefox-esr (<< 91~), gnumach-image-1.8-486 (<< 2:1.8+git20210923~), gnumach-image-1.8-486-dbg (<< 2:1.8+git20210923~), gnumach-image-1.8-xen-486 (<< 2:1.8+git20210923~), gnumach-image-1.8-xen-486-dbg (<< 2:1.8+git20210923~), hurd (<< 1:0.9.git20220301-2), ioquake3 (<< 1.36+u20200211.f2c61c1~dfsg-2~), iraf-fitsutil (<< 2018.07.06-4), libgegl-0.4-0 (<< 0.4.18), libtirpc1 (<< 0.2.3), locales (<< 2.36), locales-all (<< 2.36), macs (<< 2.2.7.1-3~), nocache (<< 1.1-1~), nscd (<< 2.36), openarena (<< 0.8.8+dfsg-4~), openssh-server (<< 1:8.1p1-5), python3-iptables (<< 1.0.0-2), r-cran-later (<< 0.7.5+dfsg-2), tinydns (<< 1:1.05-14), valgrind (<< 1:3.19.0-1~), wcc (<< 0.0.2+dfsg-3) + +Package: libgcc-s1 +Protected: yes +Status: install ok installed +Priority: optional +Section: libs +Installed-Size: 147 +Maintainer: Debian GCC Maintainers +Architecture: arm64 +Multi-Arch: same +Source: gcc-12 +Version: 12.2.0-14 +Replaces: libgcc1 (<< 1:10) +Provides: libgcc1 (= 1:12.2.0-14) +Depends: gcc-12-base (= 12.2.0-14), libc6 (>= 2.35) + +Package: gcc-12-base +Status: install ok installed +Priority: optional +Section: libs +Installed-Size: 100 +Maintainer: Debian GCC Maintainers +Architecture: arm64 +Multi-Arch: same +Source: gcc-12 +Version: 12.2.0-14 +Breaks: gnat (<< 7) + +Package: libattr1 +Status: install ok installed +Priority: optional +Section: libs +Installed-Size: 99 +Maintainer: Guillem Jover +Architecture: arm64 +Multi-Arch: same +Source: attr +Version: 1:2.5.1-4 +Depends: libc6 (>= 2.17) + +Package: libgmp10 +Status: install ok installed +Priority: optional +Section: libs +Installed-Size: 855 +Maintainer: Debian Science Team +Architecture: arm64 +Multi-Arch: same +Source: gmp +Version: 2:6.2.1+dfsg1-1.1 +Depends: libc6 (>= 2.17) +Breaks: libmath-gmp-perl (<< 2.20-1), libmath-prime-util-gmp-perl (<< 0.51-2), postgresql-pgmp (<< 1.0.3-1) + +Package: libselinux1 +Status: install ok installed +Priority: optional +Section: libs +Installed-Size: 223 +Maintainer: Debian SELinux maintainers +Architecture: arm64 +Multi-Arch: same +Source: libselinux (3.4-1) +Version: 3.4-1+b6 +Depends: libc6 (>= 2.34), libpcre2-8-0 (>= 10.22) + +Package: libpcre2-8-0 +Status: install ok installed +Priority: optional +Section: libs +Installed-Size: 649 +Maintainer: Matthew Vernon +Architecture: arm64 +Multi-Arch: same +Source: pcre2 +Version: 10.42-1 +Depends: libc6 (>= 2.34) diff --git a/syft/pkg/cataloger/deb/test-fixtures/status/doc-examples b/syft/pkg/cataloger/deb/test-fixtures/status/doc-examples new file mode 100644 index 000000000..6b9fe186f --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/status/doc-examples @@ -0,0 +1,46 @@ +Package: mutt +Version: 1.3.17-1 +Depends: libc6 (>= 2.2.1), default-mta | mail-transport-agent | not-installed + +Package: made-up-package-1 +Version: 1.0.0 +Source: glibc +Depends: kernel-headers-2.2.10 [!hurd-i386], + hurd-dev [hurd-i386], gnumach-dev [hurd-i386] + +Package: made-up-package-2 +Version: 2.0.0 +Depends: + libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips], + liblua5.1-dev [hurd-i386 ia64 kfreebsd-amd64 s390x sparc], + +Package: made-up-package-3 +Version: 3.0.0 +Depends: foo [i386], bar [amd64] + +Package: made-up-package-4 +Version: 3.0.0 +Depends: virtual-package-5 + +Package: made-up-package-5 +Provides: virtual-package-5 (=1.0) + +Package: foo + +Package: bar + +Package: kernel-headers-2.2.10 + +Package: hurd-dev + +Package: gnumach-dev + +Package: default-mta + +Package: mail-transport-agent + +Package: libc6 + +Package: libluajit5.1-dev + +Package: liblua5.1-dev diff --git a/syft/pkg/cataloger/deb/test-fixtures/status/libpam-runtime b/syft/pkg/cataloger/deb/test-fixtures/status/libpam-runtime new file mode 100644 index 000000000..73aaf6978 --- /dev/null +++ b/syft/pkg/cataloger/deb/test-fixtures/status/libpam-runtime @@ -0,0 +1,23 @@ +Package: libpam-runtime +Status: install ok installed +Priority: required +Section: admin +Installed-Size: 876 +Maintainer: Sam Hartman +Architecture: all +Multi-Arch: foreign +Source: pam +Version: 1.5.2-6+deb12u1 +Replaces: libpam0g-dev, libpam0g-util +Depends: debconf1 (>= 0.5) | debconf-2.0, debconf2 (>= 1.5.19) | cdebconf, libpam-modules (>= 1.0.1-6) +Conflicts: libpam0g-util + +Package: debconf1 + +Package: debconf2 + +Package: debconf-2.0 + +Package: cdebconf + +Package: libpam-modules diff --git a/syft/pkg/dpkg_metadata.go b/syft/pkg/dpkg_metadata.go index 2ad5de2db..5b38be03b 100644 --- a/syft/pkg/dpkg_metadata.go +++ b/syft/pkg/dpkg_metadata.go @@ -14,16 +14,49 @@ var _ FileOwner = (*DpkgMetadata)(nil) // DpkgMetadata represents all captured data for a Debian package DB entry; available fields are described // at http://manpages.ubuntu.com/manpages/xenial/man1/dpkg-query.1.html in the --showformat section. +// Additional information about how these fields are used can be found at +// - https://www.debian.org/doc/debian-policy/ch-controlfields.html +// - https://www.debian.org/doc/debian-policy/ch-relationships.html +// - https://www.debian.org/doc/debian-policy/ch-binary.html#s-virtual-pkg +// - https://www.debian.org/doc/debian-policy/ch-relationships.html#s-virtual + type DpkgMetadata struct { - Package string `mapstructure:"Package" json:"package"` - Source string `mapstructure:"Source" json:"source" cyclonedx:"source"` - Version string `mapstructure:"Version" json:"version"` - SourceVersion string `mapstructure:"SourceVersion" json:"sourceVersion" cyclonedx:"sourceVersion"` - Architecture string `mapstructure:"Architecture" json:"architecture"` - Maintainer string `mapstructure:"Maintainer" json:"maintainer"` - InstalledSize int `mapstructure:"InstalledSize" json:"installedSize" cyclonedx:"installedSize"` - Description string `mapstructure:"Description" hash:"ignore" json:"-"` - Files []DpkgFileRecord `json:"files"` + Package string `json:"package"` + Source string `json:"source" cyclonedx:"source"` + Version string `json:"version"` + SourceVersion string `json:"sourceVersion" cyclonedx:"sourceVersion"` + + // Architecture can include the following sets of values depending on context and the control file used: + // - a unique single word identifying a Debian machine architecture as described in Architecture specification string (https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s-arch-spec) . + // - an architecture wildcard identifying a set of Debian machine architectures, see Architecture wildcards (https://www.debian.org/doc/debian-policy/ch-customized-programs.html#s-arch-wildcard-spec). any matches all Debian machine architectures and is the most frequently used. + // - "all", which indicates an architecture-independent package. + // - "source", which indicates a source package. + Architecture string `json:"architecture"` + + // Maintainer is the package maintainer’s name and email address. The name must come first, then the email + // address inside angle brackets <> (in RFC822 format). + Maintainer string `json:"maintainer"` + + InstalledSize int `json:"installedSize" cyclonedx:"installedSize"` + + // Description contains a description of the binary package, consisting of two parts, the synopsis or the short + // description, and the long description (in a multiline format). + Description string `hash:"ignore" json:"-"` + + // Provides is a virtual package that is provided by one or more packages. A virtual package is one which appears + // in the Provides control field of another package. The effect is as if the package(s) which provide a particular + // virtual package name had been listed by name everywhere the virtual package name appears. (See also Virtual packages) + Provides []string `json:"provides,omitempty"` + + // Depends This declares an absolute dependency. A package will not be configured unless all of the packages listed in + // its Depends field have been correctly configured (unless there is a circular dependency). + Depends []string `json:"depends,omitempty"` + + // PreDepends is like Depends, except that it also forces dpkg to complete installation of the packages named + // before even starting the installation of the package which declares the pre-dependency. + PreDepends []string `json:"preDepends,omitempty"` + + Files []DpkgFileRecord `json:"files"` } // DpkgFileRecord represents a single file attributed to a debian package. From d1120ad56ee325e4de2d8c3ae29fb10d6378b238 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Wed, 11 Oct 2023 09:52:22 -0400 Subject: [PATCH 099/106] chore: add automated homebrew action (#2164) * chore: add automated homebrew action Signed-off-by: Christopher Phillips * migrate homebrew publish step to separate post-release workflow Signed-off-by: Alex Goodman --------- Signed-off-by: Christopher Phillips Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman --- .github/workflows/release-homebrew.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/workflows/release-homebrew.yaml diff --git a/.github/workflows/release-homebrew.yaml b/.github/workflows/release-homebrew.yaml new file mode 100644 index 000000000..0ccacd17b --- /dev/null +++ b/.github/workflows/release-homebrew.yaml @@ -0,0 +1,25 @@ +name: "Release Homebrew" + +on: + release: + types: [published] + +jobs: + homebrew: + runs-on: ubuntu-20.04 + steps: + - name: Update Homebrew formula + uses: dawidd6/action-homebrew-bump-formula@d3667e5ae14df19579e4414897498e3e88f2f458 # v3.10.0 + with: + token: ${{ secrets.HOMEBREW_TOKEN }} + org: anchore + formula: syft + + - uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #v3.15.1 + if: ${{ failure() }} + with: + status: ${{ job.status }} + fields: repo,workflow,action,eventName + text: "Post-release homebrew publish workflow has failed: https://github.com/anchore/syft/actions/workflows/release-homebrew.yaml" + env: + SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }} From b899536814dd6af4d7b22b31071b4d6320391da8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 09:52:42 -0400 Subject: [PATCH 100/106] chore(deps): bump github.com/charmbracelet/lipgloss from 0.8.0 to 0.9.0 (#2216) Bumps [github.com/charmbracelet/lipgloss](https://github.com/charmbracelet/lipgloss) from 0.8.0 to 0.9.0. - [Release notes](https://github.com/charmbracelet/lipgloss/releases) - [Commits](https://github.com/charmbracelet/lipgloss/compare/v0.8.0...v0.9.0) --- updated-dependencies: - dependency-name: github.com/charmbracelet/lipgloss dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index c00928b16..8e27885c4 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 github.com/bmatcuk/doublestar/v4 v4.6.0 github.com/charmbracelet/bubbletea v0.24.2 - github.com/charmbracelet/lipgloss v0.8.0 + github.com/charmbracelet/lipgloss v0.9.0 github.com/dave/jennifer v1.7.0 github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da github.com/docker/distribution v2.8.3+incompatible // indirect @@ -153,7 +153,7 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.18 // indirect github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect diff --git a/go.sum b/go.sum index 22bc5a6c7..9c1572932 100644 --- a/go.sum +++ b/go.sum @@ -156,8 +156,8 @@ github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06 github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.8.0 h1:IS00fk4XAHcf8uZKc3eHeMUTCxUH6NkaTrdyCQk84RU= -github.com/charmbracelet/lipgloss v0.8.0/go.mod h1:p4eYUZZJ/0oXTuCQKFF8mqyKCz0ja6y+7DniDDw5KKU= +github.com/charmbracelet/lipgloss v0.9.0 h1:BHIM7U4vX77xGEld8GrTKspBMtSv7j0wxPCH73nrdxE= +github.com/charmbracelet/lipgloss v0.9.0/go.mod h1:h8KDyaivONasw1Bhb4nWiKlk4P1wHPly+3+3v6EFMmA= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= @@ -533,8 +533,8 @@ github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZ github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= +github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= From 0302fc5b480a43f94648936fb35ed2564afb8a6c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 09:53:00 -0400 Subject: [PATCH 101/106] chore(deps): bump github.com/google/go-cmp from 0.5.9 to 0.6.0 (#2215) Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.5.9 to 0.6.0. - [Release notes](https://github.com/google/go-cmp/releases) - [Commits](https://github.com/google/go-cmp/compare/v0.5.9...v0.6.0) --- updated-dependencies: - dependency-name: github.com/google/go-cmp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 8e27885c4..f5dc3d37c 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/go-git/go-billy/v5 v5.5.0 github.com/go-git/go-git/v5 v5.9.0 github.com/go-test/deep v1.1.0 - github.com/google/go-cmp v0.5.9 + github.com/google/go-cmp v0.6.0 github.com/google/go-containerregistry v0.16.1 github.com/google/licensecheck v0.3.1 github.com/google/uuid v1.3.1 diff --git a/go.sum b/go.sum index 9c1572932..ed5624404 100644 --- a/go.sum +++ b/go.sum @@ -366,8 +366,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ= github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= From 7732cd3b48139c013b2d3358dd41feb175212819 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 11 Oct 2023 13:52:07 -0400 Subject: [PATCH 102/106] chore(deps): bump golang.org/x/net from 0.16.0 to 0.17.0 (#2214) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.16.0 to 0.17.0. - [Commits](https://github.com/golang/net/compare/v0.16.0...v0.17.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index f5dc3d37c..0b1164ab8 100644 --- a/go.mod +++ b/go.mod @@ -70,7 +70,7 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 golang.org/x/mod v0.13.0 - golang.org/x/net v0.16.0 + golang.org/x/net v0.17.0 golang.org/x/term v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 modernc.org/sqlite v1.26.0 diff --git a/go.sum b/go.sum index ed5624404..6303313b1 100644 --- a/go.sum +++ b/go.sum @@ -936,8 +936,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.16.0 h1:7eBu7KsSvFDtSXUIDbh3aqlK4DPsZ1rByC8PFfBThos= -golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= From fe7a417fb2394079e453db195f99efc29c3a5c39 Mon Sep 17 00:00:00 2001 From: Benji Visser Date: Wed, 11 Oct 2023 14:01:24 -0400 Subject: [PATCH 103/106] Parse donet dependency trees (#2143) * add dependency information for .NET pkgs Signed-off-by: Benji Visser * update pkg coverage directory test Signed-off-by: Benji Visser * reverse dependsOn relationship Signed-off-by: Benji Visser * update root pkg parsing Signed-off-by: Benji Visser * add comments about the test relationships represented Signed-off-by: Alex Goodman * add docs around relationship sorting functions + update test helpers Signed-off-by: Alex Goodman --------- Signed-off-by: Benji Visser Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman --- syft/pkg/cataloger/dotnet/package.go | 31 +- .../pkg/cataloger/dotnet/parse_dotnet_deps.go | 83 ++- .../dotnet/parse_dotnet_deps_test.go | 529 ++++++++++++------ .../test-fixtures/TestLibrary.deps.json | 2 +- .../internal/pkgtest/test_generic_parser.go | 8 + syft/pkg/relationships.go | 40 +- .../catalog_packages_cases_test.go | 2 + 7 files changed, 494 insertions(+), 201 deletions(-) diff --git a/syft/pkg/cataloger/dotnet/package.go b/syft/pkg/cataloger/dotnet/package.go index c8cb261a6..a7b3b209f 100644 --- a/syft/pkg/cataloger/dotnet/package.go +++ b/syft/pkg/cataloger/dotnet/package.go @@ -1,6 +1,8 @@ package dotnet import ( + "fmt" + "regexp" "strings" "github.com/anchore/packageurl-go" @@ -9,13 +11,7 @@ import ( ) func newDotnetDepsPackage(nameVersion string, lib dotnetDepsLibrary, locations ...file.Location) *pkg.Package { - if lib.Type != "package" { - return nil - } - - fields := strings.Split(nameVersion, "/") - name := fields[0] - version := fields[1] + name, version := extractNameAndVersion(nameVersion) m := pkg.DotnetDepsMetadata{ Name: name, @@ -41,6 +37,27 @@ func newDotnetDepsPackage(nameVersion string, lib dotnetDepsLibrary, locations . return p } +func getDepsJSONFilePrefix(p string) string { + r := regexp.MustCompile(`([^\/]+)\.deps\.json$`) + match := r.FindStringSubmatch(p) + if len(match) > 1 { + return match[1] + } + return "" +} + +func extractNameAndVersion(nameVersion string) (name, version string) { + fields := strings.Split(nameVersion, "/") + name = fields[0] + version = fields[1] + return +} + +func createNameAndVersion(name, version string) (nameVersion string) { + nameVersion = fmt.Sprintf("%s/%s", name, version) + return +} + func packageURL(m pkg.DotnetDepsMetadata) string { var qualifiers packageurl.Qualifiers diff --git a/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go b/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go index 2c7e1cf0b..e99811ac8 100644 --- a/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go +++ b/syft/pkg/cataloger/dotnet/parse_dotnet_deps.go @@ -5,6 +5,7 @@ import ( "fmt" "sort" + "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/pkg" @@ -13,8 +14,18 @@ import ( var _ generic.Parser = parseDotnetDeps +type dotnetRuntimeTarget struct { + Name string `json:"name"` +} + +type dotnetDepsTarget struct { + Dependencies map[string]string `json:"dependencies"` + Runtime map[string]struct{} `json:"runtime"` +} type dotnetDeps struct { - Libraries map[string]dotnetDepsLibrary `json:"libraries"` + RuntimeTarget dotnetRuntimeTarget `json:"runtimeTarget"` + Targets map[string]map[string]dotnetDepsTarget `json:"targets"` + Libraries map[string]dotnetDepsLibrary `json:"libraries"` } type dotnetDepsLibrary struct { @@ -24,27 +35,55 @@ type dotnetDepsLibrary struct { HashPath string `json:"hashPath"` } +//nolint:funlen func parseDotnetDeps(_ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { var pkgs []pkg.Package + var pkgMap = make(map[string]pkg.Package) + var relationships []artifact.Relationship dec := json.NewDecoder(reader) - var p dotnetDeps - if err := dec.Decode(&p); err != nil { + var depsDoc dotnetDeps + if err := dec.Decode(&depsDoc); err != nil { return nil, nil, fmt.Errorf("failed to parse deps.json file: %w", err) } - var names []string + rootName := getDepsJSONFilePrefix(reader.AccessPath()) + if rootName == "" { + return nil, nil, fmt.Errorf("unable to determine root package name from deps.json file: %s", reader.AccessPath()) + } + var rootPkg *pkg.Package + for nameVersion, lib := range depsDoc.Libraries { + name, _ := extractNameAndVersion(nameVersion) + if lib.Type == "project" && name == rootName { + rootPkg = newDotnetDepsPackage( + nameVersion, + lib, + reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ) + } + } + if rootPkg == nil { + return nil, nil, fmt.Errorf("unable to determine root package from deps.json file: %s", reader.AccessPath()) + } + pkgs = append(pkgs, *rootPkg) + pkgMap[createNameAndVersion(rootPkg.Name, rootPkg.Version)] = *rootPkg - for nameVersion := range p.Libraries { + var names []string + for nameVersion := range depsDoc.Libraries { names = append(names, nameVersion) } - // sort the names so that the order of the packages is deterministic sort.Strings(names) for _, nameVersion := range names { - lib := p.Libraries[nameVersion] + // skip the root package + name, version := extractNameAndVersion(nameVersion) + if name == rootPkg.Name && version == rootPkg.Version { + continue + } + + lib := depsDoc.Libraries[nameVersion] dotnetPkg := newDotnetDepsPackage( nameVersion, lib, @@ -53,8 +92,36 @@ func parseDotnetDeps(_ file.Resolver, _ *generic.Environment, reader file.Locati if dotnetPkg != nil { pkgs = append(pkgs, *dotnetPkg) + pkgMap[nameVersion] = *dotnetPkg } } - return pkgs, nil, nil + for pkgNameVersion, target := range depsDoc.Targets[depsDoc.RuntimeTarget.Name] { + for depName, depVersion := range target.Dependencies { + depNameVersion := createNameAndVersion(depName, depVersion) + depPkg, ok := pkgMap[depNameVersion] + if !ok { + log.Debug("unable to find package in map", depNameVersion) + continue + } + p, ok := pkgMap[pkgNameVersion] + if !ok { + log.Debug("unable to find package in map", pkgNameVersion) + continue + } + rel := artifact.Relationship{ + From: depPkg, + To: p, + Type: artifact.DependencyOfRelationship, + } + relationships = append(relationships, rel) + } + } + + // sort the relationships for deterministic output + // TODO: ideally this would be replaced with artifact.SortRelationships when one exists and is type agnostic. + // this will only consider package-to-package relationships. + pkg.SortRelationships(relationships) + + return pkgs, relationships, nil } diff --git a/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go b/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go index b85353744..b71850100 100644 --- a/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go +++ b/syft/pkg/cataloger/dotnet/parse_dotnet_deps_test.go @@ -12,202 +12,363 @@ import ( func TestParseDotnetDeps(t *testing.T) { fixture := "test-fixtures/TestLibrary.deps.json" fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture)) - expected := []pkg.Package{ - { - Name: "AWSSDK.Core", - Version: "3.7.10.6", - PURL: "pkg:nuget/AWSSDK.Core@3.7.10.6", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "AWSSDK.Core", - Version: "3.7.10.6", - Sha512: "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==", - Path: "awssdk.core/3.7.10.6", - HashPath: "awssdk.core.3.7.10.6.nupkg.sha512", - }, + rootPkg := pkg.Package{ + Name: "TestLibrary", + Version: "1.0.0", + PURL: "pkg:nuget/TestLibrary@1.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "TestLibrary", + Version: "1.0.0", }, - { - Name: "Microsoft.Extensions.DependencyInjection.Abstractions", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.DependencyInjection.Abstractions", - Version: "6.0.0", - Sha512: "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==", - Path: "microsoft.extensions.dependencyinjection.abstractions/6.0.0", - HashPath: "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512", - }, + } + testCommon := pkg.Package{ + Name: "TestCommon", + Version: "1.0.0", + PURL: "pkg:nuget/TestCommon@1.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "TestCommon", + Version: "1.0.0", }, - { - Name: "Microsoft.Extensions.DependencyInjection", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.DependencyInjection", - Version: "6.0.0", - Sha512: "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", - Path: "microsoft.extensions.dependencyinjection/6.0.0", - HashPath: "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512", - }, + } + awssdkcore := pkg.Package{ + Name: "AWSSDK.Core", + Version: "3.7.10.6", + PURL: "pkg:nuget/AWSSDK.Core@3.7.10.6", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "AWSSDK.Core", + Version: "3.7.10.6", + Sha512: "sha512-kHBB+QmosVaG6DpngXQ8OlLVVNMzltNITfsRr68Z90qO7dSqJ2EHNd8dtBU1u3AQQLqqFHOY0lfmbpexeH6Pew==", + Path: "awssdk.core/3.7.10.6", + HashPath: "awssdk.core.3.7.10.6.nupkg.sha512", }, - { - Name: "Microsoft.Extensions.Logging.Abstractions", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.Logging.Abstractions", - Version: "6.0.0", - Sha512: "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==", - Path: "microsoft.extensions.logging.abstractions/6.0.0", - HashPath: "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512", - }, + } + msftDependencyInjectionAbstractions := pkg.Package{ + Name: "Microsoft.Extensions.DependencyInjection.Abstractions", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection.Abstractions@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.DependencyInjection.Abstractions", + Version: "6.0.0", + Sha512: "sha512-xlzi2IYREJH3/m6+lUrQlujzX8wDitm4QGnUu6kUXTQAWPuZY8i+ticFJbzfqaetLA6KR/rO6Ew/HuYD+bxifg==", + Path: "microsoft.extensions.dependencyinjection.abstractions/6.0.0", + HashPath: "microsoft.extensions.dependencyinjection.abstractions.6.0.0.nupkg.sha512", }, - { - Name: "Microsoft.Extensions.Logging", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.Logging@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.Logging", - Version: "6.0.0", - Sha512: "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", - Path: "microsoft.extensions.logging/6.0.0", - HashPath: "microsoft.extensions.logging.6.0.0.nupkg.sha512", - }, + } + msftDependencyInjection := pkg.Package{ + Name: "Microsoft.Extensions.DependencyInjection", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.DependencyInjection@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.DependencyInjection", + Version: "6.0.0", + Sha512: "sha512-k6PWQMuoBDGGHOQTtyois2u4AwyVcIwL2LaSLlTZQm2CYcJ1pxbt6jfAnpWmzENA/wfrYRI/X9DTLoUkE4AsLw==", + Path: "microsoft.extensions.dependencyinjection/6.0.0", + HashPath: "microsoft.extensions.dependencyinjection.6.0.0.nupkg.sha512", }, - - { - Name: "Microsoft.Extensions.Options", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.Options@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.Options", - Version: "6.0.0", - Sha512: "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", - Path: "microsoft.extensions.options/6.0.0", - HashPath: "microsoft.extensions.options.6.0.0.nupkg.sha512", - }, + } + msftLoggingAbstractions := pkg.Package{ + Name: "Microsoft.Extensions.Logging.Abstractions", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.Logging.Abstractions@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Logging.Abstractions", + Version: "6.0.0", + Sha512: "sha512-/HggWBbTwy8TgebGSX5DBZ24ndhzi93sHUBDvP1IxbZD7FDokYzdAr6+vbWGjw2XAfR2EJ1sfKUotpjHnFWPxA==", + Path: "microsoft.extensions.logging.abstractions/6.0.0", + HashPath: "microsoft.extensions.logging.abstractions.6.0.0.nupkg.sha512", }, - { - Name: "Microsoft.Extensions.Primitives", - Version: "6.0.0", - PURL: "pkg:nuget/Microsoft.Extensions.Primitives@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Microsoft.Extensions.Primitives", - Version: "6.0.0", - Sha512: "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", - Path: "microsoft.extensions.primitives/6.0.0", - HashPath: "microsoft.extensions.primitives.6.0.0.nupkg.sha512", - }, + } + msftExtensionsLogging := pkg.Package{ + Name: "Microsoft.Extensions.Logging", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.Logging@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Logging", + Version: "6.0.0", + Sha512: "sha512-eIbyj40QDg1NDz0HBW0S5f3wrLVnKWnDJ/JtZ+yJDFnDj90VoPuoPmFkeaXrtu+0cKm5GRAwoDf+dBWXK0TUdg==", + Path: "microsoft.extensions.logging/6.0.0", + HashPath: "microsoft.extensions.logging.6.0.0.nupkg.sha512", }, - { - Name: "Newtonsoft.Json", - Version: "13.0.1", - PURL: "pkg:nuget/Newtonsoft.Json@13.0.1", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Newtonsoft.Json", - Version: "13.0.1", - Sha512: "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==", - Path: "newtonsoft.json/13.0.1", - HashPath: "newtonsoft.json.13.0.1.nupkg.sha512", - }, + } + msftExtensionsOptions := pkg.Package{ + Name: "Microsoft.Extensions.Options", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.Options@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Options", + Version: "6.0.0", + Sha512: "sha512-dzXN0+V1AyjOe2xcJ86Qbo233KHuLEY0njf/P2Kw8SfJU+d45HNS2ctJdnEnrWbM9Ye2eFgaC5Mj9otRMU6IsQ==", + Path: "microsoft.extensions.options/6.0.0", + HashPath: "microsoft.extensions.options.6.0.0.nupkg.sha512", }, - { - Name: "Serilog.Sinks.Console", - Version: "4.0.1", - PURL: "pkg:nuget/Serilog.Sinks.Console@4.0.1", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Serilog.Sinks.Console", - Version: "4.0.1", - Sha512: "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==", - Path: "serilog.sinks.console/4.0.1", - HashPath: "serilog.sinks.console.4.0.1.nupkg.sha512", - }, + } + msftExtensionsPrimitives := pkg.Package{ + Name: "Microsoft.Extensions.Primitives", + Version: "6.0.0", + PURL: "pkg:nuget/Microsoft.Extensions.Primitives@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Microsoft.Extensions.Primitives", + Version: "6.0.0", + Sha512: "sha512-9+PnzmQFfEFNR9J2aDTfJGGupShHjOuGw4VUv+JB044biSHrnmCIMD+mJHmb2H7YryrfBEXDurxQ47gJZdCKNQ==", + Path: "microsoft.extensions.primitives/6.0.0", + HashPath: "microsoft.extensions.primitives.6.0.0.nupkg.sha512", }, - { - Name: "Serilog", - Version: "2.10.0", - PURL: "pkg:nuget/Serilog@2.10.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "Serilog", - Version: "2.10.0", - Sha512: "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==", - Path: "serilog/2.10.0", - HashPath: "serilog.2.10.0.nupkg.sha512", - }, + } + newtonsoftJson := pkg.Package{ + Name: "Newtonsoft.Json", + Version: "13.0.1", + PURL: "pkg:nuget/Newtonsoft.Json@13.0.1", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Newtonsoft.Json", + Version: "13.0.1", + Sha512: "sha512-ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==", + Path: "newtonsoft.json/13.0.1", + HashPath: "newtonsoft.json.13.0.1.nupkg.sha512", }, - { - Name: "System.Diagnostics.DiagnosticSource", - Version: "6.0.0", - PURL: "pkg:nuget/System.Diagnostics.DiagnosticSource@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "System.Diagnostics.DiagnosticSource", - Version: "6.0.0", - Sha512: "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", - Path: "system.diagnostics.diagnosticsource/6.0.0", - HashPath: "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512", - }, + } + serilogSinksConsole := pkg.Package{ + Name: "Serilog.Sinks.Console", + Version: "4.0.1", + PURL: "pkg:nuget/Serilog.Sinks.Console@4.0.1", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Serilog.Sinks.Console", + Version: "4.0.1", + Sha512: "sha512-apLOvSJQLlIbKlbx+Y2UDHSP05kJsV7mou+fvJoRGs/iR+jC22r8cuFVMjjfVxz/AD4B2UCltFhE1naRLXwKNw==", + Path: "serilog.sinks.console/4.0.1", + HashPath: "serilog.sinks.console.4.0.1.nupkg.sha512", }, - { - Name: "System.Runtime.CompilerServices.Unsafe", - Version: "6.0.0", - PURL: "pkg:nuget/System.Runtime.CompilerServices.Unsafe@6.0.0", - Locations: fixtureLocationSet, - Language: pkg.Dotnet, - Type: pkg.DotnetPkg, - MetadataType: pkg.DotnetDepsMetadataType, - Metadata: pkg.DotnetDepsMetadata{ - Name: "System.Runtime.CompilerServices.Unsafe", - Version: "6.0.0", - Sha512: "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==", - Path: "system.runtime.compilerservices.unsafe/6.0.0", - HashPath: "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512", - }, + } + serilog := pkg.Package{ + Name: "Serilog", + Version: "2.10.0", + PURL: "pkg:nuget/Serilog@2.10.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "Serilog", + Version: "2.10.0", + Sha512: "sha512-+QX0hmf37a0/OZLxM3wL7V6/ADvC1XihXN4Kq/p6d8lCPfgkRdiuhbWlMaFjR9Av0dy5F0+MBeDmDdRZN/YwQA==", + Path: "serilog/2.10.0", + HashPath: "serilog.2.10.0.nupkg.sha512", + }, + } + systemDiagnosticsDiagnosticsource := pkg.Package{ + Name: "System.Diagnostics.DiagnosticSource", + Version: "6.0.0", + PURL: "pkg:nuget/System.Diagnostics.DiagnosticSource@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "System.Diagnostics.DiagnosticSource", + Version: "6.0.0", + Sha512: "sha512-frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + Path: "system.diagnostics.diagnosticsource/6.0.0", + HashPath: "system.diagnostics.diagnosticsource.6.0.0.nupkg.sha512", + }, + } + systemRuntimeCompilerServicesUnsafe := pkg.Package{ + Name: "System.Runtime.CompilerServices.Unsafe", + Version: "6.0.0", + PURL: "pkg:nuget/System.Runtime.CompilerServices.Unsafe@6.0.0", + Locations: fixtureLocationSet, + Language: pkg.Dotnet, + Type: pkg.DotnetPkg, + MetadataType: pkg.DotnetDepsMetadataType, + Metadata: pkg.DotnetDepsMetadata{ + Name: "System.Runtime.CompilerServices.Unsafe", + Version: "6.0.0", + Sha512: "sha512-/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==", + Path: "system.runtime.compilerservices.unsafe/6.0.0", + HashPath: "system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512", }, } - var expectedRelationships []artifact.Relationship - pkgtest.TestFileParser(t, fixture, parseDotnetDeps, expected, expectedRelationships) + expectedPkgs := []pkg.Package{ + awssdkcore, + msftDependencyInjection, + msftDependencyInjectionAbstractions, + msftExtensionsLogging, + msftLoggingAbstractions, + msftExtensionsOptions, + msftExtensionsPrimitives, + newtonsoftJson, + serilog, + serilogSinksConsole, + systemDiagnosticsDiagnosticsource, + systemRuntimeCompilerServicesUnsafe, + testCommon, + rootPkg, + } + + // ┌── (✓ = is represented in the test) + // ↓ + // + // ✓ TestLibrary/1.0.0 (project) + // ✓ ├── [a] Microsoft.Extensions.DependencyInjection/6.0.0 [file version: 6.0.21.52210] + // ✓ │ ├── [b] Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0 [file version: 6.0.21.52210] + // ✓ │ └── [c!] System.Runtime.CompilerServices.Unsafe/6.0.0 [NO TARGET INFO] + // ✓ ├── Microsoft.Extensions.Logging/6.0.0 [file version: 6.0.21.52210] + // ✓ │ ├── Microsoft.Extensions.DependencyInjection/6.0.0 ...to [a] + // ✓ │ ├── Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0 ...to [b] + // ✓ │ ├── Microsoft.Extensions.Logging.Abstractions/6.0.0 [file version: 6.0.21.52210] + // ✓ │ ├── Microsoft.Extensions.Options/6.0.0 [file version: 6.0.21.52210] + // ✓ │ │ ├── Microsoft.Extensions.DependencyInjection.Abstractions/6.0.0 ...to [b] + // ✓ │ │ └── Microsoft.Extensions.Primitives/6.0.0 [file version: 6.0.21.52210] + // ✓ │ │ └── System.Runtime.CompilerServices.Unsafe/6.0.0 ...to [c!] + // ✓ │ └── System.Diagnostics.DiagnosticSource/6.0.0 [NO RUNTIME INFO] + // ✓ │ └── System.Runtime.CompilerServices.Unsafe/6.0.0 ...to [c!] + // ✓ ├── Newtonsoft.Json/13.0.1 [file version: 13.0.1.25517] + // ✓ ├── [d] Serilog/2.10.0 [file version: 2.10.0.0] + // ✓ ├── Serilog.Sinks.Console/4.0.1 [file version: 4.0.1.0] + // ✓ │ └── Serilog/2.10.0 ...to [d] + // ✓ └── [e!] TestCommon/1.0.0 [NOT SERVICEABLE / NO SHA] + // ✓ └── AWSSDK.Core/3.7.10.6 [file version: 3.7.10.6] + + expectedRelationships := []artifact.Relationship{ + { + From: awssdkcore, + To: testCommon, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjection, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjection, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjectionAbstractions, + To: msftDependencyInjection, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjectionAbstractions, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftDependencyInjectionAbstractions, + To: msftExtensionsOptions, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftExtensionsLogging, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftLoggingAbstractions, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftExtensionsOptions, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: msftExtensionsPrimitives, + To: msftExtensionsOptions, + Type: artifact.DependencyOfRelationship, + }, + { + From: newtonsoftJson, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: serilog, + To: serilogSinksConsole, + Type: artifact.DependencyOfRelationship, + }, + { + From: serilog, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: serilogSinksConsole, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + { + From: systemDiagnosticsDiagnosticsource, + To: msftExtensionsLogging, + Type: artifact.DependencyOfRelationship, + }, + { + From: systemRuntimeCompilerServicesUnsafe, + To: msftDependencyInjection, + Type: artifact.DependencyOfRelationship, + }, + { + From: systemRuntimeCompilerServicesUnsafe, + To: msftExtensionsPrimitives, + Type: artifact.DependencyOfRelationship, + }, + { + From: systemRuntimeCompilerServicesUnsafe, + To: systemDiagnosticsDiagnosticsource, + Type: artifact.DependencyOfRelationship, + }, + { + From: testCommon, + To: rootPkg, + Type: artifact.DependencyOfRelationship, + }, + } + + pkgtest.TestFileParser(t, fixture, parseDotnetDeps, expectedPkgs, expectedRelationships) } diff --git a/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json b/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json index a429ec409..788eb909e 100644 --- a/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json +++ b/syft/pkg/cataloger/dotnet/test-fixtures/TestLibrary.deps.json @@ -232,4 +232,4 @@ "sha512": "" } } -} \ No newline at end of file +} diff --git a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go index 9182ce333..e5b44d459 100644 --- a/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go +++ b/syft/pkg/cataloger/internal/pkgtest/test_generic_parser.go @@ -329,6 +329,10 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi opts = append(opts, p.compareOptions...) opts = append(opts, cmp.Reporter(&r)) + // order should not matter + pkg.Sort(p.expectedPkgs) + pkg.Sort(pkgs) + if diff := cmp.Diff(p.expectedPkgs, pkgs, opts...); diff != "" { t.Log("Specific Differences:\n" + r.String()) t.Errorf("unexpected packages from parsing (-expected +actual)\n%s", diff) @@ -341,6 +345,10 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi opts = append(opts, p.compareOptions...) opts = append(opts, cmp.Reporter(&r)) + // order should not matter + pkg.SortRelationships(p.expectedRelationships) + pkg.SortRelationships(relationships) + if diff := cmp.Diff(p.expectedRelationships, relationships, opts...); diff != "" { t.Log("Specific Differences:\n" + r.String()) diff --git a/syft/pkg/relationships.go b/syft/pkg/relationships.go index 8e3628cab..204dab2d9 100644 --- a/syft/pkg/relationships.go +++ b/syft/pkg/relationships.go @@ -1,9 +1,47 @@ package pkg -import "github.com/anchore/syft/syft/artifact" +import ( + "sort" + + "github.com/anchore/syft/syft/artifact" +) func NewRelationships(catalog *Collection) []artifact.Relationship { rels := RelationshipsByFileOwnership(catalog) rels = append(rels, RelationshipsEvidentBy(catalog)...) return rels } + +// SortRelationships takes a set of package-to-package relationships and sorts them in a stable order by name and version. +// Note: this does not consider package-to-other, other-to-package, or other-to-other relationships. +// TODO: ideally this should be replaced with a more type-agnostic sort function that resides in the artifact package. +func SortRelationships(rels []artifact.Relationship) { + sort.SliceStable(rels, func(i, j int) bool { + return relationshipLess(rels[i], rels[j]) + }) +} + +func relationshipLess(i, j artifact.Relationship) bool { + iFrom, ok1 := i.From.(Package) + iTo, ok2 := i.To.(Package) + jFrom, ok3 := j.From.(Package) + jTo, ok4 := j.To.(Package) + + if !(ok1 && ok2 && ok3 && ok4) { + return false + } + + if iFrom.Name != jFrom.Name { + return iFrom.Name < jFrom.Name + } + if iFrom.Version != jFrom.Version { + return iFrom.Version < jFrom.Version + } + if iTo.Name != jTo.Name { + return iTo.Name < jTo.Name + } + if iTo.Version != jTo.Version { + return iTo.Version < jTo.Version + } + return i.Type < j.Type +} diff --git a/test/integration/catalog_packages_cases_test.go b/test/integration/catalog_packages_cases_test.go index c88b2d559..52bd6f287 100644 --- a/test/integration/catalog_packages_cases_test.go +++ b/test/integration/catalog_packages_cases_test.go @@ -247,6 +247,8 @@ var dirOnlyTestCases = []testCase{ "Serilog.Sinks.Console": "4.0.1", "System.Diagnostics.DiagnosticSource": "6.0.0", "System.Runtime.CompilerServices.Unsafe": "6.0.0", + "TestCommon": "1.0.0", + "TestLibrary": "1.0.0", }, }, { From 2687100e6aa11239516a4cb07fed85215d20c435 Mon Sep 17 00:00:00 2001 From: Colm O hEigeartaigh Date: Thu, 12 Oct 2023 16:09:53 +0100 Subject: [PATCH 104/106] Detect a license file in the root directory or META-INF of a jar (#2213) Signed-off-by: Colm O hEigeartaigh --- syft/pkg/cataloger/java/archive_parser.go | 86 +++++++++++++++---- .../pkg/cataloger/java/archive_parser_test.go | 18 ++++ 2 files changed, 87 insertions(+), 17 deletions(-) diff --git a/syft/pkg/cataloger/java/archive_parser.go b/syft/pkg/cataloger/java/archive_parser.go index 76de15348..9e4445f6c 100644 --- a/syft/pkg/cataloger/java/archive_parser.go +++ b/syft/pkg/cataloger/java/archive_parser.go @@ -8,6 +8,7 @@ import ( "strings" intFile "github.com/anchore/syft/internal/file" + "github.com/anchore/syft/internal/licenses" "github.com/anchore/syft/internal/log" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/file" @@ -177,6 +178,31 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { return nil, err } + licenses, name, version, err := j.parseLicenses(manifest) + if err != nil { + return nil, err + } + + return &pkg.Package{ + // TODO: maybe select name should just have a pom properties in it? + Name: name, + Version: version, + Language: pkg.Java, + Licenses: pkg.NewLicenseSet(licenses...), + Locations: file.NewLocationSet( + j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), + ), + Type: j.fileInfo.pkgType(), + MetadataType: pkg.JavaMetadataType, + Metadata: pkg.JavaMetadata{ + VirtualPath: j.location.AccessPath(), + Manifest: manifest, + ArchiveDigests: digests, + }, + }, nil +} + +func (j *archiveParser) parseLicenses(manifest *pkg.JavaManifest) ([]pkg.License, string, string, error) { // we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest // TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar licenses := pkg.NewLicensesFromLocation(j.location, selectLicenses(manifest)...) @@ -201,23 +227,17 @@ func (j *archiveParser) discoverMainPackage() (*pkg.Package, error) { licenses = append(licenses, pomLicenses...) } - return &pkg.Package{ - // TODO: maybe select name should just have a pom properties in it? - Name: name, - Version: version, - Language: pkg.Java, - Licenses: pkg.NewLicenseSet(licenses...), - Locations: file.NewLocationSet( - j.location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation), - ), - Type: j.fileInfo.pkgType(), - MetadataType: pkg.JavaMetadataType, - Metadata: pkg.JavaMetadata{ - VirtualPath: j.location.AccessPath(), - Manifest: manifest, - ArchiveDigests: digests, - }, - }, nil + if len(licenses) == 0 { + fileLicenses, err := j.getLicenseFromFileInArchive() + if err != nil { + return nil, "", "", err + } + if fileLicenses != nil { + licenses = append(licenses, fileLicenses...) + } + } + + return licenses, name, version, nil } type parsedPomProject struct { @@ -310,6 +330,38 @@ func getDigestsFromArchive(archivePath string) ([]file.Digest, error) { return digests, nil } +func (j *archiveParser) getLicenseFromFileInArchive() ([]pkg.License, error) { + var fileLicenses []pkg.License + for _, filename := range licenses.FileNames { + licenseMatches := j.fileManifest.GlobMatch("/META-INF/" + filename) + if len(licenseMatches) == 0 { + // Try the root directory if it's not in META-INF + licenseMatches = j.fileManifest.GlobMatch("/" + filename) + } + + if len(licenseMatches) > 0 { + contents, err := intFile.ContentsFromZip(j.archivePath, licenseMatches...) + if err != nil { + return nil, fmt.Errorf("unable to extract java license (%s): %w", j.location, err) + } + + for _, licenseMatch := range licenseMatches { + licenseContents := contents[licenseMatch] + parsed, err := licenses.Parse(strings.NewReader(licenseContents), j.location) + if err != nil { + return nil, err + } + + if len(parsed) > 0 { + fileLicenses = append(fileLicenses, parsed...) + } + } + } + } + + return fileLicenses, nil +} + func (j *archiveParser) discoverPkgsFromNestedArchives(parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) { // we know that all java archives are zip formatted files, so we can use the shared zip helper return discoverPkgsFromZip(j.location, j.archivePath, j.contentPath, j.fileManifest, parentPkg) diff --git a/syft/pkg/cataloger/java/archive_parser_test.go b/syft/pkg/cataloger/java/archive_parser_test.go index 78cbb2574..4ee463159 100644 --- a/syft/pkg/cataloger/java/archive_parser_test.go +++ b/syft/pkg/cataloger/java/archive_parser_test.go @@ -158,6 +158,15 @@ func TestParseJar(t *testing.T) { Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, + Licenses: pkg.NewLicenseSet( + pkg.License{ + Value: "Apache-2.0", + SPDXExpression: "Apache-2.0", + Type: license.Concluded, + URLs: internal.NewStringSet(), + Locations: file.NewLocationSet(file.NewLocation("test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar")), + }, + ), Metadata: pkg.JavaMetadata{ VirtualPath: "test-fixtures/java-builds/packages/example-java-app-gradle-0.1.0.jar", Manifest: &pkg.JavaManifest{ @@ -223,6 +232,15 @@ func TestParseJar(t *testing.T) { Language: pkg.Java, Type: pkg.JavaPkg, MetadataType: pkg.JavaMetadataType, + Licenses: pkg.NewLicenseSet( + pkg.License{ + Value: "Apache-2.0", + SPDXExpression: "Apache-2.0", + Type: license.Concluded, + URLs: internal.NewStringSet(), + Locations: file.NewLocationSet(file.NewLocation("test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar")), + }, + ), Metadata: pkg.JavaMetadata{ VirtualPath: "test-fixtures/java-builds/packages/example-java-app-maven-0.1.0.jar", Manifest: &pkg.JavaManifest{ From 538fe5ee1dff4094d64a328b252cf6a408b8833a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 11:10:45 -0400 Subject: [PATCH 105/106] chore(deps): bump github/codeql-action from 2.22.1 to 2.22.2 (#2224) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2.22.1 to 2.22.2. - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/github/codeql-action/compare/fdcae64e1484d349b3366718cdfef3d404390e85...d90b8d79de6dc1f58e83a1499aa58d6c93dc28de) --- updated-dependencies: - dependency-name: github/codeql-action dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/codeql-analysis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e09d6dcae..004457e1e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -45,7 +45,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@fdcae64e1484d349b3366718cdfef3d404390e85 #v2.22.1 + uses: github/codeql-action/init@d90b8d79de6dc1f58e83a1499aa58d6c93dc28de #v2.22.2 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -56,7 +56,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@fdcae64e1484d349b3366718cdfef3d404390e85 #v2.22.1 + uses: github/codeql-action/autobuild@d90b8d79de6dc1f58e83a1499aa58d6c93dc28de #v2.22.2 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -70,4 +70,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@fdcae64e1484d349b3366718cdfef3d404390e85 #v2.22.1 + uses: github/codeql-action/analyze@d90b8d79de6dc1f58e83a1499aa58d6c93dc28de #v2.22.2 From 1fe0921a5bf6fa614b8e278432a1826f17300237 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 12 Oct 2023 11:10:56 -0400 Subject: [PATCH 106/106] chore(deps): bump github.com/charmbracelet/lipgloss from 0.9.0 to 0.9.1 (#2222) Bumps [github.com/charmbracelet/lipgloss](https://github.com/charmbracelet/lipgloss) from 0.9.0 to 0.9.1. - [Release notes](https://github.com/charmbracelet/lipgloss/releases) - [Commits](https://github.com/charmbracelet/lipgloss/compare/v0.9.0...v0.9.1) --- updated-dependencies: - dependency-name: github.com/charmbracelet/lipgloss dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 0b1164ab8..182bdcde0 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 github.com/bmatcuk/doublestar/v4 v4.6.0 github.com/charmbracelet/bubbletea v0.24.2 - github.com/charmbracelet/lipgloss v0.9.0 + github.com/charmbracelet/lipgloss v0.9.1 github.com/dave/jennifer v1.7.0 github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da github.com/docker/distribution v2.8.3+incompatible // indirect diff --git a/go.sum b/go.sum index 6303313b1..90b12ccbf 100644 --- a/go.sum +++ b/go.sum @@ -156,8 +156,8 @@ github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06 github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.9.0 h1:BHIM7U4vX77xGEld8GrTKspBMtSv7j0wxPCH73nrdxE= -github.com/charmbracelet/lipgloss v0.9.0/go.mod h1:h8KDyaivONasw1Bhb4nWiKlk4P1wHPly+3+3v6EFMmA= +github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= +github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=