From d3f4764d0038e1b533360361536ac3073efe6946 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 5 Mar 2025 16:10:27 -0500 Subject: [PATCH 001/109] remove v6 development configuration (#2504) Signed-off-by: Alex Goodman --- test/quality/.grype.yaml | 5 ----- test/quality/.yardstick.yaml | 16 ++++------------ 2 files changed, 4 insertions(+), 17 deletions(-) diff --git a/test/quality/.grype.yaml b/test/quality/.grype.yaml index 964295c5c30..bdb454059e8 100644 --- a/test/quality/.grype.yaml +++ b/test/quality/.grype.yaml @@ -23,8 +23,3 @@ match: stock: using-cpes: true -# while we are merging https://github.com/anchore/grype/pull/2494 to support v6 there are a few examples that are -# worse, however, the overall matching behavior is correct, thus we should not block on these examples -ignore: - - vulnerability: CVE-2012-0979 - - vulnerability: CVE-2019-5736 diff --git a/test/quality/.yardstick.yaml b/test/quality/.yardstick.yaml index 7f00140816b..3f7c8c916d3 100644 --- a/test/quality/.yardstick.yaml +++ b/test/quality/.yardstick.yaml @@ -116,9 +116,7 @@ result-sets: # are testing with is not too stale. # version: git:current-commit+import-db=db.tar.zst # for local build of grype, use for example: -# we need to disable this while we are in pre-release of v6 (restore after release of v6) -# version: path:../../+import-db=db.tar.zst - version: path:../../ + version: path:../../+import-db=db.tar.zst takes: SBOM label: candidate @@ -128,9 +126,7 @@ result-sets: # By pinning the DB the grype code itself becomes the independent variable under test (and not the # every-changing DB). That being said, we should be updating this DB periodically to ensure what we # are testing with is not too stale. -# we need to disable this while we are in pre-release of v6 (restore after release of v6) -# version: latest+import-db=db.tar.zst - version: latest + version: latest+import-db=db.tar.zst takes: SBOM label: reference pr_vs_latest_via_sbom_2022: @@ -161,9 +157,7 @@ result-sets: # are testing with is not too stale. # version: git:current-commit+import-db=db.tar.zst # for local build of grype, use for example: -# we need to disable this while we are in pre-release of v6 (restore after release of v6) -# version: path:../../+import-db=db.tar.zst - version: path:../../ + version: path:../../+import-db=db.tar.zst takes: SBOM label: candidate # is candidate better than the current baseline? @@ -173,8 +167,6 @@ result-sets: # By pinning the DB the grype code itself becomes the independent variable under test (and not the # every-changing DB). That being said, we should be updating this DB periodically to ensure what we # are testing with is not too stale. -# we need to disable this while we are in pre-release of v6 (restore after release of v6) -# version: latest+import-db=db.tar.zst - version: latest + version: latest+import-db=db.tar.zst takes: SBOM label: reference # this run is the current baseline From a356e5ff0fd4f41490793ac815caa2d2e9ad0c8b Mon Sep 17 00:00:00 2001 From: GGMU <49076226+tomersein@users.noreply.github.com> Date: Thu, 6 Mar 2025 04:01:49 +0200 Subject: [PATCH 002/109] Add suggested fixed version when there are multiple fixes available (#2271) * add explore cve command Signed-off-by: Tomer Seinfeld * filter unrelated fixed versions Signed-off-by: tomersein * add explore cve command Signed-off-by: Tomer Seinfeld * filter unrelated packages Signed-off-by: tomersein * sort fixed versions Signed-off-by: tomersein * sort fixed versions Signed-off-by: tomersein * sort fixed versions Signed-off-by: tomersein * sort fixed versions Signed-off-by: tomersein * sort fixed versions Signed-off-by: tomersein * suggest fixed version Signed-off-by: tomersein * suggest fixed version Signed-off-by: tomersein * suggest fixed version Signed-off-by: tomersein * suggest fixed version Signed-off-by: tomersein * suggest fixed version Signed-off-by: tomersein * add suggested fix version to results Signed-off-by: tomersein * add suggested fix version to table presenter Signed-off-by: Alex Goodman --------- Signed-off-by: Tomer Seinfeld Signed-off-by: tomersein Signed-off-by: Alex Goodman Co-authored-by: Alex Goodman --- go.mod | 3 +- grype/presenter/internal/test_helpers.go | 2 +- .../snapshot/TestJsonDirsPresenter.golden | 7 +- .../snapshot/TestJsonImgsPresenter.golden | 7 +- grype/presenter/models/document_test.go | 49 +++++++ grype/presenter/models/match.go | 79 ++++++++++- grype/presenter/models/vulnerability.go | 44 +++++- .../TestSarifPresenter_directory.golden | 4 +- .../snapshot/TestSarifPresenter_image.golden | 4 +- .../table/__snapshots__/presenter_test.snap | 30 ++-- grype/presenter/table/presenter.go | 116 ++++++++++----- grype/presenter/table/presenter_test.go | 33 +++-- grype/version/version.go | 52 +++++++ grype/version/version_test.go | 133 ++++++++++++++++++ 14 files changed, 492 insertions(+), 71 deletions(-) create mode 100644 grype/version/version_test.go diff --git a/go.mod b/go.mod index c719c11515b..b998e8678a9 100644 --- a/go.mod +++ b/go.mod @@ -72,6 +72,8 @@ require ( require github.com/DataDog/zstd v1.5.5 // indirect +require github.com/muesli/termenv v0.15.2 + require ( cel.dev/expr v0.16.1 // indirect cloud.google.com/go v0.116.0 // indirect @@ -211,7 +213,6 @@ require ( github.com/moby/term v0.5.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/termenv v0.15.2 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nwaples/rardecode v1.1.3 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect diff --git a/grype/presenter/internal/test_helpers.go b/grype/presenter/internal/test_helpers.go index 6089e88977d..8c3e8fb12cc 100644 --- a/grype/presenter/internal/test_helpers.go +++ b/grype/presenter/internal/test_helpers.go @@ -111,7 +111,7 @@ func generateMatches(t *testing.T, p1, p2 pkg.Package) match.Matches { Namespace: "source-1", }, Fix: vulnerability.Fix{ - Versions: []string{"the-next-version"}, + Versions: []string{"1.2.1", "2.1.3", "3.4.0"}, State: vulnerability.FixStateFixed, }, }, diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden index aec56e5b6cd..05f412100c6 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden @@ -20,7 +20,9 @@ ], "fix": { "versions": [ - "the-next-version" + "1.2.1", + "2.1.3", + "3.4.0" ], "state": "fixed" }, @@ -39,6 +41,9 @@ }, "found": { "constraint": ">= 20" + }, + "fix": { + "suggestedVersion": "1.2.1" } } ], diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden index 6f23145e4c1..adb4ab0de50 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden @@ -20,7 +20,9 @@ ], "fix": { "versions": [ - "the-next-version" + "1.2.1", + "2.1.3", + "3.4.0" ], "state": "fixed" }, @@ -39,6 +41,9 @@ }, "found": { "constraint": ">= 20" + }, + "fix": { + "suggestedVersion": "1.2.1" } } ], diff --git a/grype/presenter/models/document_test.go b/grype/presenter/models/document_test.go index 9cd474fc493..d81f0dc144f 100644 --- a/grype/presenter/models/document_test.go +++ b/grype/presenter/models/document_test.go @@ -103,6 +103,55 @@ func TestPackagesAreSorted(t *testing.T) { assert.Equal(t, []string{"CVE-1999-0001", "CVE-1999-0003", "CVE-1999-0002"}, actualVulnerabilities) } +func TestFixSuggestedVersion(t *testing.T) { + + var pkg1 = pkg.Package{ + ID: "package-1-id", + Name: "package-1", + Version: "1.1.1", + Type: syftPkg.PythonPkg, + } + + var match1 = match.Match{ + Vulnerability: vulnerability.Vulnerability{ + Fix: vulnerability.Fix{ + Versions: []string{"1.0.0", "1.2.0", "1.1.2"}, + }, + Reference: vulnerability.Reference{ID: "CVE-1999-0003"}, + }, + Package: pkg1, + Details: match.Details{ + { + Type: match.ExactDirectMatch, + }, + }, + } + + matches := match.NewMatches() + matches.Add(match1) + + packages := []pkg.Package{pkg1} + + ctx := pkg.Context{ + Source: &syftSource.Description{ + Metadata: syftSource.DirectoryMetadata{}, + }, + Distro: &linux.Release{ + ID: "centos", + IDLike: []string{"rhel"}, + Version: "8.0", + }, + } + doc, err := NewDocument(clio.Identification{}, packages, ctx, matches, nil, NewMetadataMock(), nil, nil, SortByPackage) + if err != nil { + t.Fatalf("unable to get document: %+v", err) + } + + actualSuggestedFixedVersion := doc.Matches[0].MatchDetails[0].Fix.SuggestedVersion + + assert.Equal(t, "1.1.2", actualSuggestedFixedVersion) +} + func TestTimestampValidFormat(t *testing.T) { matches := match.NewMatches() diff --git a/grype/presenter/models/match.go b/grype/presenter/models/match.go index 07f7c6863d5..a90ba7c0d11 100644 --- a/grype/presenter/models/match.go +++ b/grype/presenter/models/match.go @@ -2,10 +2,13 @@ package models import ( "fmt" + "sort" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/pkg" + "github.com/anchore/grype/grype/version" "github.com/anchore/grype/grype/vulnerability" + "github.com/anchore/grype/internal/log" ) // Match is a single item for the JSON array reported @@ -22,6 +25,12 @@ type MatchDetails struct { Matcher string `json:"matcher"` SearchedBy interface{} `json:"searchedBy"` // The specific attributes that were used to search (other than package name and version) --this indicates "how" the match was made. Found interface{} `json:"found"` // The specific attributes on the vulnerability object that were matched with --this indicates "what" was matched on / within. + Fix *FixDetails `json:"fix,omitempty"` +} + +// FixDetails contains any data that is relevant to fixing the vulnerability specific to the package searched with +type FixDetails struct { + SuggestedVersion string `json:"suggestedVersion"` } func newMatch(m match.Match, p pkg.Package, metadataProvider vulnerability.MetadataProvider) (*Match, error) { @@ -47,6 +56,8 @@ func newMatch(m match.Match, p pkg.Package, metadataProvider vulnerability.Metad } } + format := version.FormatFromPkg(p) + details := make([]MatchDetails, len(m.Details)) for idx, d := range m.Details { details[idx] = MatchDetails{ @@ -54,13 +65,79 @@ func newMatch(m match.Match, p pkg.Package, metadataProvider vulnerability.Metad Matcher: string(d.Matcher), SearchedBy: d.SearchedBy, Found: d.Found, + Fix: getFix(m, p, format), } } return &Match{ - Vulnerability: NewVulnerability(m.Vulnerability, metadata), + Vulnerability: NewVulnerability(m.Vulnerability, metadata, format), Artifact: newPackage(p), RelatedVulnerabilities: relatedVulnerabilities, MatchDetails: details, }, nil } + +func getFix(m match.Match, p pkg.Package, format version.Format) *FixDetails { + suggested := calculateSuggestedFixedVersion(p, m.Vulnerability.Fix.Versions, format) + if suggested == "" { + return nil + } + return &FixDetails{ + SuggestedVersion: suggested, + } +} + +func calculateSuggestedFixedVersion(p pkg.Package, fixedVersions []string, format version.Format) string { + if len(fixedVersions) == 0 { + return "" + } + + if len(fixedVersions) == 1 { + return fixedVersions[0] + } + + parseConstraint := func(constStr string) (version.Constraint, error) { + constraint, err := version.GetConstraint(constStr, format) + if err != nil { + log.WithFields("package", p.Name).Trace("skipping sorting fixed versions") + } + return constraint, err + } + + checkSatisfaction := func(constraint version.Constraint, v *version.Version) bool { + satisfied, err := constraint.Satisfied(v) + if err != nil { + log.WithFields("package", p.Name).Trace("error while checking version satisfaction for sorting") + } + return satisfied && err == nil + } + + sort.SliceStable(fixedVersions, func(i, j int) bool { + v1, err1 := version.NewVersion(fixedVersions[i], format) + v2, err2 := version.NewVersion(fixedVersions[j], format) + if err1 != nil || err2 != nil { + log.WithFields("package", p.Name).Trace("error while parsing version for sorting") + return false + } + + packageConstraint, err := parseConstraint(fmt.Sprintf("<=%s", p.Version)) + if err != nil { + return false + } + + v1Satisfied := checkSatisfaction(packageConstraint, v1) + v2Satisfied := checkSatisfaction(packageConstraint, v2) + + if v1Satisfied != v2Satisfied { + return !v1Satisfied + } + + internalConstraint, err := parseConstraint(fmt.Sprintf("<=%s", v1.Raw)) + if err != nil { + return false + } + return !checkSatisfaction(internalConstraint, v2) + }) + + return fixedVersions[0] +} diff --git a/grype/presenter/models/vulnerability.go b/grype/presenter/models/vulnerability.go index c2745ce19cd..391a9f636de 100644 --- a/grype/presenter/models/vulnerability.go +++ b/grype/presenter/models/vulnerability.go @@ -1,7 +1,11 @@ package models import ( + "sort" + + "github.com/anchore/grype/grype/version" "github.com/anchore/grype/grype/vulnerability" + "github.com/anchore/grype/internal/log" ) type Vulnerability struct { @@ -20,7 +24,7 @@ type Advisory struct { Link string `json:"link"` } -func NewVulnerability(vuln vulnerability.Vulnerability, metadata *vulnerability.Metadata) Vulnerability { +func NewVulnerability(vuln vulnerability.Vulnerability, metadata *vulnerability.Metadata, versionFormat version.Format) Vulnerability { if metadata == nil { return Vulnerability{ VulnerabilityMetadata: NewVulnerabilityMetadata(vuln.ID, vuln.Namespace, metadata), @@ -44,9 +48,45 @@ func NewVulnerability(vuln vulnerability.Vulnerability, metadata *vulnerability. return Vulnerability{ VulnerabilityMetadata: NewVulnerabilityMetadata(vuln.ID, vuln.Namespace, metadata), Fix: Fix{ - Versions: fixedInVersions, + Versions: sortVersions(fixedInVersions, versionFormat), State: string(vuln.Fix.State), }, Advisories: advisories, } } +func sortVersions(fixedVersions []string, format version.Format) []string { + if len(fixedVersions) <= 1 { + return fixedVersions + } + + // First, create Version objects from strings (only once) + versionObjs := make([]*version.Version, 0, len(fixedVersions)) + for _, vStr := range fixedVersions { + v, err := version.NewVersion(vStr, format) + if err != nil { + log.WithFields("version", vStr, "error", err).Trace("error parsing version, skipping") + continue + } + versionObjs = append(versionObjs, v) + } + + // Sort the Version objects + sort.Slice(versionObjs, func(i, j int) bool { + // Compare returns -1 if v[i] < v[j], so we negate for descending order + // (higher versions first) + comparison, err := versionObjs[i].Compare(versionObjs[j]) + if err != nil { + log.WithFields("error", err).Trace("error comparing versions") + return false + } + return comparison > 0 // Descending order + }) + + // Convert back to strings + result := make([]string, len(versionObjs)) + for i, v := range versionObjs { + result[i] = v.Raw + } + + return result +} diff --git a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden index 12fb485eb72..9edb968a3a0 100644 --- a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden +++ b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden @@ -20,8 +20,8 @@ }, "helpUri": "https://github.com/anchore/grype", "help": { - "text": "Vulnerability CVE-1999-0001\nSeverity: low\nPackage: package-1\nVersion: 1.1.1\nFix Version: the-next-version\nType: rpm\nLocation: /some/path/somefile-1.txt\nData Namespace: source-1\nLink: CVE-1999-0001", - "markdown": "**Vulnerability CVE-1999-0001**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| low | package-1 | 1.1.1 | the-next-version | rpm | /some/path/somefile-1.txt | source-1 | CVE-1999-0001 |\n" + "text": "Vulnerability CVE-1999-0001\nSeverity: low\nPackage: package-1\nVersion: 1.1.1\nFix Version: 1.2.1,2.1.3,3.4.0\nType: rpm\nLocation: /some/path/somefile-1.txt\nData Namespace: source-1\nLink: CVE-1999-0001", + "markdown": "**Vulnerability CVE-1999-0001**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| low | package-1 | 1.1.1 | 1.2.1,2.1.3,3.4.0 | rpm | /some/path/somefile-1.txt | source-1 | CVE-1999-0001 |\n" }, "properties": { "security-severity": "4.0" diff --git a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden index 96c5b5432fb..027cd809648 100644 --- a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden +++ b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden @@ -20,8 +20,8 @@ }, "helpUri": "https://github.com/anchore/grype", "help": { - "text": "Vulnerability CVE-1999-0001\nSeverity: low\nPackage: package-1\nVersion: 1.1.1\nFix Version: the-next-version\nType: rpm\nLocation: somefile-1.txt\nData Namespace: source-1\nLink: CVE-1999-0001", - "markdown": "**Vulnerability CVE-1999-0001**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| low | package-1 | 1.1.1 | the-next-version | rpm | somefile-1.txt | source-1 | CVE-1999-0001 |\n" + "text": "Vulnerability CVE-1999-0001\nSeverity: low\nPackage: package-1\nVersion: 1.1.1\nFix Version: 1.2.1,2.1.3,3.4.0\nType: rpm\nLocation: somefile-1.txt\nData Namespace: source-1\nLink: CVE-1999-0001", + "markdown": "**Vulnerability CVE-1999-0001**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| low | package-1 | 1.1.1 | 1.2.1,2.1.3,3.4.0 | rpm | somefile-1.txt | source-1 | CVE-1999-0001 |\n" }, "properties": { "security-severity": "4.0" diff --git a/grype/presenter/table/__snapshots__/presenter_test.snap b/grype/presenter/table/__snapshots__/presenter_test.snap index f812157b9c4..73149c75fee 100755 --- a/grype/presenter/table/__snapshots__/presenter_test.snap +++ b/grype/presenter/table/__snapshots__/presenter_test.snap @@ -1,15 +1,15 @@ [TestTablePresenter/no_color - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 the-next-version rpm CVE-1999-0001 Low -package-2 2.2.2 deb CVE-1999-0002 Critical +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low +package-2 2.2.2 deb CVE-1999-0002 Critical --- [TestTablePresenter/with_color - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 the-next-version rpm CVE-1999-0001 Low -package-2 2.2.2 deb CVE-1999-0002 Critical +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY +package-1 1.1.1 1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low +package-2 2.2.2 deb CVE-1999-0002 Critical --- @@ -19,18 +19,18 @@ No vulnerabilities found --- [TestHidesIgnoredMatches - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 the-next-version rpm CVE-1999-0001 Low -package-2 2.2.2 deb CVE-1999-0002 Critical +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low +package-2 2.2.2 deb CVE-1999-0002 Critical --- [TestDisplaysIgnoredMatches - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 the-next-version rpm CVE-1999-0001 Low -package-2 2.2.2 deb CVE-1999-0002 Critical -package-2 2.2.2 deb CVE-1999-0001 Low (suppressed) -package-2 2.2.2 deb CVE-1999-0002 Critical (suppressed) -package-2 2.2.2 deb CVE-1999-0004 Critical (suppressed by VEX) +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low +package-2 2.2.2 deb CVE-1999-0002 Critical +package-2 2.2.2 deb CVE-1999-0001 Low (suppressed) +package-2 2.2.2 deb CVE-1999-0002 Critical (suppressed) +package-2 2.2.2 deb CVE-1999-0004 Critical (suppressed by VEX) --- diff --git a/grype/presenter/table/presenter.go b/grype/presenter/table/presenter.go index c981be808db..3e0671490e1 100644 --- a/grype/presenter/table/presenter.go +++ b/grype/presenter/table/presenter.go @@ -6,6 +6,7 @@ import ( "github.com/charmbracelet/lipgloss" "github.com/olekukonko/tablewriter" + "github.com/scylladb/go-set/strset" "github.com/anchore/grype/grype/presenter/models" "github.com/anchore/grype/grype/vulnerability" @@ -21,20 +22,39 @@ type Presenter struct { document models.Document showSuppressed bool withColor bool + + recommendedFixStyle lipgloss.Style +} + +type rows []row + +type row struct { + Name string + Version string + Fix string + PackageType string + VulnerabilityID string + Severity string } // NewPresenter is a *Presenter constructor func NewPresenter(pb models.PresenterConfig, showSuppressed bool) *Presenter { + withColor := supportsColor() + fixStyle := lipgloss.NewStyle().Border(lipgloss.Border{Left: "*"}, false, false, false, true) + if withColor { + fixStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("6")).Bold(true).Underline(true) + } return &Presenter{ - document: pb.Document, - showSuppressed: showSuppressed, - withColor: supportsColor(), + document: pb.Document, + showSuppressed: showSuppressed, + withColor: withColor, + recommendedFixStyle: fixStyle, } } // Present creates a JSON-based reporting func (p *Presenter) Present(output io.Writer) error { - rs := getRows(p.document, p.showSuppressed) + rs := p.getRows(p.document, p.showSuppressed) if len(rs) == 0 { _, err := io.WriteString(output, "No vulnerabilities found\n") @@ -57,9 +77,16 @@ func (p *Presenter) Present(output io.Writer) error { table.SetNoWhiteSpace(true) if p.withColor { - for _, row := range rs.Render() { - severityColor := getSeverityColor(row[len(row)-1]) - table.Rich(row, []tablewriter.Colors{{}, {}, {}, {}, {}, severityColor}) + for _, row := range rs.Deduplicate() { + severityColor := getSeverityColor(row.Severity) + table.Rich(row.Columns(), []tablewriter.Colors{ + {}, // name + {}, // version + {}, // fix + {}, // package type + {}, // vulnerability ID + severityColor, // severity + }) } } else { table.AppendBulk(rs.Render()) @@ -70,12 +97,12 @@ func (p *Presenter) Present(output io.Writer) error { return nil } -func getRows(doc models.Document, showSuppressed bool) rows { +func (p *Presenter) getRows(doc models.Document, showSuppressed bool) rows { var rs rows // generate rows for matching vulnerabilities for _, m := range doc.Matches { - rs = append(rs, newRow(m, "")) + rs = append(rs, p.newRow(m, "")) } // generate rows for suppressed vulnerabilities @@ -89,7 +116,7 @@ func getRows(doc models.Document, showSuppressed bool) rows { } } } - rs = append(rs, newRow(m.Match, msg)) + rs = append(rs, p.newRow(m.Match, msg)) } } return rs @@ -99,41 +126,53 @@ func supportsColor() bool { return lipgloss.NewStyle().Foreground(lipgloss.Color("5")).Render("") != "" } -type rows []row - -type row struct { - Name string - Version string - Fix string - PackageType string - VulnerabilityID string - Severity string -} - -func newRow(m models.Match, severitySuffix string) row { +func (p *Presenter) newRow(m models.Match, severitySuffix string) row { severity := m.Vulnerability.Severity if severity != "" { severity += severitySuffix } - fixVersion := strings.Join(m.Vulnerability.Fix.Versions, ", ") - switch m.Vulnerability.Fix.State { - case vulnerability.FixStateWontFix.String(): - fixVersion = "(won't fix)" - case vulnerability.FixStateUnknown.String(): - fixVersion = "" - } - return row{ Name: m.Artifact.Name, Version: m.Artifact.Version, - Fix: fixVersion, + Fix: p.formatFix(m), PackageType: string(m.Artifact.Type), VulnerabilityID: m.Vulnerability.ID, Severity: severity, } } +func (p *Presenter) formatFix(m models.Match) string { + switch m.Vulnerability.Fix.State { + case vulnerability.FixStateWontFix.String(): + return "(won't fix)" + case vulnerability.FixStateUnknown.String(): + return "" + } + + recommended := strset.New() + for _, d := range m.MatchDetails { + if d.Fix == nil { + continue + } + if d.Fix.SuggestedVersion != "" { + recommended.Add(d.Fix.SuggestedVersion) + } + } + + var vers []string + hasMultipleVersions := len(m.Vulnerability.Fix.Versions) > 1 + for _, v := range m.Vulnerability.Fix.Versions { + if hasMultipleVersions && recommended.Has(v) { + vers = append(vers, p.recommendedFixStyle.Render(v)) + continue + } + vers = append(vers, v) + } + + return strings.Join(vers, ", ") +} + func (r row) Columns() []string { return []string{r.Name, r.Version, r.Fix, r.PackageType, r.VulnerabilityID, r.Severity} } @@ -143,6 +182,15 @@ func (r row) String() string { } func (rs rows) Render() [][]string { + deduped := rs.Deduplicate() + out := make([][]string, len(deduped)) + for idx, r := range deduped { + out[idx] = r.Columns() + } + return out +} + +func (rs rows) Deduplicate() []row { // deduplicate seen := map[string]row{} var deduped rows @@ -159,11 +207,7 @@ func (rs rows) Render() [][]string { } // render final columns - out := make([][]string, len(deduped)) - for idx, r := range deduped { - out[idx] = r.Columns() - } - return out + return deduped } func getSeverityColor(severity string) tablewriter.Colors { diff --git a/grype/presenter/table/presenter_test.go b/grype/presenter/table/presenter_test.go index 46743cbd245..bdd93f97bde 100644 --- a/grype/presenter/table/presenter_test.go +++ b/grype/presenter/table/presenter_test.go @@ -4,8 +4,10 @@ import ( "bytes" "testing" + "github.com/charmbracelet/lipgloss" "github.com/gkampitakis/go-snaps/snaps" "github.com/google/go-cmp/cmp" + "github.com/muesli/termenv" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -22,11 +24,15 @@ func TestCreateRow(t *testing.T) { pkg1 := models.Package{ ID: "package-1-id", Name: "package-1", - Version: "1.0.1", + Version: "2.0.0", Type: syftPkg.DebPkg, } match1 := models.Match{ Vulnerability: models.Vulnerability{ + Fix: models.Fix{ + Versions: []string{"1.0.2", "2.0.1", "3.0.4"}, + State: vulnerability.FixStateFixed.String(), + }, VulnerabilityMetadata: models.VulnerabilityMetadata{ ID: "CVE-1999-0001", Namespace: "source-1", @@ -48,6 +54,9 @@ func TestCreateRow(t *testing.T) { { Type: match.ExactDirectMatch.String(), Matcher: match.DpkgMatcher.String(), + Fix: &models.FixDetails{ + SuggestedVersion: "2.0.1", + }, }, }, } @@ -61,19 +70,20 @@ func TestCreateRow(t *testing.T) { name: "create row for vulnerability", match: match1, severitySuffix: "", - expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "", string(match1.Artifact.Type), match1.Vulnerability.ID, "Low"}, + expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "1.0.2, *2.0.1, 3.0.4", string(match1.Artifact.Type), match1.Vulnerability.ID, "Low"}, }, { name: "create row for suppressed vulnerability", match: match1, severitySuffix: appendSuppressed, - expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "", string(match1.Artifact.Type), match1.Vulnerability.ID, "Low (suppressed)"}, + expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "1.0.2, *2.0.1, 3.0.4", string(match1.Artifact.Type), match1.Vulnerability.ID, "Low (suppressed)"}, }, } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { - row := newRow(testCase.match, testCase.severitySuffix) + p := NewPresenter(models.PresenterConfig{}, false) + row := p.newRow(testCase.match, testCase.severitySuffix) cols := rows{row}.Render()[0] assert.Equal(t, testCase.expectedRow, cols) @@ -83,11 +93,11 @@ func TestCreateRow(t *testing.T) { func TestTablePresenter(t *testing.T) { pb := internal.GeneratePresenterConfig(t, internal.ImageSource) - pres := NewPresenter(pb, false) t.Run("no color", func(t *testing.T) { var buffer bytes.Buffer - pres.withColor = false + lipgloss.SetColorProfile(termenv.Ascii) + pres := NewPresenter(pb, false) err := pres.Present(&buffer) require.NoError(t, err) @@ -98,7 +108,12 @@ func TestTablePresenter(t *testing.T) { t.Run("with color", func(t *testing.T) { var buffer bytes.Buffer - pres.withColor = true + lipgloss.SetColorProfile(termenv.TrueColor) + t.Cleanup(func() { + // don't affect other tests + lipgloss.SetColorProfile(termenv.Ascii) + }) + pres := NewPresenter(pb, false) err := pres.Present(&buffer) require.NoError(t, err) @@ -226,7 +241,6 @@ func TestRowsRender(t *testing.T) { }) } -// Helper function to create a test row func createTestRow(name, version, fix, pkgType, vulnID, severity string, fixState vulnerability.FixState) (row, error) { m := models.Match{ Vulnerability: models.Vulnerability{ @@ -246,7 +260,8 @@ func createTestRow(name, version, fix, pkgType, vulnID, severity string, fixStat }, } - r := newRow(m, "") + p := NewPresenter(models.PresenterConfig{}, false) + r := p.newRow(m, "") return r, nil } diff --git a/grype/version/version.go b/grype/version/version.go index 6173ccf678d..b8195940454 100644 --- a/grype/version/version.go +++ b/grype/version/version.go @@ -118,3 +118,55 @@ func (v Version) CPEs() []cpe.CPE { func (v Version) String() string { return fmt.Sprintf("%s (%s)", v.Raw, v.Format) } + +func (v Version) Compare(other *Version) (int, error) { + if other == nil { + return -1, ErrNoVersionProvided + } + + if other.Format == v.Format { + return v.compareSameFormat(other) + } + + // different formats, try to convert to a common format + common, err := finalizeComparisonVersion(other, v.Format) + if err != nil { + return -1, err + } + + return v.compareSameFormat(common) +} + +func (v Version) compareSameFormat(other *Version) (int, error) { + switch v.Format { + case SemanticFormat: + return v.rich.semVer.verObj.Compare(other.rich.semVer.verObj), nil + case ApkFormat: + return v.rich.apkVer.Compare(other) + case DebFormat: + return v.rich.debVer.Compare(other) + case GolangFormat: + return v.rich.golangVersion.Compare(other) + case MavenFormat: + return v.rich.mavenVer.Compare(other) + case RpmFormat: + return v.rich.rpmVer.Compare(other) + case PythonFormat: + return v.rich.pep440version.Compare(other) + case KBFormat: + return v.rich.kbVer.Compare(other) + case GemFormat: + return v.rich.semVer.verObj.Compare(other.rich.semVer.verObj), nil + case PortageFormat: + return v.rich.portVer.Compare(other) + case JVMFormat: + return v.rich.jvmVersion.Compare(other) + } + + v1, err := newFuzzyVersion(v.Raw) + if err != nil { + return -1, fmt.Errorf("unable to parse version (%s) as a fuzzy version: %w", v.Raw, err) + } + + return v1.Compare(other) +} diff --git a/grype/version/version_test.go b/grype/version/version_test.go new file mode 100644 index 00000000000..fb59d2cbf79 --- /dev/null +++ b/grype/version/version_test.go @@ -0,0 +1,133 @@ +package version + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestVersionCompare(t *testing.T) { + tests := []struct { + name string + version1 string + version2 string + format Format + expectedResult int + expectErr bool + }{ + { + name: "v1 greater than v2", + version1: "2.0.0", + version2: "1.0.0", + format: SemanticFormat, + expectedResult: 1, + expectErr: false, + }, + { + name: "v1 less than v2", + version1: "1.0.0", + version2: "2.0.0", + format: SemanticFormat, + expectedResult: -1, + expectErr: false, + }, + { + name: "v1 equal to v2", + version1: "1.0.0", + version2: "1.0.0", + format: SemanticFormat, + expectedResult: 0, + expectErr: false, + }, + { + name: "compare with nil version", + version1: "1.0.0", + version2: "", + format: SemanticFormat, + expectedResult: -1, + expectErr: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + v1, err := NewVersion(tc.version1, tc.format) + require.NoError(t, err, "failed to create version1") + + var v2 *Version + if tc.version2 == "" { + v2 = nil // test nil case + } else if tc.name == "different formats" { + // use a different format for the second version + v2, err = NewVersion(tc.version2, PythonFormat) + require.NoError(t, err, "failed to create version2 with different format") + } else { + v2, err = NewVersion(tc.version2, tc.format) + require.NoError(t, err, "failed to create version2") + } + + result, err := v1.Compare(v2) + + if tc.expectErr { + assert.Error(t, err, "expected an error but got none") + } else { + assert.NoError(t, err, "unexpected error during comparison") + assert.Equal(t, tc.expectedResult, result, "comparison result mismatch") + } + }) + } +} + +func Test_UpgradeUnknownRightSideComparison(t *testing.T) { + v1, err := NewVersion("1.0.0", SemanticFormat) + require.NoError(t, err) + + // test if we can upgrade an unknown format to a known format when the left hand side is known + v2, err := NewVersion("1.0.0", UnknownFormat) + require.NoError(t, err) + + result, err := v1.Compare(v2) + assert.NoError(t, err) + assert.Equal(t, 0, result, "versions should be equal after format conversion") +} + +func TestVersionCompareSameFormat(t *testing.T) { + formats := []struct { + name string + format Format + }{ + {"Semantic", SemanticFormat}, + {"APK", ApkFormat}, + {"Deb", DebFormat}, + {"Golang", GolangFormat}, + {"Maven", MavenFormat}, + {"RPM", RpmFormat}, + {"Python", PythonFormat}, + {"KB", KBFormat}, + {"Gem", GemFormat}, + {"Portage", PortageFormat}, + {"JVM", JVMFormat}, + {"Unknown", UnknownFormat}, + } + + for _, fmt := range formats { + t.Run(fmt.name, func(t *testing.T) { + // just test that we can create and compare versions of this format + // without errors - not testing the actual comparison logic + v1, err := NewVersion("1.0.0", fmt.format) + if err != nil { + t.Skipf("Skipping %s format, couldn't create version: %v", fmt.name, err) + } + + v2, err := NewVersion("1.0.0", fmt.format) + if err != nil { + t.Skipf("Skipping %s format, couldn't create second version: %v", fmt.name, err) + } + + result, err := v1.Compare(v2) + assert.NoError(t, err, "comparison error") + assert.Equal(t, 0, result, "equal versions should return 0") + }) + } +} From c086bce72957aee8407f74a9bb4351699b82721b 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, 6 Mar 2025 08:21:06 -0500 Subject: [PATCH 003/109] chore(deps): update tools to latest versions (#2506) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.binny.yaml b/.binny.yaml index cd894dd4eb5..ec3fbb050a8 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -98,7 +98,7 @@ tools: # used for triggering a release - name: gh version: - want: v2.67.0 + want: v2.68.0 method: github-release with: repo: cli/cli From ca690281e6833f8727b03a6e3f7363f0e2ff66ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 08:21:29 -0500 Subject: [PATCH 004/109] chore(deps): bump golang.org/x/time from 0.10.0 to 0.11.0 (#2503) Bumps [golang.org/x/time](https://github.com/golang/time) from 0.10.0 to 0.11.0. - [Commits](https://github.com/golang/time/compare/v0.10.0...v0.11.0) --- updated-dependencies: - dependency-name: golang.org/x/time 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 b998e8678a9..bebb344de12 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 - golang.org/x/time v0.10.0 + golang.org/x/time v0.11.0 golang.org/x/tools v0.30.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/gorm v1.25.12 diff --git a/go.sum b/go.sum index 13771c0a04d..d52b5e4b907 100644 --- a/go.sum +++ b/go.sum @@ -1991,8 +1991,8 @@ golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= -golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= From d5f13ab8d3f8343400889c54feb3416ae154c530 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 6 Mar 2025 10:08:40 -0500 Subject: [PATCH 005/109] fix regression to allow for reading listing from local FS (#2508) Signed-off-by: Alex Goodman --- grype/db/v6/distribution/client.go | 38 +++++---- grype/db/v6/distribution/client_test.go | 100 ++++++++++++------------ grype/db/v6/distribution/latest.go | 9 +++ 3 files changed, 80 insertions(+), 67 deletions(-) diff --git a/grype/db/v6/distribution/client.go b/grype/db/v6/distribution/client.go index 1c9ba8e8d43..171f575880e 100644 --- a/grype/db/v6/distribution/client.go +++ b/grype/db/v6/distribution/client.go @@ -44,10 +44,10 @@ type Client interface { } type client struct { - fs afero.Fs - latestHTTPClient *http.Client - updateDownloader file.Getter - config Config + fs afero.Fs + dbDownloader file.Getter + listingDownloader file.Getter + config Config } func DefaultConfig() Config { @@ -72,10 +72,10 @@ func NewClient(cfg Config) (Client, error) { } return client{ - fs: fs, - latestHTTPClient: latestClient, - updateDownloader: file.NewGetter(cfg.ID, dbClient), - config: cfg, + fs: fs, + listingDownloader: file.NewGetter(cfg.ID, latestClient), + dbDownloader: file.NewGetter(cfg.ID, dbClient), + config: cfg, }, nil } @@ -157,7 +157,7 @@ func (c client) Download(archive Archive, dest string, downloadProgress *progres u.RawQuery = query.Encode() // go-getter will automatically extract all files within the archive to the temp dir - err = c.updateDownloader.GetToDir(tempDir, u.String(), downloadProgress) + err = c.dbDownloader.GetToDir(tempDir, u.String(), downloadProgress) if err != nil { removeAllOrLog(afero.NewOsFs(), tempDir) return "", fmt.Errorf("unable to download db: %w", err) @@ -168,17 +168,23 @@ func (c client) Download(archive Archive, dest string, downloadProgress *progres // Latest loads a LatestDocument from the configured URL. func (c client) Latest() (*LatestDocument, error) { - resp, err := c.latestHTTPClient.Get(c.latestURL()) + tempFile, err := afero.TempFile(c.fs, "", "grype-db-listing") if err != nil { - return nil, fmt.Errorf("unable to fetch latest.json: %w", err) - } - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("unable to fetch latest.json: %s", resp.Status) + return nil, fmt.Errorf("unable to create listing temp file: %w", err) } + defer func() { + err := c.fs.RemoveAll(tempFile.Name()) + if err != nil { + log.WithFields("error", err, "file", tempFile.Name()).Errorf("failed to remove file") + } + }() - defer func() { _ = resp.Body.Close() }() + err = c.listingDownloader.GetFile(tempFile.Name(), c.latestURL()) + if err != nil { + return nil, fmt.Errorf("unable to download listing: %w", err) + } - return NewLatestFromReader(resp.Body) + return NewLatestFromFile(c.fs, tempFile.Name()) } func (c client) latestURL() string { diff --git a/grype/db/v6/distribution/client_test.go b/grype/db/v6/distribution/client_test.go index 9dac1c2c1d8..183302e0d96 100644 --- a/grype/db/v6/distribution/client_test.go +++ b/grype/db/v6/distribution/client_test.go @@ -3,12 +3,11 @@ package distribution import ( "encoding/json" "errors" - "net/http" - "net/http/httptest" "path/filepath" "testing" "time" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -18,16 +17,31 @@ import ( "github.com/anchore/grype/internal/schemaver" ) -func TestClient_LatestFromURL(t *testing.T) { +type mockGetter struct { + mock.Mock +} + +func (m *mockGetter) GetFile(dst, src string, manuals ...*progress.Manual) error { + args := m.Called(dst, src, manuals) + return args.Error(0) +} + +func (m *mockGetter) GetToDir(dst, src string, manuals ...*progress.Manual) error { + args := m.Called(dst, src, manuals) + return args.Error(0) +} + +func TestClient_Latest(t *testing.T) { tests := []struct { - name string - setupServer func() *httptest.Server - expectedDoc *LatestDocument - expectedErr require.ErrorAssertionFunc + name string + latestResponse []byte + getFileErr error + expectedDoc *LatestDocument + expectedErr require.ErrorAssertionFunc }{ { name: "go case", - setupServer: func() *httptest.Server { + latestResponse: func() []byte { doc := LatestDocument{ Status: "active", Archive: Archive{ @@ -39,15 +53,10 @@ func TestClient_LatestFromURL(t *testing.T) { Checksum: "checksum123", }, } - data, _ := json.Marshal(doc) - - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - w.Header().Set("Content-Type", "application/json") - _, err := w.Write(data) - require.NoError(t, err) - })) - }, + data, err := json.Marshal(doc) + require.NoError(t, err) + return data + }(), expectedDoc: &LatestDocument{ Status: "active", Archive: Archive{ @@ -61,28 +70,18 @@ func TestClient_LatestFromURL(t *testing.T) { }, }, { - name: "error response", - setupServer: func() *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusInternalServerError) - })) - }, + name: "download error", + getFileErr: errors.New("failed to download file"), expectedDoc: nil, expectedErr: func(t require.TestingT, err error, _ ...interface{}) { require.Error(t, err) - require.Contains(t, err.Error(), "500 Internal Server Error") + require.Contains(t, err.Error(), "unable to download listing") }, }, { - name: "malformed JSON response", - setupServer: func() *httptest.Server { - return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusOK) - _, err := w.Write([]byte("malformed json")) - require.NoError(t, err) - })) - }, - expectedDoc: nil, + name: "malformed JSON response", + latestResponse: []byte("malformed json"), + expectedDoc: nil, expectedErr: func(t require.TestingT, err error, _ ...interface{}) { require.Error(t, err) require.Contains(t, err.Error(), "invalid character 'm' looking for beginning of value") @@ -95,16 +94,28 @@ func TestClient_LatestFromURL(t *testing.T) { if tt.expectedErr == nil { tt.expectedErr = require.NoError } + mockFs := afero.NewMemMapFs() - server := tt.setupServer() - defer server.Close() + mg := new(mockGetter) + + mg.On("GetFile", mock.Anything, "http://localhost:8080/latest.json", mock.Anything).Run(func(args mock.Arguments) { + if tt.getFileErr != nil { + return + } + + dst := args.String(0) + err := afero.WriteFile(mockFs, dst, tt.latestResponse, 0644) + require.NoError(t, err) + }).Return(tt.getFileErr) c, err := NewClient(Config{ - LatestURL: server.URL, + LatestURL: "http://localhost:8080/latest.json", }) require.NoError(t, err) cl := c.(client) + cl.fs = mockFs + cl.listingDownloader = mg doc, err := cl.Latest() tt.expectedErr(t, err) @@ -113,24 +124,11 @@ func TestClient_LatestFromURL(t *testing.T) { } require.Equal(t, tt.expectedDoc, doc) + mg.AssertExpectations(t) }) } } -type mockGetter struct { - mock.Mock -} - -func (m *mockGetter) GetFile(dst, src string, manuals ...*progress.Manual) error { - args := m.Called(dst, src, manuals) - return args.Error(0) -} - -func (m *mockGetter) GetToDir(dst, src string, manuals ...*progress.Manual) error { - args := m.Called(dst, src, manuals) - return args.Error(0) -} - func TestClient_Download(t *testing.T) { destDir := t.TempDir() archive := &Archive{ @@ -147,7 +145,7 @@ func TestClient_Download(t *testing.T) { require.NoError(t, err) cl := c.(client) - cl.updateDownloader = mg + cl.dbDownloader = mg return cl, mg } diff --git a/grype/db/v6/distribution/latest.go b/grype/db/v6/distribution/latest.go index b0a12e77ec7..176ad3e40bd 100644 --- a/grype/db/v6/distribution/latest.go +++ b/grype/db/v6/distribution/latest.go @@ -74,6 +74,15 @@ func NewLatestFromReader(reader io.Reader) (*LatestDocument, error) { return &l, nil } +func NewLatestFromFile(fs afero.Fs, path string) (*LatestDocument, error) { + fh, err := fs.Open(path) + if err != nil { + return nil, fmt.Errorf("failed to read listing file: %w", err) + } + defer fh.Close() + return NewLatestFromReader(fh) +} + func NewArchive(path string, t time.Time, model, revision, addition int) (*Archive, error) { checksum, err := calculateArchiveDigest(path) if err != nil { From eae38766759acaf314668768574456bc4cb77062 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 11:12:22 -0500 Subject: [PATCH 006/109] chore(deps): bump golang.org/x/tools from 0.30.0 to 0.31.0 (#2510) Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.30.0 to 0.31.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.30.0...v0.31.0) --- updated-dependencies: - dependency-name: golang.org/x/tools 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 | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/go.mod b/go.mod index bebb344de12..ad2ecfec419 100644 --- a/go.mod +++ b/go.mod @@ -65,7 +65,7 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 golang.org/x/time v0.11.0 - golang.org/x/tools v0.30.0 + golang.org/x/tools v0.31.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/gorm v1.25.12 ) @@ -275,14 +275,14 @@ require ( go.opentelemetry.io/otel/trace v1.33.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect - golang.org/x/crypto v0.33.0 // indirect - golang.org/x/mod v0.23.0 // indirect - golang.org/x/net v0.35.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.37.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/term v0.29.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/term v0.30.0 // indirect + golang.org/x/text v0.23.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/api v0.215.0 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect diff --git a/go.sum b/go.sum index d52b5e4b907..529d862a803 100644 --- a/go.sum +++ b/go.sum @@ -1649,8 +1649,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1713,8 +1713,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= 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= @@ -1784,8 +1784,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 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= @@ -1838,8 +1838,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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= @@ -1943,8 +1943,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 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= @@ -1960,8 +1960,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= 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= @@ -1983,8 +1983,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= 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= @@ -2059,8 +2059,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= 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 1bf47c38bede40dea7b72bbe4712191820f1aa15 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 11:12:29 -0500 Subject: [PATCH 007/109] chore(deps): bump github.com/muesli/termenv from 0.15.2 to 0.16.0 (#2509) Bumps [github.com/muesli/termenv](https://github.com/muesli/termenv) from 0.15.2 to 0.16.0. - [Release notes](https://github.com/muesli/termenv/releases) - [Commits](https://github.com/muesli/termenv/compare/v0.15.2...v0.16.0) --- updated-dependencies: - dependency-name: github.com/muesli/termenv 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 ad2ecfec419..d39f72ba4f9 100644 --- a/go.mod +++ b/go.mod @@ -72,7 +72,7 @@ require ( require github.com/DataDog/zstd v1.5.5 // indirect -require github.com/muesli/termenv v0.15.2 +require github.com/muesli/termenv v0.16.0 require ( cel.dev/expr v0.16.1 // indirect diff --git a/go.sum b/go.sum index 529d862a803..d69a3797c22 100644 --- a/go.sum +++ b/go.sum @@ -1337,8 +1337,8 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= -github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= -github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc= +github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= From b9acbc2ff616b45eba7bb65231380bb0ce17434f Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Fri, 7 Mar 2025 14:44:43 -0500 Subject: [PATCH 008/109] fix: exclude self from related vulnerability list (#2515) Signed-off-by: Keith Zantow --- grype/db/v6/vulnerability.go | 4 +- grype/db/v6/vulnerability_test.go | 65 +++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/grype/db/v6/vulnerability.go b/grype/db/v6/vulnerability.go index 2da7700fc1e..336d7bb2222 100644 --- a/grype/db/v6/vulnerability.go +++ b/grype/db/v6/vulnerability.go @@ -104,7 +104,7 @@ func getRelatedVulnerabilities(vuln *VulnerabilityHandle, affected *AffectedPack cveSet := strset.New() var relatedVulnerabilities []vulnerability.Reference for _, alias := range vuln.BlobValue.Aliases { - if cveSet.Has(alias) { + if cveSet.Has(alias) || strings.EqualFold(vuln.Name, alias) { continue } if !strings.HasPrefix(strings.ToLower(alias), "cve-") { @@ -118,7 +118,7 @@ func getRelatedVulnerabilities(vuln *VulnerabilityHandle, affected *AffectedPack } if affected != nil { for _, cve := range affected.CVEs { - if cveSet.Has(cve) { + if cveSet.Has(cve) || strings.EqualFold(vuln.Name, cve) { continue } if !strings.HasPrefix(strings.ToLower(cve), "cve-") { diff --git a/grype/db/v6/vulnerability_test.go b/grype/db/v6/vulnerability_test.go index 934ab2f7ed8..2feff56df19 100644 --- a/grype/db/v6/vulnerability_test.go +++ b/grype/db/v6/vulnerability_test.go @@ -6,6 +6,9 @@ import ( "unicode" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/grype/grype/vulnerability" ) func TestV5Namespace(t *testing.T) { @@ -468,6 +471,68 @@ func TestV5Namespace(t *testing.T) { } } +func Test_getRelatedVulnerabilities(t *testing.T) { + tests := []struct { + name string + vuln VulnerabilityHandle + affected AffectedPackageBlob + expected []string + }{ + { + name: "GHSA with related CVEs", + vuln: VulnerabilityHandle{ + Name: "GHSA-1234", + BlobValue: &VulnerabilityBlob{ + Aliases: []string{"CVE-2024-1"}, + }, + }, + affected: AffectedPackageBlob{ + CVEs: []string{"CVE-2024-2", "CVE-2024-3"}, + }, + expected: []string{"CVE-2024-1", "CVE-2024-2", "CVE-2024-3"}, + }, + { + name: "CVE with related CVEs", + vuln: VulnerabilityHandle{ + Name: "CVE-2024-1234", + BlobValue: &VulnerabilityBlob{ + Aliases: []string{"CVE-2024-1"}, + }, + }, + affected: AffectedPackageBlob{ + CVEs: []string{"CVE-2024-2", "CVE-2024-3"}, + }, + expected: []string{"CVE-2024-1", "CVE-2024-2", "CVE-2024-3"}, + }, + { + name: "CVE with related CVEs and self", + vuln: VulnerabilityHandle{ + Name: "CVE-2024-1234", + BlobValue: &VulnerabilityBlob{ + Aliases: []string{"CVE-2024-1", "CVE-2024-1234"}, + }, + }, + affected: AffectedPackageBlob{ + CVEs: []string{"CVE-2024-2", "CVE-2024-1234"}, + }, + expected: []string{"CVE-2024-1", "CVE-2024-2"}, // does not include "CVE-2024-1234" + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getRelatedVulnerabilities(&tt.vuln, &tt.affected) + var expected []vulnerability.Reference + for _, name := range tt.expected { + expected = append(expected, vulnerability.Reference{ + ID: name, + Namespace: v5NvdNamespace, + }) + } + require.ElementsMatch(t, expected, got) + }) + } +} + func majorMinorPatch(ver string) (string, string, string) { if !unicode.IsDigit(rune(ver[0])) { return "", "", "" From 963b0ae2f690af61c425f5908202d4dcfe6c9d72 Mon Sep 17 00:00:00 2001 From: Dan Luhring Date: Mon, 10 Mar 2025 11:48:58 -0400 Subject: [PATCH 009/109] fix(java): ensure fatal error from maven search bubbles up (#2518) Signed-off-by: Dan Luhring Signed-off-by: Keith Zantow Co-authored-by: Keith Zantow --- grype/match/matcher.go | 26 +++++++++++ grype/matcher/java/matcher.go | 3 +- grype/matcher/mock/matcher.go | 45 +++++++++++++++++++ grype/vulnerability_matcher.go | 36 +++++++++++----- grype/vulnerability_matcher_test.go | 67 +++++++++++++++++++++++++---- 5 files changed, 155 insertions(+), 22 deletions(-) create mode 100644 grype/matcher/mock/matcher.go diff --git a/grype/match/matcher.go b/grype/match/matcher.go index 1e8e387faf6..ceb0e42183d 100644 --- a/grype/match/matcher.go +++ b/grype/match/matcher.go @@ -1,6 +1,9 @@ package match import ( + "errors" + "fmt" + "github.com/anchore/grype/grype/pkg" "github.com/anchore/grype/grype/vulnerability" syftPkg "github.com/anchore/syft/syft/pkg" @@ -16,3 +19,26 @@ type Matcher interface { // after all matches are found Match(vp vulnerability.Provider, p pkg.Package) ([]Match, []IgnoredMatch, error) } + +// fatalError can be returned from a Matcher to indicate the matching process should stop. +// When fatalError(s) are encountered by the top-level matching process, these will be returned as errors to the caller. +type fatalError struct { + matcher MatcherType + inner error +} + +// NewFatalError creates a new fatalError wrapping the given error +func NewFatalError(matcher MatcherType, e error) error { + return fatalError{matcher: matcher, inner: e} +} + +// Error implements the error interface for fatalError. +func (f fatalError) Error() string { + return fmt.Sprintf("%s encountered a fatal error: %v", f.matcher, f.inner) +} + +// IsFatalError returns true if err includes a fatalError +func IsFatalError(err error) bool { + var fe fatalError + return err != nil && errors.As(err, &fe) +} diff --git a/grype/matcher/java/matcher.go b/grype/matcher/java/matcher.go index 23b5a1a5d81..6831ea57b71 100644 --- a/grype/matcher/java/matcher.go +++ b/grype/matcher/java/matcher.go @@ -59,8 +59,7 @@ func (m *Matcher) Match(store vulnerability.Provider, p pkg.Package) ([]match.Ma if strings.Contains(err.Error(), "no artifact found") { log.Debugf("no upstream maven artifact found for %s", p.Name) } else { - log.WithFields("package", p.Name, "error", err).Error("failed to resolve package details with maven") - return nil, nil, fmt.Errorf("resolving package details with maven: %w", err) + return nil, nil, match.NewFatalError(match.JavaMatcher, fmt.Errorf("resolving details for package %q with maven: %w", p.Name, err)) } } else { matches = append(matches, upstreamMatches...) diff --git a/grype/matcher/mock/matcher.go b/grype/matcher/mock/matcher.go new file mode 100644 index 00000000000..11e444f0510 --- /dev/null +++ b/grype/matcher/mock/matcher.go @@ -0,0 +1,45 @@ +package mock + +import ( + "errors" + + "github.com/anchore/grype/grype/match" + "github.com/anchore/grype/grype/pkg" + "github.com/anchore/grype/grype/vulnerability" + syftPkg "github.com/anchore/syft/syft/pkg" +) + +// MatchFunc is a function that takes a vulnerability provider and a package, +// and returns matches, ignored matches, and an error. +type MatchFunc func(vp vulnerability.Provider, p pkg.Package) ([]match.Match, []match.IgnoredMatch, error) + +// Matcher is a mock implementation of the match.Matcher interface. This is +// intended for testing purposes only. +type Matcher struct { + typ syftPkg.Type + matchFunc MatchFunc +} + +// New creates a new mock Matcher with the given type and match function. +func New(typ syftPkg.Type, matchFunc MatchFunc) *Matcher { + return &Matcher{ + typ: typ, + matchFunc: matchFunc, + } +} + +func (m Matcher) PackageTypes() []syftPkg.Type { + return []syftPkg.Type{m.typ} +} + +func (m Matcher) Type() match.MatcherType { + return "MOCK" +} + +func (m Matcher) Match(vp vulnerability.Provider, p pkg.Package) ([]match.Match, []match.IgnoredMatch, error) { + if m.matchFunc != nil { + return m.matchFunc(vp, p) + } + + return nil, nil, errors.New("no match function provided") +} diff --git a/grype/vulnerability_matcher.go b/grype/vulnerability_matcher.go index 2ebc73ef7cb..307d88154c5 100644 --- a/grype/vulnerability_matcher.go +++ b/grype/vulnerability_matcher.go @@ -64,7 +64,11 @@ func (m *VulnerabilityMatcher) FindMatches(pkgs []pkg.Package, context pkg.Conte } }() - remainingMatches, ignoredMatches = m.findDBMatches(pkgs, context, progressMonitor) + remainingMatches, ignoredMatches, err = m.findDBMatches(pkgs, context, progressMonitor) + if err != nil { + err = fmt.Errorf("unable to find matches against vulnerability database: %w", err) + return remainingMatches, ignoredMatches, err + } remainingMatches, ignoredMatches, err = m.findVEXMatches(context, remainingMatches, ignoredMatches, progressMonitor) if err != nil { @@ -84,13 +88,18 @@ func (m *VulnerabilityMatcher) FindMatches(pkgs []pkg.Package, context pkg.Conte return remainingMatches, ignoredMatches, nil } -func (m *VulnerabilityMatcher) findDBMatches(pkgs []pkg.Package, context pkg.Context, progressMonitor *monitorWriter) (*match.Matches, []match.IgnoredMatch) { +func (m *VulnerabilityMatcher) findDBMatches(pkgs []pkg.Package, context pkg.Context, progressMonitor *monitorWriter) (*match.Matches, []match.IgnoredMatch, error) { var ignoredMatches []match.IgnoredMatch log.Trace("finding matches against DB") matches, err := m.searchDBForMatches(context.Distro, pkgs, progressMonitor) if err != nil { - // errors returned from matchers during searchDBForMatches were being logged and not returned, so just log them here + if match.IsFatalError(err) { + return nil, nil, err + } + + // other errors returned from matchers during searchDBForMatches were being + // logged and not returned, so just log them here log.WithFields("error", err).Debug("error(s) returned from searchDBForMatches") } @@ -111,7 +120,7 @@ func (m *VulnerabilityMatcher) findDBMatches(pkgs []pkg.Package, context pkg.Con ignoredMatches = m.mergeIgnoredMatches(originalIgnoredMatches, ignoredMatches) } - return &matches, ignoredMatches + return &matches, ignoredMatches, nil } func (m *VulnerabilityMatcher) mergeIgnoredMatches(allIgnoredMatches ...[]match.IgnoredMatch) []match.IgnoredMatch { @@ -133,17 +142,16 @@ func (m *VulnerabilityMatcher) searchDBForMatches( packages []pkg.Package, progressMonitor *monitorWriter, ) (match.Matches, error) { - var errs error var allMatches []match.Match var allIgnored []match.IgnoredMatch matcherIndex, defaultMatcher := newMatcherIndex(m.Matchers) var d *distro.Distro if release != nil { - d, errs = distro.NewFromRelease(*release) - if errs != nil { - log.Warnf("unable to determine linux distribution: %+v", errs) - errs = nil + var err error + d, err = distro.NewFromRelease(*release) + if err != nil { + log.Warnf("unable to determine linux distribution: %+v", err) } if d != nil && d.Disabled() { log.Warnf("unsupported linux distribution: %s", d.Name()) @@ -154,6 +162,8 @@ func (m *VulnerabilityMatcher) searchDBForMatches( if defaultMatcher == nil { defaultMatcher = stock.NewStockMatcher(stock.MatcherConfig{UseCPEs: true}) } + + var matcherErrs []error for _, p := range packages { progressMonitor.PackagesProcessed.Increment() log.WithFields("package", displayPackage(p)).Trace("searching for vulnerability matches") @@ -171,8 +181,12 @@ func (m *VulnerabilityMatcher) searchDBForMatches( for _, theMatcher := range matchAgainst { matches, ignoredMatches, err := theMatcher.Match(m.VulnerabilityProvider, p) if err != nil { + if match.IsFatalError(err) { + return match.Matches{}, err + } + log.WithFields("error", err, "package", displayPackage(p)).Warn("matcher returned error") - errs = errors.Join(errs, err) + matcherErrs = append(matcherErrs, err) } allIgnored = append(allIgnored, ignoredMatches...) @@ -206,7 +220,7 @@ func (m *VulnerabilityMatcher) searchDBForMatches( // update the total discovered matches after removing all duplicates and ignores progressMonitor.MatchesDiscovered.Set(int64(res.Count())) - return res, errs + return res, errors.Join(matcherErrs...) } func (m *VulnerabilityMatcher) findVEXMatches(context pkg.Context, remainingMatches *match.Matches, ignoredMatches []match.IgnoredMatch, progressMonitor *monitorWriter) (*match.Matches, []match.IgnoredMatch, error) { diff --git a/grype/vulnerability_matcher_test.go b/grype/vulnerability_matcher_test.go index 988b30ef9d6..9b3f66ad89b 100644 --- a/grype/vulnerability_matcher_test.go +++ b/grype/vulnerability_matcher_test.go @@ -1,6 +1,7 @@ package grype import ( + "errors" "testing" "github.com/google/go-cmp/cmp" @@ -17,6 +18,7 @@ import ( "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/matcher" "github.com/anchore/grype/grype/matcher/apk" + matcherMock "github.com/anchore/grype/grype/matcher/mock" "github.com/anchore/grype/grype/matcher/ruby" "github.com/anchore/grype/grype/pkg" "github.com/anchore/grype/grype/pkg/qualifier" @@ -250,7 +252,6 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { { name: "no matches", fields: fields{ - //Store: str, Matchers: matcher.NewDefaultMatchers(matcher.Config{}), }, args: args{ @@ -273,7 +274,6 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { { name: "matches by exact-direct match (OS)", fields: fields{ - //Store: str, Matchers: matcher.NewDefaultMatchers(matcher.Config{}), }, args: args{ @@ -325,7 +325,6 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { { name: "fail on severity threshold", fields: fields{ - //Store: str, Matchers: matcher.NewDefaultMatchers(matcher.Config{}), FailSeverity: func() *vulnerability.Severity { x := vulnerability.LowSeverity @@ -381,7 +380,6 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { { name: "pass on severity threshold with VEX", fields: fields{ - //Store: str, Matchers: matcher.NewDefaultMatchers(matcher.Config{}), FailSeverity: func() *vulnerability.Severity { x := vulnerability.LowSeverity @@ -464,7 +462,6 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { { name: "matches by exact-direct match (language)", fields: fields{ - //Store: str, Matchers: matcher.NewDefaultMatchers(matcher.Config{ Ruby: ruby.MatcherConfig{ UseCPEs: true, @@ -561,7 +558,6 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { { name: "normalize by cve", fields: fields{ - //Store: str, Matchers: matcher.NewDefaultMatchers( matcher.Config{ Ruby: ruby.MatcherConfig{ @@ -645,7 +641,6 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { { name: "normalize by cve -- ignore GHSA", fields: fields{ - //Store: str, Matchers: matcher.NewDefaultMatchers( matcher.Config{ Ruby: ruby.MatcherConfig{ @@ -758,7 +753,6 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { { name: "normalize by cve -- ignore CVE", fields: fields{ - //Store: str, Matchers: matcher.NewDefaultMatchers( matcher.Config{ Ruby: ruby.MatcherConfig{ @@ -878,7 +872,6 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { { name: "ignore CVE (not normalized by CVE)", fields: fields{ - //Store: str, Matchers: matcher.NewDefaultMatchers(matcher.Config{ Ruby: ruby.MatcherConfig{ UseCPEs: true, @@ -985,6 +978,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { wantErr: nil, }, } + for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { m := &VulnerabilityMatcher{ @@ -1032,6 +1026,61 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { } } +func Test_fatalErrors(t *testing.T) { + tests := []struct { + name string + matcherFunc matcherMock.MatchFunc + assertErr assert.ErrorAssertionFunc + }{ + { + name: "no error", + matcherFunc: func(_ vulnerability.Provider, _ pkg.Package) ([]match.Match, []match.IgnoredMatch, error) { + return nil, nil, nil + }, + assertErr: assert.NoError, + }, + { + name: "non-fatal error", + matcherFunc: func(_ vulnerability.Provider, _ pkg.Package) ([]match.Match, []match.IgnoredMatch, error) { + return nil, nil, errors.New("some error") + }, + assertErr: assert.NoError, + }, + { + name: "fatal error", + matcherFunc: func(_ vulnerability.Provider, _ pkg.Package) ([]match.Match, []match.IgnoredMatch, error) { + return nil, nil, match.NewFatalError(match.UnknownMatcherType, errors.New("some error")) + }, + assertErr: assert.Error, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &VulnerabilityMatcher{ + Matchers: []match.Matcher{matcherMock.New(syftPkg.JavaPkg, tt.matcherFunc)}, + } + + _, _, err := m.FindMatches([]pkg.Package{ + { + Name: "foo", + Version: "1.2.3", + Type: syftPkg.JavaPkg, + }, + }, + pkg.Context{ + Distro: &linux.Release{ + ID: "debian", + VersionID: "8", + }, + }, + ) + + tt.assertErr(t, err) + }) + } +} + func Test_indexFalsePositivesByLocation(t *testing.T) { cases := []struct { name string From 718ea3060267edcae7b10a9bf16c0acdad10820a Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Tue, 11 Mar 2025 10:55:51 -0400 Subject: [PATCH 010/109] fix: populate vulnerability.Metadata.DataSource with first reference URL (#2523) Signed-off-by: Keith Zantow --- grype/db/v6/vulnerability_provider.go | 26 +++-- grype/db/v6/vulnerability_provider_test.go | 120 ++++++++++++++++++--- grype/vulnerability/metadata.go | 4 +- 3 files changed, 124 insertions(+), 26 deletions(-) diff --git a/grype/db/v6/vulnerability_provider.go b/grype/db/v6/vulnerability_provider.go index aa5eff83c48..26e6d7d9429 100644 --- a/grype/db/v6/vulnerability_provider.go +++ b/grype/db/v6/vulnerability_provider.go @@ -83,20 +83,14 @@ func newVulnerabilityMetadata(vuln *VulnerabilityHandle, namespace string, kevs sev, cvss, err := extractSeverities(vuln) if err != nil { log.WithFields("id", vuln.Name, "vulnerability", vuln.String()).Debug("unable to extract severity from vulnerability") - return &vulnerability.Metadata{ - ID: vuln.Name, - DataSource: strings.Split(namespace, ":")[0], - Namespace: namespace, - Severity: toSeverityString(vulnerability.UnknownSeverity), - }, nil } return &vulnerability.Metadata{ ID: vuln.Name, - DataSource: vuln.Provider.ID, + DataSource: firstReferenceURL(vuln), Namespace: namespace, Severity: toSeverityString(sev), - URLs: toURLs(vuln), + URLs: lastReferenceURLs(vuln), Description: vuln.BlobValue.Description, Cvss: cvss, KnownExploited: kevs, @@ -522,9 +516,21 @@ func toSeverityString(sev vulnerability.Severity) string { return strcase.ToCamel(sev.String()) } -func toURLs(vuln *VulnerabilityHandle) []string { - var out []string +// returns the first reference url to populate the DataSource +func firstReferenceURL(vuln *VulnerabilityHandle) string { for _, v := range vuln.BlobValue.References { + return v.URL + } + return "" +} + +// skip the first reference URL and return the remainder to populate the URLs +func lastReferenceURLs(vuln *VulnerabilityHandle) []string { + var out []string + for i, v := range vuln.BlobValue.References { + if i == 0 { + continue + } out = append(out, v.URL) } return out diff --git a/grype/db/v6/vulnerability_provider_test.go b/grype/db/v6/vulnerability_provider_test.go index 010ec973215..668a2b8410c 100644 --- a/grype/db/v6/vulnerability_provider_test.go +++ b/grype/db/v6/vulnerability_provider_test.go @@ -46,10 +46,10 @@ func Test_FindVulnerabilitiesByDistro(t *testing.T) { Advisories: []vulnerability.Advisory{}, Metadata: &vulnerability.Metadata{ ID: "CVE-2014-fake-1", - DataSource: "debian", + DataSource: "http://somewhere/CVE-2014-fake-1", Namespace: "debian:distro:debian:8", Severity: "High", - URLs: []string{"http://somewhere/CVE-2014-fake-1"}, + URLs: nil, Description: "CVE-2014-fake-1-description", }, }, @@ -65,10 +65,10 @@ func Test_FindVulnerabilitiesByDistro(t *testing.T) { Advisories: []vulnerability.Advisory{}, Metadata: &vulnerability.Metadata{ ID: "CVE-2013-fake-2", - DataSource: "debian", + DataSource: "http://somewhere/CVE-2013-fake-2", Namespace: "debian:distro:debian:8", Severity: "High", - URLs: []string{"http://somewhere/CVE-2013-fake-2"}, + URLs: nil, Description: "CVE-2013-fake-2-description", }, }, @@ -123,10 +123,10 @@ func Test_FindVulnerabilitiesByCPE(t *testing.T) { Advisories: []vulnerability.Advisory{}, Metadata: &vulnerability.Metadata{ ID: "CVE-2014-fake-4", - DataSource: "debian", + DataSource: "http://somewhere/CVE-2014-fake-4", Namespace: "nvd:cpe", Severity: "High", - URLs: []string{"http://somewhere/CVE-2014-fake-4"}, + URLs: nil, Description: "CVE-2014-fake-4-description", }, }, @@ -150,10 +150,10 @@ func Test_FindVulnerabilitiesByCPE(t *testing.T) { Advisories: []vulnerability.Advisory{}, Metadata: &vulnerability.Metadata{ ID: "CVE-2014-fake-4", - DataSource: "debian", + DataSource: "http://somewhere/CVE-2014-fake-4", Namespace: "nvd:cpe", Severity: "High", - URLs: []string{"http://somewhere/CVE-2014-fake-4"}, + URLs: nil, Description: "CVE-2014-fake-4-description", }, }, @@ -177,10 +177,10 @@ func Test_FindVulnerabilitiesByCPE(t *testing.T) { Advisories: []vulnerability.Advisory{}, Metadata: &vulnerability.Metadata{ ID: "CVE-2014-fake-3", - DataSource: "debian", + DataSource: "http://somewhere/CVE-2014-fake-3", Namespace: "nvd:cpe", Severity: "High", - URLs: []string{"http://somewhere/CVE-2014-fake-3"}, + URLs: nil, Description: "CVE-2014-fake-3-description", }, }, @@ -198,10 +198,10 @@ func Test_FindVulnerabilitiesByCPE(t *testing.T) { Advisories: []vulnerability.Advisory{}, Metadata: &vulnerability.Metadata{ ID: "CVE-2014-fake-4", - DataSource: "debian", + DataSource: "http://somewhere/CVE-2014-fake-4", Namespace: "nvd:cpe", Severity: "High", - URLs: []string{"http://somewhere/CVE-2014-fake-4"}, + URLs: nil, Description: "CVE-2014-fake-4-description", }, }, @@ -270,10 +270,10 @@ func Test_FindVulnerabilitiesByByID(t *testing.T) { Advisories: []vulnerability.Advisory{}, Metadata: &vulnerability.Metadata{ ID: "CVE-2014-fake-1", - DataSource: "debian", + DataSource: "http://somewhere/CVE-2014-fake-1", Namespace: "debian:distro:debian:8", Severity: "High", - URLs: []string{"http://somewhere/CVE-2014-fake-1"}, + URLs: nil, Description: "CVE-2014-fake-1-description", }, }, @@ -303,6 +303,98 @@ func Test_FindVulnerabilitiesByByID(t *testing.T) { require.Empty(t, actual) } +func Test_DataSource(t *testing.T) { + tests := []struct { + name string + vuln VulnerabilityHandle + expected vulnerability.Metadata + }{ + { + name: "no reference urls", + vuln: VulnerabilityHandle{ + BlobValue: &VulnerabilityBlob{ + References: nil, + }, + }, + expected: vulnerability.Metadata{ + DataSource: "", + URLs: nil, + }, + }, + { + name: "one reference url", + vuln: VulnerabilityHandle{ + BlobValue: &VulnerabilityBlob{ + References: []Reference{ + { + URL: "url1", + }, + }, + }, + }, + expected: vulnerability.Metadata{ + DataSource: "url1", + URLs: nil, + }, + }, + { + name: "two reference urls", + vuln: VulnerabilityHandle{ + BlobValue: &VulnerabilityBlob{ + References: []Reference{ + { + URL: "url1", + }, + { + URL: "url2", + }, + }, + }, + }, + expected: vulnerability.Metadata{ + DataSource: "url1", + URLs: []string{"url2"}, + }, + }, + { + name: "many reference urls", + vuln: VulnerabilityHandle{ + BlobValue: &VulnerabilityBlob{ + References: []Reference{ + { + URL: "url4", + }, + { + URL: "url3", + }, + { + URL: "url2", + }, + { + URL: "url1", + }, + }, + }, + }, + expected: vulnerability.Metadata{ + DataSource: "url4", + URLs: []string{"url3", "url2", "url1"}, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := newVulnerabilityMetadata(&tt.vuln, "", nil, nil) + got.Severity = "" + require.NoError(t, err) + if diff := cmp.Diff(&tt.expected, got, cmpOpts()...); diff != "" { + t.Fatal(diff) + } + }) + } +} + func cmpOpts() []cmp.Option { return []cmp.Option{ // globally ignore unexported -- these are unexported structs we cannot reference here to use cmpopts.IgnoreUnexported diff --git a/grype/vulnerability/metadata.go b/grype/vulnerability/metadata.go index 633978e5229..2032c7d9857 100644 --- a/grype/vulnerability/metadata.go +++ b/grype/vulnerability/metadata.go @@ -6,10 +6,10 @@ import ( type Metadata struct { ID string - DataSource string + DataSource string // the primary reference URL, i.e. where the data originated Namespace string Severity string - URLs []string + URLs []string // secondary reference URLs a vulnerability may provide Description string Cvss []Cvss KnownExploited []KnownExploited From 387d57365b7e435cfe74c7b8dcd63c6a033abe67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 16:11:36 -0400 Subject: [PATCH 011/109] chore(deps): bump docker/login-action from 3.3.0 to 3.4.0 (#2528) Bumps [docker/login-action](https://github.com/docker/login-action) from 3.3.0 to 3.4.0. - [Release notes](https://github.com/docker/login-action/releases) - [Commits](https://github.com/docker/login-action/compare/9780b0c442fbb1117ed29e0efdff1e18412f7567...74a5d142397b4f367a81961eba4e8cd7edddf772) --- updated-dependencies: - dependency-name: docker/login-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/release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c7364a00319..04fe4ec4cfd 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -123,13 +123,13 @@ jobs: build-cache-key-prefix: "snapshot" - name: Login to Docker Hub - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 #v3.3.0 + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 #v3.4.0 with: username: ${{ secrets.ANCHOREOSSWRITE_DH_USERNAME }} password: ${{ secrets.ANCHOREOSSWRITE_DH_PAT }} - name: Login to GitHub Container Registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 #v3.3.0 + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 #v3.4.0 with: registry: ghcr.io username: ${{ github.actor }} From 0e73d95f4169fdc1ccf52a69dae8c2d8d4834cb5 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, 14 Mar 2025 16:12:24 -0400 Subject: [PATCH 012/109] chore(deps): update tools to latest versions (#2512) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.binny.yaml b/.binny.yaml index ec3fbb050a8..ef56a9c84ee 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -2,7 +2,7 @@ tools: # we want to use a pinned version of binny to manage the toolchain (so binny manages itself!) - name: binny version: - want: v0.8.0 + want: v0.9.0 method: github-release with: repo: anchore/binny @@ -26,7 +26,7 @@ tools: # used for linting - name: golangci-lint version: - want: v1.64.6 + want: v1.64.7 method: github-release with: repo: golangci/golangci-lint @@ -58,7 +58,7 @@ tools: # used to release all artifacts - name: goreleaser version: - want: v2.7.0 + want: v2.8.1 method: github-release with: repo: goreleaser/goreleaser @@ -90,7 +90,7 @@ tools: # used for running all local and CI tasks - name: task version: - want: v3.41.0 + want: v3.42.1 method: github-release with: repo: go-task/task @@ -98,7 +98,7 @@ tools: # used for triggering a release - name: gh version: - want: v2.68.0 + want: v2.68.1 method: github-release with: repo: cli/cli From c42c8d4364eed55933fd40ee0a6bde573dc8b6a7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 16:12:32 -0400 Subject: [PATCH 013/109] chore(deps): bump github.com/charmbracelet/lipgloss from 1.0.0 to 1.1.0 (#2525) Bumps [github.com/charmbracelet/lipgloss](https://github.com/charmbracelet/lipgloss) from 1.0.0 to 1.1.0. - [Release notes](https://github.com/charmbracelet/lipgloss/releases) - [Changelog](https://github.com/charmbracelet/lipgloss/blob/master/.goreleaser.yml) - [Commits](https://github.com/charmbracelet/lipgloss/compare/v1.0.0...v1.1.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, 9 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index d39f72ba4f9..e0cb40c8226 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 github.com/charmbracelet/bubbletea v1.3.4 - github.com/charmbracelet/lipgloss v1.0.0 + github.com/charmbracelet/lipgloss v1.1.0 github.com/dave/jennifer v1.7.1 github.com/docker/docker v28.0.1+incompatible github.com/dustin/go-humanize v1.0.1 @@ -115,8 +115,10 @@ require ( github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/charmbracelet/bubbles v0.20.0 // indirect + github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/charmbracelet/x/ansi v0.8.0 // indirect + github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect github.com/cloudflare/circl v1.3.8 // indirect github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect diff --git a/go.sum b/go.sum index d69a3797c22..e85a4a86e44 100644 --- a/go.sum +++ b/go.sum @@ -776,12 +776,16 @@ github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQW github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= +github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= 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 v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= -github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY= +github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30= github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE= github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8= +github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs= github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= From c0bba92b872bfc93196bb5563d40ab83bf86e227 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Mar 2025 16:12:39 -0400 Subject: [PATCH 014/109] chore(deps): bump github/codeql-action from 3.28.10 to 3.28.11 (#2519) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.10 to 3.28.11. - [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/b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d...6bb031afdd8eb862ea3fc1848194185e076637e5) --- 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 | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 67d1fc17e08..08c339bdfc8 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -63,7 +63,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -85,4 +85,4 @@ jobs: run: make grype - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.28.10 + uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 10c014b21a3..bbe27042998 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -38,6 +38,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v1.0.26 + uses: github/codeql-action/upload-sarif@6bb031afdd8eb862ea3fc1848194185e076637e5 # v1.0.26 with: sarif_file: results.sarif From 2eb0c33e01081ae876ddd874ae22a59c1795d914 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 17 Mar 2025 12:34:11 -0400 Subject: [PATCH 015/109] Improve DB metadata regarding data provenance (#2529) * improve metadata around data provenance Signed-off-by: Alex Goodman * remove DB checksum from status Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- cmd/grype/cli/commands/db_import.go | 2 +- cmd/grype/cli/commands/db_status.go | 22 +++++-- cmd/grype/cli/commands/db_status_test.go | 31 ++++----- cmd/grype/cli/commands/root.go | 44 +++++++++---- grype/db/v6/db.go | 3 +- grype/db/v6/distribution/client.go | 17 ++--- grype/db/v6/distribution/client_test.go | 18 +++-- grype/db/v6/import_metadata.go | 11 ++-- grype/db/v6/import_metadata_test.go | 7 +- grype/db/v6/installation/curator.go | 69 ++++++++++++-------- grype/db/v6/installation/curator_test.go | 6 +- grype/db/v6/status.go | 39 ----------- grype/db/v6/vulnerability_provider.go | 76 +++++++++++++++------- grype/load_vulnerability_db.go | 6 +- grype/presenter/json/presenter.go | 15 ++--- grype/presenter/models/descriptor.go | 10 +-- grype/presenter/models/document.go | 12 ++-- grype/presenter/models/presenter_bundle.go | 10 ++- grype/vulnerability/provider.go | 62 ++++++++++++++++++ 19 files changed, 280 insertions(+), 180 deletions(-) delete mode 100644 grype/db/v6/status.go diff --git a/cmd/grype/cli/commands/db_import.go b/cmd/grype/cli/commands/db_import.go index 03beeb453a9..0af546a208a 100644 --- a/cmd/grype/cli/commands/db_import.go +++ b/cmd/grype/cli/commands/db_import.go @@ -50,6 +50,6 @@ func runDBImport(opts options.DatabaseCommand, dbArchivePath string) error { } s := c.Status() - log.WithFields("built", s.Built.String(), "status", s.Status()).Info("vulnerability database imported") + log.WithFields("built", s.Built.String(), "status", renderStoreValidation(s)).Info("vulnerability database imported") return nil } diff --git a/cmd/grype/cli/commands/db_status.go b/cmd/grype/cli/commands/db_status.go index 46c31c63f10..af402e27aab 100644 --- a/cmd/grype/cli/commands/db_status.go +++ b/cmd/grype/cli/commands/db_status.go @@ -5,14 +5,15 @@ import ( "fmt" "io" "os" + "time" "github.com/spf13/cobra" "github.com/anchore/clio" "github.com/anchore/grype/cmd/grype/cli/options" - v6 "github.com/anchore/grype/grype/db/v6" "github.com/anchore/grype/grype/db/v6/distribution" "github.com/anchore/grype/grype/db/v6/installation" + "github.com/anchore/grype/grype/vulnerability" ) type dbStatusOptions struct { @@ -67,17 +68,19 @@ func runDBStatus(opts dbStatusOptions) error { return fmt.Errorf("failed to present db status information: %+v", err) } - return status.Err + return status.Error } -func presentDBStatus(format string, writer io.Writer, status v6.Status) error { +func presentDBStatus(format string, writer io.Writer, status vulnerability.ProviderStatus) error { switch format { case textOutputFormat: fmt.Fprintln(writer, "Path: ", status.Path) fmt.Fprintln(writer, "Schema: ", status.SchemaVersion) - fmt.Fprintln(writer, "Built: ", status.Built.String()) - fmt.Fprintln(writer, "Checksum: ", status.Checksum) - fmt.Fprintln(writer, "Status: ", status.Status()) + fmt.Fprintln(writer, "Built: ", status.Built.Format(time.RFC3339)) + if status.From != "" { + fmt.Fprintln(writer, "From: ", status.From) + } + fmt.Fprintln(writer, "Status: ", renderStoreValidation(status)) case jsonOutputFormat: enc := json.NewEncoder(writer) enc.SetEscapeHTML(false) @@ -91,3 +94,10 @@ func presentDBStatus(format string, writer io.Writer, status v6.Status) error { return nil } + +func renderStoreValidation(status vulnerability.ProviderStatus) string { + if status.Error != nil { + return "invalid" + } + return "valid" +} diff --git a/cmd/grype/cli/commands/db_status_test.go b/cmd/grype/cli/commands/db_status_test.go index dbfbcc73525..a2d774a8b4c 100644 --- a/cmd/grype/cli/commands/db_status_test.go +++ b/cmd/grype/cli/commands/db_status_test.go @@ -10,30 +10,30 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - v6 "github.com/anchore/grype/grype/db/v6" + "github.com/anchore/grype/grype/vulnerability" ) func TestPresentDBStatus(t *testing.T) { - validStatus := v6.Status{ + validStatus := vulnerability.ProviderStatus{ Path: "/Users/test/Library/Caches/grype/db/6/vulnerability.db", + From: "https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.2_2025-03-14T01:31:06Z_1741925227.tar.zst?checksum=sha256%3Ad4654e3b212f1d8a1aaab979599691099af541568d687c4a7c4e7c1da079b9b8", SchemaVersion: "6.0.0", - Built: v6.Time{Time: time.Date(2024, 11, 27, 14, 43, 17, 0, time.UTC)}, - Checksum: "xxh64:89d3ae128f6e718e", - Err: nil, + Built: time.Date(2024, 11, 27, 14, 43, 17, 0, time.UTC), + Error: nil, } - invalidStatus := v6.Status{ + invalidStatus := vulnerability.ProviderStatus{ Path: "/Users/test/Library/Caches/grype/db/6/vulnerability.db", + From: "https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.2_2025-03-14T01:31:06Z_1741925227.tar.zst?checksum=sha256%3Ad4654e3b212f1d8a1aaab979599691099af541568d687c4a7c4e7c1da079b9b8", SchemaVersion: "6.0.0", - Built: v6.Time{Time: time.Date(2024, 11, 27, 14, 43, 17, 0, time.UTC)}, - Checksum: "xxh64:89d3ae128f6e718e", - Err: errors.New("checksum mismatch"), + Built: time.Date(2024, 11, 27, 14, 43, 17, 0, time.UTC), + Error: errors.New("checksum mismatch"), } tests := []struct { name string format string - status v6.Status + status vulnerability.ProviderStatus expectedText string expectedErr require.ErrorAssertionFunc }{ @@ -44,7 +44,7 @@ func TestPresentDBStatus(t *testing.T) { expectedText: `Path: /Users/test/Library/Caches/grype/db/6/vulnerability.db Schema: 6.0.0 Built: 2024-11-27T14:43:17Z -Checksum: xxh64:89d3ae128f6e718e +From: https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.2_2025-03-14T01:31:06Z_1741925227.tar.zst?checksum=sha256%3Ad4654e3b212f1d8a1aaab979599691099af541568d687c4a7c4e7c1da079b9b8 Status: valid `, expectedErr: require.NoError, @@ -56,7 +56,7 @@ Status: valid expectedText: `Path: /Users/test/Library/Caches/grype/db/6/vulnerability.db Schema: 6.0.0 Built: 2024-11-27T14:43:17Z -Checksum: xxh64:89d3ae128f6e718e +From: https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.2_2025-03-14T01:31:06Z_1741925227.tar.zst?checksum=sha256%3Ad4654e3b212f1d8a1aaab979599691099af541568d687c4a7c4e7c1da079b9b8 Status: invalid `, expectedErr: require.NoError, @@ -67,10 +67,10 @@ Status: invalid status: validStatus, expectedText: `{ "schemaVersion": "6.0.0", + "from": "https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.2_2025-03-14T01:31:06Z_1741925227.tar.zst?checksum=sha256%3Ad4654e3b212f1d8a1aaab979599691099af541568d687c4a7c4e7c1da079b9b8", "built": "2024-11-27T14:43:17Z", "path": "/Users/test/Library/Caches/grype/db/6/vulnerability.db", - "checksum": "xxh64:89d3ae128f6e718e", - "error": "" + "valid": true } `, expectedErr: require.NoError, @@ -81,9 +81,10 @@ Status: invalid status: invalidStatus, expectedText: `{ "schemaVersion": "6.0.0", + "from": "https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.2_2025-03-14T01:31:06Z_1741925227.tar.zst?checksum=sha256%3Ad4654e3b212f1d8a1aaab979599691099af541568d687c4a7c4e7c1da079b9b8", "built": "2024-11-27T14:43:17Z", "path": "/Users/test/Library/Caches/grype/db/6/vulnerability.db", - "checksum": "xxh64:89d3ae128f6e718e", + "valid": false, "error": "checksum mismatch" } `, diff --git a/cmd/grype/cli/commands/root.go b/cmd/grype/cli/commands/root.go index 0883b6b81d0..750522be5df 100644 --- a/cmd/grype/cli/commands/root.go +++ b/cmd/grype/cli/commands/root.go @@ -11,7 +11,6 @@ import ( "github.com/anchore/clio" "github.com/anchore/grype/cmd/grype/cli/options" "github.com/anchore/grype/grype" - v6 "github.com/anchore/grype/grype/db/v6" "github.com/anchore/grype/grype/event" "github.com/anchore/grype/grype/event/parsers" "github.com/anchore/grype/grype/grypeerr" @@ -119,7 +118,7 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs } var vp vulnerability.Provider - var status *v6.Status + var status *vulnerability.ProviderStatus var packages []pkg.Package var s *sbom.SBOM var pkgContext pkg.Context @@ -201,18 +200,16 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs errs = appendErrors(errs, err) } - model, err := models.NewDocument(app.ID(), packages, pkgContext, *remainingMatches, ignoredMatches, vp, opts, status, models.SortByPackage) + model, err := models.NewDocument(app.ID(), packages, pkgContext, *remainingMatches, ignoredMatches, vp, opts, dbInfo(status, vp), models.SortByPackage) if err != nil { return fmt.Errorf("failed to create document: %w", err) } if err = writer.Write(models.PresenterConfig{ - ID: app.ID(), - Document: model, - SBOM: s, - AppConfig: opts, - DBStatus: status, - Pretty: opts.Pretty, + ID: app.ID(), + Document: model, + SBOM: s, + Pretty: opts.Pretty, }); err != nil { errs = appendErrors(errs, err) } @@ -220,6 +217,29 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs return errs } +func dbInfo(status *vulnerability.ProviderStatus, vp vulnerability.Provider) any { + var providers map[string]vulnerability.DataProvenance + + if vp != nil { + providers = make(map[string]vulnerability.DataProvenance) + if dpr, ok := vp.(vulnerability.StoreMetadataProvider); ok { + dps, err := dpr.DataProvenance() + // ignore errors here + if err == nil { + providers = dps + } + } + } + + return struct { + Status *vulnerability.ProviderStatus `json:"status"` + Providers map[string]vulnerability.DataProvenance `json:"providers"` + }{ + Status: status, + Providers: providers, + } +} + func applyDistroHint(pkgs []pkg.Package, context *pkg.Context, opts *options.Grype) { if opts.Distro != "" { log.Infof("using distro: %s", opts.Distro) @@ -326,7 +346,7 @@ func getProviderConfig(opts *options.Grype) pkg.ProviderConfig { } } -func validateDBLoad(loadErr error, status *v6.Status) error { +func validateDBLoad(loadErr error, status *vulnerability.ProviderStatus) error { if loadErr != nil { // notify the user about grype db delete to fix checksum errors if strings.Contains(loadErr.Error(), "checksum") { @@ -340,8 +360,8 @@ func validateDBLoad(loadErr error, status *v6.Status) error { if status == nil { return fmt.Errorf("unable to determine the status of the vulnerability db") } - if status.Err != nil { - return fmt.Errorf("db could not be loaded: %w", status.Err) + if status.Error != nil { + return fmt.Errorf("db could not be loaded: %w", status.Error) } return nil } diff --git a/grype/db/v6/db.go b/grype/db/v6/db.go index 84630538302..795fd4c6295 100644 --- a/grype/db/v6/db.go +++ b/grype/db/v6/db.go @@ -9,6 +9,7 @@ import ( "gorm.io/gorm" "github.com/anchore/grype/grype/db/internal/gormadapter" + "github.com/anchore/grype/grype/vulnerability" "github.com/anchore/grype/internal/log" ) @@ -72,7 +73,7 @@ type Writer interface { type Curator interface { Reader() (Reader, error) - Status() Status + Status() vulnerability.ProviderStatus Delete() error Update() (bool, error) Import(dbArchivePath string) error diff --git a/grype/db/v6/distribution/client.go b/grype/db/v6/distribution/client.go index 171f575880e..41f9ef9916a 100644 --- a/grype/db/v6/distribution/client.go +++ b/grype/db/v6/distribution/client.go @@ -40,7 +40,7 @@ type Config struct { type Client interface { Latest() (*LatestDocument, error) IsUpdateAvailable(current *v6.Description) (*Archive, error) - Download(archive Archive, dest string, downloadProgress *progress.Manual) (string, error) + Download(archive Archive, dest string, downloadProgress *progress.Manual) (string, string, error) } type client struct { @@ -126,24 +126,24 @@ func (c client) isUpdateAvailable(current *v6.Description, candidate *LatestDocu return nil, message } -func (c client) Download(archive Archive, dest string, downloadProgress *progress.Manual) (string, error) { +func (c client) Download(archive Archive, dest string, downloadProgress *progress.Manual) (string, string, error) { defer downloadProgress.SetCompleted() if err := os.MkdirAll(dest, 0700); err != nil { - return "", fmt.Errorf("unable to create db download root dir: %w", err) + return "", "", fmt.Errorf("unable to create db download root dir: %w", err) } // note: as much as I'd like to use the afero FS abstraction here, the go-getter library does not support it tempDir, err := os.MkdirTemp(dest, "grype-db-download") if err != nil { - return "", fmt.Errorf("unable to create db client temp dir: %w", err) + return "", "", fmt.Errorf("unable to create db client temp dir: %w", err) } // download the db to the temp dir u, err := url.Parse(c.latestURL()) if err != nil { removeAllOrLog(afero.NewOsFs(), tempDir) - return "", fmt.Errorf("unable to parse db URL %q: %w", c.latestURL(), err) + return "", "", fmt.Errorf("unable to parse db URL %q: %w", c.latestURL(), err) } u.Path = path.Join(path.Dir(u.Path), path.Clean(archive.Path)) @@ -157,13 +157,14 @@ func (c client) Download(archive Archive, dest string, downloadProgress *progres u.RawQuery = query.Encode() // go-getter will automatically extract all files within the archive to the temp dir - err = c.dbDownloader.GetToDir(tempDir, u.String(), downloadProgress) + finalURL := u.String() + err = c.dbDownloader.GetToDir(tempDir, finalURL, downloadProgress) if err != nil { removeAllOrLog(afero.NewOsFs(), tempDir) - return "", fmt.Errorf("unable to download db: %w", err) + return "", "", fmt.Errorf("unable to download db: %w", err) } - return tempDir, nil + return tempDir, finalURL, nil } // Latest loads a LatestDocument from the configured URL. diff --git a/grype/db/v6/distribution/client_test.go b/grype/db/v6/distribution/client_test.go index 183302e0d96..24cd6e7a715 100644 --- a/grype/db/v6/distribution/client_test.go +++ b/grype/db/v6/distribution/client_test.go @@ -152,35 +152,41 @@ func TestClient_Download(t *testing.T) { t.Run("successful download", func(t *testing.T) { c, mg := setup() - mg.On("GetToDir", mock.Anything, "http://localhost:8080/path/to/archive.tar.gz?checksum=checksum123", mock.Anything).Return(nil) + url := "http://localhost:8080/path/to/archive.tar.gz?checksum=checksum123" + mg.On("GetToDir", mock.Anything, url, mock.Anything).Return(nil) - tempDir, err := c.Download(*archive, destDir, &progress.Manual{}) + tempDir, actualURL, err := c.Download(*archive, destDir, &progress.Manual{}) require.NoError(t, err) require.True(t, len(tempDir) > 0) + assert.Equal(t, url, actualURL) mg.AssertExpectations(t) }) t.Run("download error", func(t *testing.T) { c, mg := setup() - mg.On("GetToDir", mock.Anything, "http://localhost:8080/path/to/archive.tar.gz?checksum=checksum123", mock.Anything).Return(errors.New("download failed")) + url := "http://localhost:8080/path/to/archive.tar.gz?checksum=checksum123" + mg.On("GetToDir", mock.Anything, url, mock.Anything).Return(errors.New("download failed")) - tempDir, err := c.Download(*archive, destDir, &progress.Manual{}) + tempDir, actualURL, err := c.Download(*archive, destDir, &progress.Manual{}) require.Error(t, err) require.Empty(t, tempDir) require.Contains(t, err.Error(), "unable to download db") + assert.Empty(t, actualURL) mg.AssertExpectations(t) }) t.Run("nested into dir that does not exist", func(t *testing.T) { c, mg := setup() - mg.On("GetToDir", mock.Anything, "http://localhost:8080/path/to/archive.tar.gz?checksum=checksum123", mock.Anything).Return(nil) + url := "http://localhost:8080/path/to/archive.tar.gz?checksum=checksum123" + mg.On("GetToDir", mock.Anything, url, mock.Anything).Return(nil) nestedPath := filepath.Join(destDir, "nested") - tempDir, err := c.Download(*archive, nestedPath, &progress.Manual{}) + tempDir, actualURL, err := c.Download(*archive, nestedPath, &progress.Manual{}) require.NoError(t, err) require.True(t, len(tempDir) > 0) + assert.Equal(t, url, actualURL) mg.AssertExpectations(t) }) diff --git a/grype/db/v6/import_metadata.go b/grype/db/v6/import_metadata.go index 1456615a95e..b18e8fa22c0 100644 --- a/grype/db/v6/import_metadata.go +++ b/grype/db/v6/import_metadata.go @@ -18,8 +18,8 @@ import ( const ImportMetadataFileName = "import.json" type ImportMetadata struct { - Digest string `json:"digest"` - + Digest string `json:"digest"` + Source string `json:"source,omitempty"` ClientVersion string `json:"client_version"` } @@ -59,7 +59,7 @@ func CalculateDBDigest(fs afero.Fs, dbFilePath string) (string, error) { return fmt.Sprintf("xxh64:%s", digest), nil } -func WriteImportMetadata(fs afero.Fs, dbDir string) (*ImportMetadata, error) { +func WriteImportMetadata(fs afero.Fs, dbDir, source string) (*ImportMetadata, error) { metadataFilePath := filepath.Join(dbDir, ImportMetadataFileName) f, err := fs.OpenFile(metadataFilePath, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0644) if err != nil { @@ -72,10 +72,10 @@ func WriteImportMetadata(fs afero.Fs, dbDir string) (*ImportMetadata, error) { return nil, fmt.Errorf("failed to calculate checksum for DB file: %w", err) } - return writeImportMetadata(f, checksums) + return writeImportMetadata(f, checksums, source) } -func writeImportMetadata(writer io.Writer, checksums string) (*ImportMetadata, error) { +func writeImportMetadata(writer io.Writer, checksums, source string) (*ImportMetadata, error) { if checksums == "" { return nil, fmt.Errorf("checksum is required") } @@ -89,6 +89,7 @@ func writeImportMetadata(writer io.Writer, checksums string) (*ImportMetadata, e doc := ImportMetadata{ Digest: checksums, + Source: source, ClientVersion: schemaver.New(ModelVersion, Revision, Addition).String(), } diff --git a/grype/db/v6/import_metadata_test.go b/grype/db/v6/import_metadata_test.go index d4bc8a58d48..5b1dd13a98e 100644 --- a/grype/db/v6/import_metadata_test.go +++ b/grype/db/v6/import_metadata_test.go @@ -44,9 +44,10 @@ func TestReadImportMetadata(t *testing.T) { }, { name: "valid metadata", - fileContent: `{"digest": "xxh64:testdigest", "client_version": "1.0.0"}`, + fileContent: `{"digest": "xxh64:testdigest", "source": "http://localhost:1234/archive.tar.gz", "client_version": "1.0.0"}`, expectedResult: &ImportMetadata{ Digest: "xxh64:testdigest", + Source: "http://localhost:1234/archive.tar.gz", ClientVersion: "1.0.0", }, }, @@ -106,7 +107,8 @@ func TestWriteImportMetadata(t *testing.T) { for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { var buf bytes.Buffer - claim, err := writeImportMetadata(&buf, tc.checksum) + src := "source!" + claim, err := writeImportMetadata(&buf, tc.checksum, src) tc.wantErr(t, err) if err == nil { @@ -120,6 +122,7 @@ func TestWriteImportMetadata(t *testing.T) { assert.Equal(t, tc.checksum, claim.Digest) assert.Equal(t, tc.expectedVersion, doc.ClientVersion) assert.Equal(t, tc.expectedVersion, claim.ClientVersion) + assert.Equal(t, src, doc.Source) } }) } diff --git a/grype/db/v6/installation/curator.go b/grype/db/v6/installation/curator.go index 5050c2f3fe2..f4186abad5c 100644 --- a/grype/db/v6/installation/curator.go +++ b/grype/db/v6/installation/curator.go @@ -20,6 +20,7 @@ import ( db "github.com/anchore/grype/grype/db/v6" "github.com/anchore/grype/grype/db/v6/distribution" "github.com/anchore/grype/grype/event" + "github.com/anchore/grype/grype/vulnerability" "github.com/anchore/grype/internal/bus" "github.com/anchore/grype/internal/file" "github.com/anchore/grype/internal/log" @@ -116,10 +117,19 @@ func (c curator) Reader() (db.Reader, error) { mon.Set("rehydrating DB") log.Info("rehydrating DB") + // we're not changing the source of the DB, so we just want to use any existing value. + // if the source is empty/does not exist, it will be empty in the new metadata. + var source string + im, err := db.ReadImportMetadata(c.fs, c.config.DBDirectoryPath()) + if err == nil && im != nil { + // ignore errors, as this is just a best-effort to get the source + source = im.Source + } + // this is a condition where an old client imported a DB with additional capabilities than it can handle at hydration. // this could lead to missing indexes and degraded performance now that a newer client is running (that can handle these capabilities). // the only sensible thing to do is to rehydrate the existing DB to ensure indexes are up-to-date with the current client's capabilities. - if err := c.hydrate(c.config.DBDirectoryPath(), mon); err != nil { + if err := c.hydrate(c.config.DBDirectoryPath(), source, mon); err != nil { log.WithFields("error", err).Warn("unable to rehydrate DB") } mon.Set("rehydrated") @@ -139,38 +149,45 @@ func (c curator) Reader() (db.Reader, error) { return s, nil } -func (c curator) Status() db.Status { +func (c curator) Status() vulnerability.ProviderStatus { dbFile := c.config.DBFilePath() - d, err := db.ReadDescription(dbFile) - if err != nil { - return db.Status{ - Path: dbFile, - Err: err, + d, validateErr := db.ReadDescription(dbFile) + if validateErr != nil { + return vulnerability.ProviderStatus{ + Path: dbFile, + Error: validateErr, } } if d == nil { - return db.Status{ - Path: dbFile, - Err: fmt.Errorf("database not found at %q", dbFile), + return vulnerability.ProviderStatus{ + Path: dbFile, + Error: fmt.Errorf("database not found at %q", dbFile), } } - err = c.validateAge(d) - digest, checksumErr := c.validateIntegrity(d) + validateErr = c.validateAge(d) + _, checksumErr := c.validateIntegrity(d) if checksumErr != nil && c.config.ValidateChecksum { - if err != nil { - err = errors.Join(err, checksumErr) + if validateErr != nil { + validateErr = errors.Join(validateErr, checksumErr) } else { - err = checksumErr + validateErr = checksumErr } } - return db.Status{ - Built: db.Time{Time: d.Built.Time}, + var source string + im, readErr := db.ReadImportMetadata(c.fs, c.config.DBDirectoryPath()) + if readErr == nil && im != nil { + // only make a best-effort to get the source + source = im.Source + } + + return vulnerability.ProviderStatus{ + Built: d.Built.Time, SchemaVersion: d.SchemaVersion.String(), + From: source, Path: dbFile, - Checksum: digest, - Err: err, + Error: validateErr, } } @@ -275,12 +292,12 @@ func (c curator) update(current *db.Description) (*distribution.Archive, error) log.Infof("downloading new vulnerability DB") mon.Set("downloading") - dest, err := c.client.Download(*update, filepath.Dir(c.config.DBRootDir), mon.downloadProgress.Manual) + dest, url, err := c.client.Download(*update, filepath.Dir(c.config.DBRootDir), mon.downloadProgress.Manual) if err != nil { return nil, fmt.Errorf("unable to update vulnerability database: %w", err) } mon.downloadProgress.SetCompleted() - if err = c.activate(dest, mon); err != nil { + if err = c.activate(dest, url, mon); err != nil { return nil, fmt.Errorf("unable to activate new vulnerability database: %w", err) } @@ -415,7 +432,7 @@ func (c curator) Import(path string) error { mon.downloadProgress.SetCompleted() - err = c.activate(tempDir, mon) + err = c.activate(tempDir, "manual import", mon) if err != nil { removeAllOrLog(c.fs, tempDir) return err @@ -427,10 +444,10 @@ func (c curator) Import(path string) error { } // activate swaps over the downloaded db to the application directory, calculates the checksum, and records the checksums to a file. -func (c curator) activate(dbDirPath string, mon monitor) error { +func (c curator) activate(dbDirPath, url string, mon monitor) error { defer mon.SetCompleted() - if err := c.hydrate(dbDirPath, mon); err != nil { + if err := c.hydrate(dbDirPath, url, mon); err != nil { return fmt.Errorf("failed to hydrate database: %w", err) } @@ -439,7 +456,7 @@ func (c curator) activate(dbDirPath string, mon monitor) error { return c.replaceDB(dbDirPath) } -func (c curator) hydrate(dbDirPath string, mon monitor) error { +func (c curator) hydrate(dbDirPath, from string, mon monitor) error { if c.hydrator != nil { mon.Set("hydrating") if err := c.hydrator(dbDirPath); err != nil { @@ -450,7 +467,7 @@ func (c curator) hydrate(dbDirPath string, mon monitor) error { mon.Set("hashing") - doc, err := db.WriteImportMetadata(c.fs, dbDirPath) + doc, err := db.WriteImportMetadata(c.fs, dbDirPath, from) if err != nil { return fmt.Errorf("failed to write checksums file: %w", err) } diff --git a/grype/db/v6/installation/curator_test.go b/grype/db/v6/installation/curator_test.go index e207575d9da..ef9669c93b9 100644 --- a/grype/db/v6/installation/curator_test.go +++ b/grype/db/v6/installation/curator_test.go @@ -36,9 +36,9 @@ func (m *mockClient) IsUpdateAvailable(current *db.Description) (*distribution.A return args.Get(0).(*distribution.Archive), nil } -func (m *mockClient) Download(archive distribution.Archive, dest string, downloadProgress *progress.Manual) (string, error) { +func (m *mockClient) Download(archive distribution.Archive, dest string, downloadProgress *progress.Manual) (string, string, error) { args := m.Called(archive, dest, downloadProgress) - return args.String(0), args.Error(1) + return args.String(0), "http://localhost/archive.tar.zst", args.Error(1) } func (m *mockClient) Latest() (*distribution.LatestDocument, error) { @@ -175,7 +175,7 @@ func writeTestDB(t *testing.T, fs afero.Fs, dir string) string { require.NoError(t, rw.SetDBMetadata()) require.NoError(t, rw.Close()) - doc, err := db.WriteImportMetadata(fs, dir) + doc, err := db.WriteImportMetadata(fs, dir, "source") require.NoError(t, err) require.NotNil(t, doc) diff --git a/grype/db/v6/status.go b/grype/db/v6/status.go deleted file mode 100644 index ee90524ca65..00000000000 --- a/grype/db/v6/status.go +++ /dev/null @@ -1,39 +0,0 @@ -package v6 - -import "encoding/json" - -type Status struct { - SchemaVersion string `json:"schemaVersion"` - Built Time `json:"built"` - Path string `json:"path"` - Checksum string `json:"checksum"` - Err error `json:"error"` -} - -func (s Status) Status() string { - if s.Err != nil { - return "invalid" - } - return "valid" -} - -func (s Status) MarshalJSON() ([]byte, error) { - errStr := "" - if s.Err != nil { - errStr = s.Err.Error() - } - - return json.Marshal(&struct { - SchemaVersion string `json:"schemaVersion"` - Built Time `json:"built"` - Path string `json:"path"` - Checksum string `json:"checksum"` - Err string `json:"error"` - }{ - SchemaVersion: s.SchemaVersion, - Built: s.Built, - Path: s.Path, - Checksum: s.Checksum, - Err: errStr, - }) -} diff --git a/grype/db/v6/vulnerability_provider.go b/grype/db/v6/vulnerability_provider.go index 26e6d7d9429..acf66c6dc97 100644 --- a/grype/db/v6/vulnerability_provider.go +++ b/grype/db/v6/vulnerability_provider.go @@ -5,6 +5,7 @@ import ( "fmt" "io" "strings" + "time" "github.com/hashicorp/go-multierror" "github.com/iancoleman/strcase" @@ -21,6 +22,11 @@ import ( syftPkg "github.com/anchore/syft/syft/pkg" ) +var ( + _ vulnerability.Provider = (*vulnerabilityProvider)(nil) + _ vulnerability.StoreMetadataProvider = (*vulnerabilityProvider)(nil) +) + func NewVulnerabilityProvider(rdr Reader) vulnerability.Provider { return &vulnerabilityProvider{ reader: rdr, @@ -36,11 +42,11 @@ var _ interface { } = (*vulnerabilityProvider)(nil) // Deprecated: vulnerability.Vulnerability objects now have metadata included -func (s vulnerabilityProvider) VulnerabilityMetadata(ref vulnerability.Reference) (*vulnerability.Metadata, error) { +func (vp vulnerabilityProvider) VulnerabilityMetadata(ref vulnerability.Reference) (*vulnerability.Metadata, error) { vuln, ok := ref.Internal.(*VulnerabilityHandle) if !ok { var err error - vuln, err = s.fetchVulnerability(ref) + vuln, err = vp.fetchVulnerability(ref) if err != nil { return nil, err } @@ -56,18 +62,18 @@ func (s vulnerabilityProvider) VulnerabilityMetadata(ref vulnerability.Reference }, nil } - return s.getVulnerabilityMetadata(vuln, ref.Namespace) + return vp.getVulnerabilityMetadata(vuln, ref.Namespace) } -func (s vulnerabilityProvider) getVulnerabilityMetadata(vuln *VulnerabilityHandle, namespace string) (*vulnerability.Metadata, error) { +func (vp vulnerabilityProvider) getVulnerabilityMetadata(vuln *VulnerabilityHandle, namespace string) (*vulnerability.Metadata, error) { cves := getCVEs(vuln) - kevs, err := s.fetchKnownExploited(cves) + kevs, err := vp.fetchKnownExploited(cves) if err != nil { log.WithFields("id", vuln.Name, "vulnerability", vuln.String(), "error", err).Debug("unable to fetch known exploited from vulnerability") } - epss, err := s.fetchEpss(cves) + epss, err := vp.fetchEpss(cves) if err != nil { log.WithFields("id", vuln.Name, "vulnerability", vuln.String(), "error", err).Debug("unable to fetch epss from vulnerability") } @@ -98,9 +104,29 @@ func newVulnerabilityMetadata(vuln *VulnerabilityHandle, namespace string, kevs }, nil } -func (s vulnerabilityProvider) fetchVulnerability(ref vulnerability.Reference) (*VulnerabilityHandle, error) { +func (vp vulnerabilityProvider) DataProvenance() (map[string]vulnerability.DataProvenance, error) { + providers, err := vp.reader.AllProviders() + if err != nil { + return nil, err + } + dps := make(map[string]vulnerability.DataProvenance) + + for _, p := range providers { + var date time.Time + if p.DateCaptured != nil { + date = *p.DateCaptured + } + dps[p.ID] = vulnerability.DataProvenance{ + DateCaptured: date, + InputDigest: p.InputDigest, + } + } + return dps, nil +} + +func (vp vulnerabilityProvider) fetchVulnerability(ref vulnerability.Reference) (*VulnerabilityHandle, error) { provider := strings.Split(ref.Namespace, ":")[0] - vulns, err := s.reader.GetVulnerabilities(&VulnerabilitySpecifier{Name: ref.ID, Providers: []string{provider}}, &GetVulnerabilityOptions{Preload: true}) + vulns, err := vp.reader.GetVulnerabilities(&VulnerabilitySpecifier{Name: ref.ID, Providers: []string{provider}}, &GetVulnerabilityOptions{Preload: true}) if err != nil { return nil, err } @@ -110,11 +136,11 @@ func (s vulnerabilityProvider) fetchVulnerability(ref vulnerability.Reference) ( return nil, nil } -func (s vulnerabilityProvider) fetchKnownExploited(cves []string) ([]vulnerability.KnownExploited, error) { +func (vp vulnerabilityProvider) fetchKnownExploited(cves []string) ([]vulnerability.KnownExploited, error) { var out []vulnerability.KnownExploited var errs error for _, cve := range cves { - kevs, err := s.reader.GetKnownExploitedVulnerabilities(cve) + kevs, err := vp.reader.GetKnownExploitedVulnerabilities(cve) if err != nil { errs = multierror.Append(errs, err) continue @@ -137,11 +163,11 @@ func (s vulnerabilityProvider) fetchKnownExploited(cves []string) ([]vulnerabili return out, errs } -func (s vulnerabilityProvider) fetchEpss(cves []string) ([]vulnerability.EPSS, error) { +func (vp vulnerabilityProvider) fetchEpss(cves []string) ([]vulnerability.EPSS, error) { var out []vulnerability.EPSS var errs error for _, cve := range cves { - entries, err := s.reader.GetEpss(cve) + entries, err := vp.reader.GetEpss(cve) if err != nil { errs = multierror.Append(errs, err) continue @@ -158,16 +184,16 @@ func (s vulnerabilityProvider) fetchEpss(cves []string) ([]vulnerability.EPSS, e return out, errs } -func (s vulnerabilityProvider) PackageSearchNames(p pkg.Package) []string { +func (vp vulnerabilityProvider) PackageSearchNames(p pkg.Package) []string { return name.PackageNames(p) } -func (s vulnerabilityProvider) Close() error { - return s.reader.(io.Closer).Close() +func (vp vulnerabilityProvider) Close() error { + return vp.reader.(io.Closer).Close() } //nolint:funlen,gocognit,gocyclo -func (s vulnerabilityProvider) FindVulnerabilities(criteria ...vulnerability.Criteria) ([]vulnerability.Vulnerability, error) { +func (vp vulnerabilityProvider) FindVulnerabilities(criteria ...vulnerability.Criteria) ([]vulnerability.Vulnerability, error) { if err := search.ValidateCriteria(criteria); err != nil { return nil, err } @@ -257,7 +283,7 @@ func (s vulnerabilityProvider) FindVulnerabilities(criteria ...vulnerability.Cri var affectedCPEs []AffectedCPEHandle if pkgSpec != nil || len(vulnSpecs) > 0 { - affectedPackages, err = s.reader.GetAffectedPackages(pkgSpec, &GetAffectedPackageOptions{ + affectedPackages, err = vp.reader.GetAffectedPackages(pkgSpec, &GetAffectedPackageOptions{ OSs: osSpecs, Vulnerabilities: vulnSpecs, PreloadBlob: true, @@ -273,13 +299,13 @@ func (s vulnerabilityProvider) FindVulnerabilities(criteria ...vulnerability.Cri affectedPackages = filterAffectedPackageVersions(versionMatcher, affectedPackages) // after filtering, read vulnerability data - if err = fillAffectedPackageHandles(s.reader, ptrs(affectedPackages)); err != nil { + if err = fillAffectedPackageHandles(vp.reader, ptrs(affectedPackages)); err != nil { return nil, err } } if cpeSpec != nil { - affectedCPEs, err = s.reader.GetAffectedCPEs(cpeSpec, &GetAffectedCPEOptions{ + affectedCPEs, err = vp.reader.GetAffectedCPEs(cpeSpec, &GetAffectedCPEOptions{ Vulnerabilities: vulnSpecs, PreloadBlob: true, }) @@ -290,19 +316,19 @@ func (s vulnerabilityProvider) FindVulnerabilities(criteria ...vulnerability.Cri affectedCPEs = filterAffectedCPEVersions(versionMatcher, affectedCPEs, cpeSpec) // after filtering, read vulnerability data - if err = fillAffectedCPEHandles(s.reader, ptrs(affectedCPEs)); err != nil { + if err = fillAffectedCPEHandles(vp.reader, ptrs(affectedCPEs)); err != nil { return nil, err } } // fill complete vulnerabilities for this set -- these should have already had all properties lazy loaded - vulns, err := s.toVulnerabilities(affectedPackages, affectedCPEs) + vulns, err := vp.toVulnerabilities(affectedPackages, affectedCPEs) if err != nil { return nil, err } // filter vulnerabilities by any remaining criteria such as ByQualifiedPackages - vulns, err = s.filterVulnerabilities(vulns, remainingCriteria...) + vulns, err = vp.filterVulnerabilities(vulns, remainingCriteria...) if err != nil { return nil, err } @@ -313,7 +339,7 @@ func (s vulnerabilityProvider) FindVulnerabilities(criteria ...vulnerability.Cri return out, nil } -func (s vulnerabilityProvider) filterVulnerabilities(vulns []vulnerability.Vulnerability, criteria ...vulnerability.Criteria) ([]vulnerability.Vulnerability, error) { +func (vp vulnerabilityProvider) filterVulnerabilities(vulns []vulnerability.Vulnerability, criteria ...vulnerability.Criteria) ([]vulnerability.Vulnerability, error) { isMatch := func(v vulnerability.Vulnerability) (bool, error) { for _, c := range criteria { if _, ok := c.(search.VersionConstraintMatcher); ok { @@ -348,7 +374,7 @@ func (s vulnerabilityProvider) filterVulnerabilities(vulns []vulnerability.Vulne } // toVulnerabilities takes fully-filled handles and returns all vulnerabilities from them -func (s vulnerabilityProvider) toVulnerabilities(packageHandles []AffectedPackageHandle, cpeHandles []AffectedCPEHandle) ([]vulnerability.Vulnerability, error) { //nolint:funlen,gocognit +func (vp vulnerabilityProvider) toVulnerabilities(packageHandles []AffectedPackageHandle, cpeHandles []AffectedCPEHandle) ([]vulnerability.Vulnerability, error) { //nolint:funlen,gocognit var out []vulnerability.Vulnerability metadataByCVE := make(map[string]*vulnerability.Metadata) @@ -362,7 +388,7 @@ func (s vulnerabilityProvider) toVulnerabilities(packageHandles []AffectedPackag return metadata, nil } - metadata, err := s.getVulnerabilityMetadata(vuln, namespace) + metadata, err := vp.getVulnerabilityMetadata(vuln, namespace) if err != nil { return nil, err } diff --git a/grype/load_vulnerability_db.go b/grype/load_vulnerability_db.go index ba303435b16..286fcdb1e4b 100644 --- a/grype/load_vulnerability_db.go +++ b/grype/load_vulnerability_db.go @@ -10,7 +10,7 @@ import ( "github.com/anchore/grype/internal/log" ) -func LoadVulnerabilityDB(distCfg v6dist.Config, installCfg v6inst.Config, update bool) (vulnerability.Provider, *v6.Status, error) { +func LoadVulnerabilityDB(distCfg v6dist.Config, installCfg v6inst.Config, update bool) (vulnerability.Provider, *vulnerability.ProviderStatus, error) { client, err := v6dist.NewClient(distCfg) if err != nil { return nil, nil, fmt.Errorf("unable to create distribution client: %w", err) @@ -36,8 +36,8 @@ func LoadVulnerabilityDB(distCfg v6dist.Config, installCfg v6inst.Config, update } s := c.Status() - if s.Err != nil { - return nil, nil, s.Err + if s.Error != nil { + return nil, nil, s.Error } rdr, err := c.Reader() diff --git a/grype/presenter/json/presenter.go b/grype/presenter/json/presenter.go index 03d46c67042..27ca2a912d5 100644 --- a/grype/presenter/json/presenter.go +++ b/grype/presenter/json/presenter.go @@ -4,25 +4,18 @@ import ( "encoding/json" "io" - "github.com/anchore/clio" "github.com/anchore/grype/grype/presenter/models" ) type Presenter struct { - id clio.Identification - document models.Document - appConfig interface{} - dbStatus interface{} - pretty bool + document models.Document + pretty bool } func NewPresenter(pb models.PresenterConfig) *Presenter { return &Presenter{ - id: pb.ID, - document: pb.Document, - appConfig: pb.AppConfig, - dbStatus: pb.DBStatus, - pretty: pb.Pretty, + document: pb.Document, + pretty: pb.Pretty, } } diff --git a/grype/presenter/models/descriptor.go b/grype/presenter/models/descriptor.go index 33cfb6ad2ef..e604fb1142f 100644 --- a/grype/presenter/models/descriptor.go +++ b/grype/presenter/models/descriptor.go @@ -2,9 +2,9 @@ package models // descriptor describes what created the document as well as surrounding metadata type descriptor struct { - Name string `json:"name"` - Version string `json:"version"` - Configuration interface{} `json:"configuration,omitempty"` - VulnerabilityDBStatus interface{} `json:"db,omitempty"` - Timestamp string `json:"timestamp"` + Name string `json:"name"` + Version string `json:"version"` + Configuration any `json:"configuration,omitempty"` + DB any `json:"db,omitempty"` + Timestamp string `json:"timestamp"` } diff --git a/grype/presenter/models/document.go b/grype/presenter/models/document.go index 93dbadcaada..068486d1eb1 100644 --- a/grype/presenter/models/document.go +++ b/grype/presenter/models/document.go @@ -20,7 +20,7 @@ type Document struct { } // NewDocument creates and populates a new Document struct, representing the populated JSON document. -func NewDocument(id clio.Identification, packages []pkg.Package, context pkg.Context, matches match.Matches, ignoredMatches []match.IgnoredMatch, metadataProvider vulnerability.MetadataProvider, appConfig any, dbStatus any, strategy SortStrategy) (Document, error) { +func NewDocument(id clio.Identification, packages []pkg.Package, context pkg.Context, matches match.Matches, ignoredMatches []match.IgnoredMatch, metadataProvider vulnerability.MetadataProvider, appConfig any, dbInfo any, strategy SortStrategy) (Document, error) { timestamp, timestampErr := time.Now().Local().MarshalText() if timestampErr != nil { return Document{}, timestampErr @@ -78,11 +78,11 @@ func NewDocument(id clio.Identification, packages []pkg.Package, context pkg.Con Source: src, Distro: newDistribution(context.Distro), Descriptor: descriptor{ - Name: id.Name, - Version: id.Version, - Configuration: appConfig, - VulnerabilityDBStatus: dbStatus, - Timestamp: string(timestamp), + Name: id.Name, + Version: id.Version, + Configuration: appConfig, + DB: dbInfo, + Timestamp: string(timestamp), }, }, nil } diff --git a/grype/presenter/models/presenter_bundle.go b/grype/presenter/models/presenter_bundle.go index cdd99b89cbc..106e963592c 100644 --- a/grype/presenter/models/presenter_bundle.go +++ b/grype/presenter/models/presenter_bundle.go @@ -6,10 +6,8 @@ import ( ) type PresenterConfig struct { - ID clio.Identification - Document Document - SBOM *sbom.SBOM - AppConfig interface{} - DBStatus interface{} - Pretty bool + ID clio.Identification + Document Document + SBOM *sbom.SBOM + Pretty bool } diff --git a/grype/vulnerability/provider.go b/grype/vulnerability/provider.go index 5e0743f2693..d395bcecd13 100644 --- a/grype/vulnerability/provider.go +++ b/grype/vulnerability/provider.go @@ -1,7 +1,9 @@ package vulnerability import ( + "encoding/json" "io" + "time" grypePkg "github.com/anchore/grype/grype/pkg" ) @@ -30,3 +32,63 @@ type Provider interface { io.Closer } + +type StoreMetadataProvider interface { + DataProvenance() (map[string]DataProvenance, error) +} + +type DataProvenance struct { + DateCaptured time.Time `json:"captured,omitempty"` + InputDigest string `json:"input,omitempty"` +} + +type ProviderStatus struct { + SchemaVersion string `json:"schemaVersion"` + From string `json:"from,omitempty"` + Built time.Time `json:"built,omitempty"` + Path string `json:"path,omitempty"` + Error error `json:"error,omitempty"` +} + +func (s ProviderStatus) MarshalJSON() ([]byte, error) { + errStr := "" + if s.Error != nil { + errStr = s.Error.Error() + } + + var t string + if !s.Built.IsZero() { + t = s.Built.Format(time.RFC3339) + } + + return json.Marshal(&struct { + SchemaVersion string `json:"schemaVersion"` + From string `json:"from,omitempty"` + Built string `json:"built,omitempty"` + Path string `json:"path,omitempty"` + Valid bool `json:"valid"` + Error string `json:"error,omitempty"` + }{ + SchemaVersion: s.SchemaVersion, + From: s.From, + Built: t, + Path: s.Path, + Valid: s.Error == nil, + Error: errStr, + }) +} + +func (s DataProvenance) MarshalJSON() ([]byte, error) { + var t string + if !s.DateCaptured.IsZero() { + t = s.DateCaptured.Format(time.RFC3339) + } + + return json.Marshal(&struct { + DateCaptured string `json:"captured,omitempty"` + InputDigest string `json:"input,omitempty"` + }{ + DateCaptured: t, + InputDigest: s.InputDigest, + }) +} From fc781677fd78cda3ab925bd06c64e41b8c3b6a3d Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 17 Mar 2025 14:48:39 -0400 Subject: [PATCH 016/109] import DB from URL (#2532) Signed-off-by: Alex Goodman --- cmd/grype/cli/commands/db_import.go | 12 ++-- grype/db/v6/distribution/client.go | 43 +++++++------- grype/db/v6/distribution/client_test.go | 13 +---- grype/db/v6/installation/curator.go | 72 +++++++++++++++++------- grype/db/v6/installation/curator_test.go | 10 +++- 5 files changed, 90 insertions(+), 60 deletions(-) diff --git a/cmd/grype/cli/commands/db_import.go b/cmd/grype/cli/commands/db_import.go index 0af546a208a..587ce08e281 100644 --- a/cmd/grype/cli/commands/db_import.go +++ b/cmd/grype/cli/commands/db_import.go @@ -16,9 +16,9 @@ func DBImport(app clio.Application) *cobra.Command { opts := options.DefaultDatabaseCommand(app.ID()) cmd := &cobra.Command{ - Use: "import FILE", - Short: "Import a vulnerability database or archive", - Long: fmt.Sprintf("import a vulnerability database or archive from a local FILE.\nDB archives can be obtained from %q.", opts.DB.UpdateURL), + Use: "import FILE | URL", + Short: "Import a vulnerability database or archive from a local file or URL", + Long: fmt.Sprintf("import a vulnerability database archive from a local FILE or URL.\nDB archives can be obtained from %q (or running `db list`). If the URL has a `checksum` query parameter with a fully qualified digest (e.g. 'sha256:abc728...') then the archive/DB will be verified against this value.", opts.DB.UpdateURL), Args: cobra.ExactArgs(1), RunE: func(_ *cobra.Command, args []string) error { return runDBImport(*opts, args[0]) @@ -33,7 +33,7 @@ func DBImport(app clio.Application) *cobra.Command { return app.SetupCommand(cmd, &configWrapper{opts}) } -func runDBImport(opts options.DatabaseCommand, dbArchivePath string) error { +func runDBImport(opts options.DatabaseCommand, reference string) error { // TODO: tui update? better logging? client, err := distribution.NewClient(opts.ToClientConfig()) if err != nil { @@ -44,8 +44,8 @@ func runDBImport(opts options.DatabaseCommand, dbArchivePath string) error { return fmt.Errorf("unable to create curator: %w", err) } - log.WithFields("path", dbArchivePath).Infof("importing vulnerability database archive") - if err := c.Import(dbArchivePath); err != nil { + log.WithFields("reference", reference).Infof("importing vulnerability database archive") + if err := c.Import(reference); err != nil { return fmt.Errorf("unable to import vulnerability database: %w", err) } diff --git a/grype/db/v6/distribution/client.go b/grype/db/v6/distribution/client.go index 41f9ef9916a..1f15435e4ac 100644 --- a/grype/db/v6/distribution/client.go +++ b/grype/db/v6/distribution/client.go @@ -40,7 +40,8 @@ type Config struct { type Client interface { Latest() (*LatestDocument, error) IsUpdateAvailable(current *v6.Description) (*Archive, error) - Download(archive Archive, dest string, downloadProgress *progress.Manual) (string, string, error) + ResolveArchiveURL(archive Archive) (string, error) + Download(url, dest string, downloadProgress *progress.Manual) (string, error) } type client struct { @@ -126,24 +127,11 @@ func (c client) isUpdateAvailable(current *v6.Description, candidate *LatestDocu return nil, message } -func (c client) Download(archive Archive, dest string, downloadProgress *progress.Manual) (string, string, error) { - defer downloadProgress.SetCompleted() - - if err := os.MkdirAll(dest, 0700); err != nil { - return "", "", fmt.Errorf("unable to create db download root dir: %w", err) - } - - // note: as much as I'd like to use the afero FS abstraction here, the go-getter library does not support it - tempDir, err := os.MkdirTemp(dest, "grype-db-download") - if err != nil { - return "", "", fmt.Errorf("unable to create db client temp dir: %w", err) - } - +func (c client) ResolveArchiveURL(archive Archive) (string, error) { // download the db to the temp dir u, err := url.Parse(c.latestURL()) if err != nil { - removeAllOrLog(afero.NewOsFs(), tempDir) - return "", "", fmt.Errorf("unable to parse db URL %q: %w", c.latestURL(), err) + return "", fmt.Errorf("unable to parse db URL %q: %w", c.latestURL(), err) } u.Path = path.Join(path.Dir(u.Path), path.Clean(archive.Path)) @@ -156,15 +144,30 @@ func (c client) Download(archive Archive, dest string, downloadProgress *progres } u.RawQuery = query.Encode() + return u.String(), nil +} + +func (c client) Download(archiveURL, dest string, downloadProgress *progress.Manual) (string, error) { + defer downloadProgress.SetCompleted() + + if err := os.MkdirAll(dest, 0700); err != nil { + return "", fmt.Errorf("unable to create db download root dir: %w", err) + } + + // note: as much as I'd like to use the afero FS abstraction here, the go-getter library does not support it + tempDir, err := os.MkdirTemp(dest, "grype-db-download") + if err != nil { + return "", fmt.Errorf("unable to create db client temp dir: %w", err) + } + // go-getter will automatically extract all files within the archive to the temp dir - finalURL := u.String() - err = c.dbDownloader.GetToDir(tempDir, finalURL, downloadProgress) + err = c.dbDownloader.GetToDir(tempDir, archiveURL, downloadProgress) if err != nil { removeAllOrLog(afero.NewOsFs(), tempDir) - return "", "", fmt.Errorf("unable to download db: %w", err) + return "", fmt.Errorf("unable to download db: %w", err) } - return tempDir, finalURL, nil + return tempDir, nil } // Latest loads a LatestDocument from the configured URL. diff --git a/grype/db/v6/distribution/client_test.go b/grype/db/v6/distribution/client_test.go index 24cd6e7a715..f8f72bbf1b3 100644 --- a/grype/db/v6/distribution/client_test.go +++ b/grype/db/v6/distribution/client_test.go @@ -131,10 +131,6 @@ func TestClient_Latest(t *testing.T) { func TestClient_Download(t *testing.T) { destDir := t.TempDir() - archive := &Archive{ - Path: "path/to/archive.tar.gz", - Checksum: "checksum123", - } setup := func() (Client, *mockGetter) { mg := new(mockGetter) @@ -155,10 +151,9 @@ func TestClient_Download(t *testing.T) { url := "http://localhost:8080/path/to/archive.tar.gz?checksum=checksum123" mg.On("GetToDir", mock.Anything, url, mock.Anything).Return(nil) - tempDir, actualURL, err := c.Download(*archive, destDir, &progress.Manual{}) + tempDir, err := c.Download(url, destDir, &progress.Manual{}) require.NoError(t, err) require.True(t, len(tempDir) > 0) - assert.Equal(t, url, actualURL) mg.AssertExpectations(t) }) @@ -168,11 +163,10 @@ func TestClient_Download(t *testing.T) { url := "http://localhost:8080/path/to/archive.tar.gz?checksum=checksum123" mg.On("GetToDir", mock.Anything, url, mock.Anything).Return(errors.New("download failed")) - tempDir, actualURL, err := c.Download(*archive, destDir, &progress.Manual{}) + tempDir, err := c.Download(url, destDir, &progress.Manual{}) require.Error(t, err) require.Empty(t, tempDir) require.Contains(t, err.Error(), "unable to download db") - assert.Empty(t, actualURL) mg.AssertExpectations(t) }) @@ -183,10 +177,9 @@ func TestClient_Download(t *testing.T) { mg.On("GetToDir", mock.Anything, url, mock.Anything).Return(nil) nestedPath := filepath.Join(destDir, "nested") - tempDir, actualURL, err := c.Download(*archive, nestedPath, &progress.Manual{}) + tempDir, err := c.Download(url, nestedPath, &progress.Manual{}) require.NoError(t, err) require.True(t, len(tempDir) > 0) - assert.Equal(t, url, actualURL) mg.AssertExpectations(t) }) diff --git a/grype/db/v6/installation/curator.go b/grype/db/v6/installation/curator.go index f4186abad5c..704cfafc1be 100644 --- a/grype/db/v6/installation/curator.go +++ b/grype/db/v6/installation/curator.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "regexp" "strconv" "strings" "time" @@ -290,12 +291,19 @@ func (c curator) update(current *db.Description) (*distribution.Archive, error) return nil, checkErr } - log.Infof("downloading new vulnerability DB") + log.Info("downloading new vulnerability DB") mon.Set("downloading") - dest, url, err := c.client.Download(*update, filepath.Dir(c.config.DBRootDir), mon.downloadProgress.Manual) + url, err := c.client.ResolveArchiveURL(*update) + if err != nil { + return nil, fmt.Errorf("unable to resolve vulnerability DB URL: %w", err) + } + dest, err := c.client.Download(url, filepath.Dir(c.config.DBRootDir), mon.downloadProgress.Manual) if err != nil { return nil, fmt.Errorf("unable to update vulnerability database: %w", err) } + + log.WithFields("url", url).Debug("obtained vulnerability DB archive") + mon.downloadProgress.SetCompleted() if err = c.activate(dest, url, mon); err != nil { return nil, fmt.Errorf("unable to activate new vulnerability database: %w", err) @@ -399,41 +407,57 @@ func (c curator) setLastSuccessfulUpdateCheck() { _, _ = fmt.Fprintf(fh, "%s", time.Now().UTC().Format(time.RFC3339)) } -// Import takes a DB archive file and imports it into the final DB location. -func (c curator) Import(path string) error { +// Import takes a DB file path, archive file path, or URL and imports it into the final DB location. +func (c curator) Import(reference string) error { mon := newMonitor() - mon.Set("unarchiving") + mon.Set("preparing") defer mon.SetCompleted() if err := os.MkdirAll(c.config.DBRootDir, 0700); err != nil { return fmt.Errorf("unable to create db root dir: %w", err) } - // note: the temp directory is persisted upon download/validation/activation failure to allow for investigation - tempDir, err := os.MkdirTemp(c.config.DBRootDir, fmt.Sprintf("tmp-v%v-import", db.ModelVersion)) - if err != nil { - return fmt.Errorf("unable to create db import temp dir: %w", err) - } + var tempDir, url string + if isURL(reference) { + log.Info("downloading new vulnerability DB") + mon.Set("downloading") + var err error - if strings.HasSuffix(path, ".db") { - // this is a raw DB file, copy it to the temp dir - log.Trace("copying DB") - if err := file.CopyFile(afero.NewOsFs(), path, filepath.Join(tempDir, db.VulnerabilityDBFileName)); err != nil { - return fmt.Errorf("unable to copy DB file: %w", err) + tempDir, err = c.client.Download(reference, filepath.Dir(c.config.DBRootDir), mon.downloadProgress.Manual) + if err != nil { + return fmt.Errorf("unable to update vulnerability database: %w", err) } + + url = reference } else { - // assume it is an archive - log.Trace("unarchiving DB") - err = archiver.Unarchive(path, tempDir) + // note: the temp directory is persisted upon download/validation/activation failure to allow for investigation + var err error + tempDir, err = os.MkdirTemp(c.config.DBRootDir, fmt.Sprintf("tmp-v%v-import", db.ModelVersion)) if err != nil { - return err + return fmt.Errorf("unable to create db import temp dir: %w", err) + } + + url = "manual import" + + if strings.HasSuffix(reference, ".db") { + // this is a raw DB file, copy it to the temp dir + log.Trace("copying DB") + if err := file.CopyFile(afero.NewOsFs(), reference, filepath.Join(tempDir, db.VulnerabilityDBFileName)); err != nil { + return fmt.Errorf("unable to copy DB file: %w", err) + } + } else { + // assume it is an archive + log.Info("unarchiving DB") + err := archiver.Unarchive(reference, tempDir) + if err != nil { + return err + } } } mon.downloadProgress.SetCompleted() - err = c.activate(tempDir, "manual import", mon) - if err != nil { + if err := c.activate(tempDir, url, mon); err != nil { removeAllOrLog(c.fs, tempDir) return err } @@ -443,6 +467,12 @@ func (c curator) Import(path string) error { return nil } +var urlPrefixPattern = regexp.MustCompile("^[a-zA-Z]+://") + +func isURL(reference string) bool { + return urlPrefixPattern.MatchString(reference) +} + // activate swaps over the downloaded db to the application directory, calculates the checksum, and records the checksums to a file. func (c curator) activate(dbDirPath, url string, mon monitor) error { defer mon.SetCompleted() diff --git a/grype/db/v6/installation/curator_test.go b/grype/db/v6/installation/curator_test.go index ef9669c93b9..c00a893fbfe 100644 --- a/grype/db/v6/installation/curator_test.go +++ b/grype/db/v6/installation/curator_test.go @@ -36,9 +36,13 @@ func (m *mockClient) IsUpdateAvailable(current *db.Description) (*distribution.A return args.Get(0).(*distribution.Archive), nil } -func (m *mockClient) Download(archive distribution.Archive, dest string, downloadProgress *progress.Manual) (string, string, error) { - args := m.Called(archive, dest, downloadProgress) - return args.String(0), "http://localhost/archive.tar.zst", args.Error(1) +func (m *mockClient) ResolveArchiveURL(_ distribution.Archive) (string, error) { + return "http://localhost/archive.tar.zst", nil +} + +func (m *mockClient) Download(url, dest string, downloadProgress *progress.Manual) (string, error) { + args := m.Called(url, dest, downloadProgress) + return args.String(0), args.Error(1) } func (m *mockClient) Latest() (*distribution.LatestDocument, error) { From 3b0722955113c725205a8374766ddb5d4d6e44ef Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 17 Mar 2025 15:07:09 -0400 Subject: [PATCH 017/109] feat: specify distro without version (#2534) Signed-off-by: Keith Zantow --- cmd/grype/cli/options/database_search_os.go | 3 - .../cli/options/database_search_os_test.go | 10 +-- grype/db/v6/affected_package_store.go | 33 ++-------- grype/db/v6/affected_package_store_test.go | 19 +----- .../table/__snapshots__/presenter_test.snap | 29 +++++++-- grype/presenter/table/presenter.go | 65 ++++++++++++++----- grype/presenter/table/presenter_test.go | 45 ++++++++++++- 7 files changed, 128 insertions(+), 76 deletions(-) diff --git a/cmd/grype/cli/options/database_search_os.go b/cmd/grype/cli/options/database_search_os.go index 4f2f6fd2c1d..00c073dd881 100644 --- a/cmd/grype/cli/options/database_search_os.go +++ b/cmd/grype/cli/options/database_search_os.go @@ -32,9 +32,6 @@ func (o *DBSearchOSs) PostLoad() error { if err != nil { return err } - if spec != nil { - spec.AllowMultiple = true - } specs = append(specs, spec) } o.Specs = specs diff --git a/cmd/grype/cli/options/database_search_os_test.go b/cmd/grype/cli/options/database_search_os_test.go index f4ae9af4b78..26b485fc1dc 100644 --- a/cmd/grype/cli/options/database_search_os_test.go +++ b/cmd/grype/cli/options/database_search_os_test.go @@ -27,7 +27,7 @@ func TestDBSearchOSsPostLoad(t *testing.T) { OSs: []string{"ubuntu"}, }, expectedSpecs: []*v6.OSSpecifier{ - {Name: "ubuntu", AllowMultiple: true}, + {Name: "ubuntu"}, }, }, { @@ -36,7 +36,7 @@ func TestDBSearchOSsPostLoad(t *testing.T) { OSs: []string{"ubuntu@20"}, }, expectedSpecs: []*v6.OSSpecifier{ - {Name: "ubuntu", MajorVersion: "20", AllowMultiple: true}, + {Name: "ubuntu", MajorVersion: "20"}, }, }, { @@ -45,7 +45,7 @@ func TestDBSearchOSsPostLoad(t *testing.T) { OSs: []string{"ubuntu@20.04"}, }, expectedSpecs: []*v6.OSSpecifier{ - {Name: "ubuntu", MajorVersion: "20", MinorVersion: "04", AllowMultiple: true}, + {Name: "ubuntu", MajorVersion: "20", MinorVersion: "04"}, }, }, { @@ -54,7 +54,7 @@ func TestDBSearchOSsPostLoad(t *testing.T) { OSs: []string{"ubuntu@focal"}, }, expectedSpecs: []*v6.OSSpecifier{ - {Name: "ubuntu", LabelVersion: "focal", AllowMultiple: true}, + {Name: "ubuntu", LabelVersion: "focal"}, }, }, { @@ -70,7 +70,7 @@ func TestDBSearchOSsPostLoad(t *testing.T) { OSs: []string{"ubuntu:20"}, }, expectedSpecs: []*v6.OSSpecifier{ - {Name: "ubuntu", MajorVersion: "20", AllowMultiple: true}, + {Name: "ubuntu", MajorVersion: "20"}, }, }, { diff --git a/grype/db/v6/affected_package_store.go b/grype/db/v6/affected_package_store.go index 8701c250f3e..e902ba4c6fe 100644 --- a/grype/db/v6/affected_package_store.go +++ b/grype/db/v6/affected_package_store.go @@ -4,10 +4,10 @@ import ( "errors" "fmt" "regexp" - "sort" "strings" "time" + "golang.org/x/exp/maps" "gorm.io/gorm" "gorm.io/gorm/clause" @@ -25,7 +25,6 @@ var AnyOSSpecified *OSSpecifier var AnyPackageSpecified *PackageSpecifier var ErrMissingOSIdentification = errors.New("missing OS name or codename") var ErrOSNotPresent = errors.New("OS not present") -var ErrMultipleOSMatches = errors.New("multiple OS matches found but not allowed") var ErrLimitReached = errors.New("query limit reached") type GetAffectedPackageOptions struct { @@ -100,9 +99,6 @@ type OSSpecifier struct { // LabelVersion is a string that represents a floating version (e.g. "edge" or "unstable") or is the CODENAME field in /etc/os-release (e.g. "wheezy" for debian 7) LabelVersion string - - // AllowMultiple specifies whether we intend to allow for multiple distro identities to be matched. - AllowMultiple bool } func (d *OSSpecifier) String() string { @@ -509,7 +505,7 @@ func (s *affectedPackageStore) handleVulnerabilityOptions(query *gorm.DB, config } func (s *affectedPackageStore) handleOSOptions(query *gorm.DB, configs []*OSSpecifier) (*gorm.DB, error) { - resolvedDistroMap := make(map[int64]OperatingSystem) + ids := map[int64]struct{}{} if len(configs) == 0 { configs = append(configs, AnyOSSpecified) @@ -524,15 +520,12 @@ func (s *affectedPackageStore) handleOSOptions(query *gorm.DB, configs []*OSSpec return nil, fmt.Errorf("unable to resolve distro: %w", err) } - switch { - case len(curResolvedDistros) == 0: + if len(curResolvedDistros) == 0 { return nil, ErrOSNotPresent - case len(curResolvedDistros) > 1 && !config.AllowMultiple: - return nil, ErrMultipleOSMatches } hasSpecific = true for _, d := range curResolvedDistros { - resolvedDistroMap[int64(d.ID)] = d + ids[int64(d.ID)] = struct{}{} } case config == AnyOSSpecified: // TODO: one enhancement we may want to do later is "has OS defined but is not specific" which this does NOT cover. This is "may or may not have an OS defined" which is different. @@ -546,30 +539,14 @@ func (s *affectedPackageStore) handleOSOptions(query *gorm.DB, configs []*OSSpec return nil, fmt.Errorf("cannot mix specific distro with any or none distro specifiers") } - var resolvedDistros []OperatingSystem switch { case hasAny: return query, nil case hasNone: return query.Where("operating_system_id IS NULL"), nil - case hasSpecific: - for _, d := range resolvedDistroMap { - resolvedDistros = append(resolvedDistros, d) - } - sort.Slice(resolvedDistros, func(i, j int) bool { - return resolvedDistros[i].ID < resolvedDistros[j].ID - }) } - query = query.Joins("JOIN operating_systems ON affected_package_handles.operating_system_id = operating_systems.id") - - if len(resolvedDistros) > 0 { - ids := make([]ID, len(resolvedDistros)) - for i, d := range resolvedDistros { - ids[i] = d.ID - } - query = query.Where("operating_systems.id IN ?", ids) - } + query = query.Where("affected_package_handles.operating_system_id IN ?", maps.Keys(ids)) return query, nil } diff --git a/grype/db/v6/affected_package_store_test.go b/grype/db/v6/affected_package_store_test.go index 11ebb09e65e..c8f1d8a2b39 100644 --- a/grype/db/v6/affected_package_store_test.go +++ b/grype/db/v6/affected_package_store_test.go @@ -817,29 +817,16 @@ func TestAffectedPackageStore_GetAffectedPackages(t *testing.T) { expected: []AffectedPackageHandle{*pkg2d1}, }, { - name: "distro major version only (allow multiple)", + name: "distro major version only", pkg: pkgFromName(pkg2d1.Package.Name), options: &GetAffectedPackageOptions{ OSs: []*OSSpecifier{{ - Name: "ubuntu", - MajorVersion: "20", - AllowMultiple: true, + Name: "ubuntu", + MajorVersion: "20", }}, }, expected: []AffectedPackageHandle{*pkg2d1, *pkg2d2}, }, - { - name: "distro major version only (default)", - pkg: pkgFromName(pkg2d1.Package.Name), - options: &GetAffectedPackageOptions{ - OSs: []*OSSpecifier{{ - Name: "ubuntu", - MajorVersion: "20", - AllowMultiple: false, - }}, - }, - wantErr: expectErrIs(t, ErrMultipleOSMatches), - }, { name: "distro codename", pkg: pkgFromName(pkg2d1.Package.Name), diff --git a/grype/presenter/table/__snapshots__/presenter_test.snap b/grype/presenter/table/__snapshots__/presenter_test.snap index 73149c75fee..7ea321677bd 100755 --- a/grype/presenter/table/__snapshots__/presenter_test.snap +++ b/grype/presenter/table/__snapshots__/presenter_test.snap @@ -26,11 +26,28 @@ package-2 2.2.2 deb CVE-1999-0002 Critical --- [TestDisplaysIgnoredMatches - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low -package-2 2.2.2 deb CVE-1999-0002 Critical -package-2 2.2.2 deb CVE-1999-0001 Low (suppressed) -package-2 2.2.2 deb CVE-1999-0002 Critical (suppressed) -package-2 2.2.2 deb CVE-1999-0004 Critical (suppressed by VEX) +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low +package-2 2.2.2 deb CVE-1999-0002 Critical +package-2 2.2.2 deb CVE-1999-0001 Low (suppressed) +package-2 2.2.2 deb CVE-1999-0002 Critical (suppressed) +package-2 2.2.2 deb CVE-1999-0004 Critical (suppressed by VEX) + +--- + +[TestDisplaysDistro - 1] +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low (ubuntu:2.5) +package-2 2.2.2 deb CVE-1999-0002 Critical (ubuntu:3.5) + +--- + +[TestDisplaysIgnoredMatchesAndDistro - 1] +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low (ubuntu:2.5) +package-2 2.2.2 deb CVE-1999-0002 Critical (ubuntu:3.5) +package-2 2.2.2 deb CVE-1999-0001 Low (ubuntu:2.5, suppressed) +package-2 2.2.2 deb CVE-1999-0002 Critical (ubuntu:3.5, suppressed) +package-2 2.2.2 deb CVE-1999-0004 Critical (suppressed by VEX) --- diff --git a/grype/presenter/table/presenter.go b/grype/presenter/table/presenter.go index 3e0671490e1..f936861d893 100644 --- a/grype/presenter/table/presenter.go +++ b/grype/presenter/table/presenter.go @@ -1,6 +1,7 @@ package table import ( + "fmt" "io" "strings" @@ -8,13 +9,14 @@ import ( "github.com/olekukonko/tablewriter" "github.com/scylladb/go-set/strset" + "github.com/anchore/grype/grype/db/v5/namespace/distro" "github.com/anchore/grype/grype/presenter/models" "github.com/anchore/grype/grype/vulnerability" ) const ( - appendSuppressed = " (suppressed)" - appendSuppressedVEX = " (suppressed by VEX)" + appendSuppressed = "suppressed" + appendSuppressedVEX = "suppressed by VEX" ) // Presenter is a generic struct for holding fields needed for reporting @@ -35,6 +37,7 @@ type row struct { PackageType string VulnerabilityID string Severity string + Annotation string } // NewPresenter is a *Presenter constructor @@ -80,12 +83,13 @@ func (p *Presenter) Present(output io.Writer) error { for _, row := range rs.Deduplicate() { severityColor := getSeverityColor(row.Severity) table.Rich(row.Columns(), []tablewriter.Colors{ - {}, // name - {}, // version - {}, // fix - {}, // package type - {}, // vulnerability ID - severityColor, // severity + {}, // name + {}, // version + {}, // fix + {}, // package type + {}, // vulnerability ID + severityColor, // severity + annotationColor, // annotations }) } } else { @@ -100,9 +104,22 @@ func (p *Presenter) Present(output io.Writer) error { func (p *Presenter) getRows(doc models.Document, showSuppressed bool) rows { var rs rows + multipleDistros := false + existingDistro := "" + for _, m := range doc.Matches { + if _, err := distro.FromString(m.Vulnerability.Namespace); err == nil { + if existingDistro == "" { + existingDistro = m.Vulnerability.Namespace + } else if existingDistro != m.Vulnerability.Namespace { + multipleDistros = true + break + } + } + } + // generate rows for matching vulnerabilities for _, m := range doc.Matches { - rs = append(rs, p.newRow(m, "")) + rs = append(rs, p.newRow(m, "", multipleDistros)) } // generate rows for suppressed vulnerabilities @@ -116,7 +133,7 @@ func (p *Presenter) getRows(doc models.Document, showSuppressed bool) rows { } } } - rs = append(rs, p.newRow(m.Match, msg)) + rs = append(rs, p.newRow(m.Match, msg, multipleDistros)) } } return rs @@ -126,10 +143,22 @@ func supportsColor() bool { return lipgloss.NewStyle().Foreground(lipgloss.Color("5")).Render("") != "" } -func (p *Presenter) newRow(m models.Match, severitySuffix string) row { - severity := m.Vulnerability.Severity - if severity != "" { - severity += severitySuffix +func (p *Presenter) newRow(m models.Match, severitySuffix string, showDistro bool) row { + var annotations []string + + if showDistro { + if d, err := distro.FromString(m.Vulnerability.Namespace); err == nil { + annotations = append(annotations, fmt.Sprintf("%s:%s", d.DistroType(), d.Version())) + } + } + + if severitySuffix != "" { + annotations = append(annotations, severitySuffix) + } + + annotation := "" + if len(annotations) > 0 { + annotation = fmt.Sprintf("(%s)", strings.Join(annotations, ", ")) } return row{ @@ -138,7 +167,8 @@ func (p *Presenter) newRow(m models.Match, severitySuffix string) row { Fix: p.formatFix(m), PackageType: string(m.Artifact.Type), VulnerabilityID: m.Vulnerability.ID, - Severity: severity, + Severity: m.Vulnerability.Severity, + Annotation: annotation, } } @@ -174,6 +204,9 @@ func (p *Presenter) formatFix(m models.Match) string { } func (r row) Columns() []string { + if r.Annotation != "" { + return []string{r.Name, r.Version, r.Fix, r.PackageType, r.VulnerabilityID, r.Severity, r.Annotation} + } return []string{r.Name, r.Version, r.Fix, r.PackageType, r.VulnerabilityID, r.Severity} } @@ -229,3 +262,5 @@ func getSeverityColor(severity string) tablewriter.Colors { return tablewriter.Colors{severityFontType, severityColor} } + +var annotationColor = tablewriter.Colors{tablewriter.FgWhiteColor} diff --git a/grype/presenter/table/presenter_test.go b/grype/presenter/table/presenter_test.go index bdd93f97bde..2f7394b18f8 100644 --- a/grype/presenter/table/presenter_test.go +++ b/grype/presenter/table/presenter_test.go @@ -76,14 +76,14 @@ func TestCreateRow(t *testing.T) { name: "create row for suppressed vulnerability", match: match1, severitySuffix: appendSuppressed, - expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "1.0.2, *2.0.1, 3.0.4", string(match1.Artifact.Type), match1.Vulnerability.ID, "Low (suppressed)"}, + expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "1.0.2, *2.0.1, 3.0.4", string(match1.Artifact.Type), match1.Vulnerability.ID, "Low", "(suppressed)"}, }, } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { p := NewPresenter(models.PresenterConfig{}, false) - row := p.newRow(testCase.match, testCase.severitySuffix) + row := p.newRow(testCase.match, testCase.severitySuffix, false) cols := rows{row}.Render()[0] assert.Equal(t, testCase.expectedRow, cols) @@ -175,6 +175,45 @@ func TestDisplaysIgnoredMatches(t *testing.T) { snaps.MatchSnapshot(t, actual) } +func TestDisplaysDistro(t *testing.T) { + var buffer bytes.Buffer + pb := models.PresenterConfig{ + Document: internal.GenerateAnalysisWithIgnoredMatches(t, internal.ImageSource), + } + + pb.Document.Matches[0].Vulnerability.Namespace = "ubuntu:distro:ubuntu:2.5" + pb.Document.Matches[1].Vulnerability.Namespace = "ubuntu:distro:ubuntu:3.5" + + pres := NewPresenter(pb, false) + + err := pres.Present(&buffer) + require.NoError(t, err) + + actual := buffer.String() + snaps.MatchSnapshot(t, actual) +} + +func TestDisplaysIgnoredMatchesAndDistro(t *testing.T) { + var buffer bytes.Buffer + pb := models.PresenterConfig{ + Document: internal.GenerateAnalysisWithIgnoredMatches(t, internal.ImageSource), + } + + pb.Document.Matches[0].Vulnerability.Namespace = "ubuntu:distro:ubuntu:2.5" + pb.Document.Matches[1].Vulnerability.Namespace = "ubuntu:distro:ubuntu:3.5" + + pb.Document.IgnoredMatches[0].Vulnerability.Namespace = "ubuntu:distro:ubuntu:2.5" + pb.Document.IgnoredMatches[1].Vulnerability.Namespace = "ubuntu:distro:ubuntu:3.5" + + pres := NewPresenter(pb, true) + + err := pres.Present(&buffer) + require.NoError(t, err) + + actual := buffer.String() + snaps.MatchSnapshot(t, actual) +} + func TestRowsRender(t *testing.T) { t.Run("empty rows returns empty slice", func(t *testing.T) { @@ -261,7 +300,7 @@ func createTestRow(name, version, fix, pkgType, vulnID, severity string, fixStat } p := NewPresenter(models.PresenterConfig{}, false) - r := p.newRow(m, "") + r := p.newRow(m, "", false) return r, nil } From 9158dcf79041e83ef515757709cfbd1edf62e59d 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, 17 Mar 2025 20:17:33 +0000 Subject: [PATCH 018/109] chore(deps): update anchore dependencies (#2533) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com> --- go.mod | 43 +++++++++++++++--------------- go.sum | 84 ++++++++++++++++++++++++++++++---------------------------- 2 files changed, 65 insertions(+), 62 deletions(-) diff --git a/go.mod b/go.mod index e0cb40c8226..a289bcc4364 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/anchore/grype -go 1.24.0 +go 1.24.1 require ( github.com/CycloneDX/cyclonedx-go v0.9.2 @@ -16,8 +16,8 @@ require ( github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 - github.com/anchore/stereoscope v0.0.13 - github.com/anchore/syft v1.20.0 + github.com/anchore/stereoscope v0.1.0 + github.com/anchore/syft v1.21.0 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 @@ -55,7 +55,7 @@ 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.2-0.20230802210424-5b0b94c5c0d3 - github.com/spf13/afero v1.12.0 + github.com/spf13/afero v1.14.0 github.com/spf13/cobra v1.9.1 github.com/stretchr/testify v1.10.0 github.com/ulikunitz/xz v0.5.12 @@ -120,24 +120,24 @@ require ( github.com/charmbracelet/x/ansi v0.8.0 // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/cloudflare/circl v1.3.8 // indirect + github.com/cloudflare/circl v1.6.0 // indirect github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/containerd/cgroups v1.1.0 // indirect - github.com/containerd/containerd v1.7.24 // indirect - github.com/containerd/containerd/api v1.7.19 // indirect - github.com/containerd/continuity v0.4.2 // indirect + github.com/containerd/containerd v1.7.26 // indirect + github.com/containerd/containerd/api v1.8.0 // indirect + github.com/containerd/continuity v0.4.4 // indirect github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/fifo v1.1.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect - github.com/containerd/ttrpc v1.2.5 // indirect + github.com/containerd/ttrpc v1.2.7 // indirect github.com/containerd/typeurl/v2 v2.1.1 // indirect - github.com/cyphar/filepath-securejoin v0.3.6 // indirect + github.com/cyphar/filepath-securejoin v0.4.1 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/cli v27.5.0+incompatible // indirect + github.com/docker/cli v28.0.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-connections v0.5.0 // indirect @@ -159,13 +159,13 @@ require ( github.com/glebarez/go-sqlite v1.21.2 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect - github.com/go-git/go-git/v5 v5.13.2 // indirect + github.com/go-git/go-git/v5 v5.14.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-restruct/restruct v1.2.0-alpha // indirect github.com/goccy/go-yaml v1.15.13 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/licensecheck v0.3.1 // indirect github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect @@ -185,7 +185,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 // indirect github.com/kevinburke/ssh_config v1.2.0 // indirect - github.com/klauspost/compress v1.17.11 // indirect + github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/knqyf263/go-rpmdb v0.1.1 // indirect github.com/kr/pretty v0.3.1 // indirect @@ -200,7 +200,6 @@ require ( github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect - github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect @@ -218,14 +217,14 @@ require ( github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nwaples/rardecode v1.1.3 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/opencontainers/selinux v1.11.0 // indirect github.com/package-url/packageurl-go v0.1.1 // indirect github.com/pborman/indent v1.2.1 // indirect github.com/pelletier/go-toml v1.9.5 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect - github.com/pierrec/lz4/v4 v4.1.21 // indirect + github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pkg/profile v1.7.0 // indirect @@ -233,7 +232,8 @@ require ( github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect + github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect github.com/saferwall/pe v1.5.6 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect @@ -242,8 +242,9 @@ require ( github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/skeema/knownhosts v1.3.0 // indirect + github.com/skeema/knownhosts v1.3.1 // indirect github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb // indirect github.com/spdx/tools-golang v0.5.5 // indirect github.com/spf13/cast v1.7.0 // indirect github.com/spf13/pflag v1.0.6 // indirect @@ -251,7 +252,7 @@ require ( github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/sylabs/sif/v2 v2.20.2 // indirect - github.com/sylabs/squashfs v1.0.4 // indirect + github.com/sylabs/squashfs v1.0.5 // indirect github.com/therootcompany/xz v1.0.1 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -297,7 +298,7 @@ require ( modernc.org/libc v1.61.13 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.8.2 // indirect - modernc.org/sqlite v1.35.0 // indirect + modernc.org/sqlite v1.36.1 // indirect ) // this is a breaking change, so we need to pin the version until glebarez/go-sqlite is updated to use internal/libc diff --git a/go.sum b/go.sum index e85a4a86e44..1d97ca85244 100644 --- a/go.sum +++ b/go.sum @@ -702,10 +702,10 @@ github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= -github.com/anchore/stereoscope v0.0.13 h1:9Ivkh7k+vOeG3JHrt44jOg/8UdZrCvMsSjLQ7trHBig= -github.com/anchore/stereoscope v0.0.13/go.mod h1:QfhhFc2pezp5aX/dVJ5qnBFpBUv5+KUTphwaQLxMUig= -github.com/anchore/syft v1.20.0 h1:4nVM/eiqrb2GJCkW+d1xv8M5mxply8vVblpWOvVCgN8= -github.com/anchore/syft v1.20.0/go.mod h1:h8U0q+Fk7f1d9ay4oa+gDb//AJYFuQftrBLOuS6llz4= +github.com/anchore/stereoscope v0.1.0 h1:waF2C5b/XRAyShKup3VoJz8Pdv7AiDxUbABTizmJdJ0= +github.com/anchore/stereoscope v0.1.0/go.mod h1:3vasimie0IJOXvMbMpjwvwIHBDA1+192QZDWNhJRqFQ= +github.com/anchore/syft v1.21.0 h1:JHmYOnEbCJsElROCCfg+3oIODw1LQLfXGkIrmXNZYsI= +github.com/anchore/syft v1.21.0/go.mod h1:8i8Yp/MiSOdqID0+6eiwE9bOJWM7fEBYitINZyr2G6s= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -801,8 +801,8 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38 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= -github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI= -github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU= +github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -821,12 +821,12 @@ github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8E github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= 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/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA= -github.com/containerd/containerd v1.7.24/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= -github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA= -github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= -github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= -github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/containerd v1.7.26 h1:3cs8K2RHlMQaPifLqgRyI4VBkoldNdEw62cb7qQga7k= +github.com/containerd/containerd v1.7.26/go.mod h1:m4JU0E+h0ebbo9yXD7Hyt+sWnc8tChm7MudCjj4jRvQ= +github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= +github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= +github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII= +github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4= github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= @@ -837,8 +837,8 @@ github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpS github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= -github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU= -github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= +github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -846,8 +846,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= -github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= +github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo= github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -859,8 +859,8 @@ github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4i github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM= -github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs= +github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 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 v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= @@ -882,8 +882,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 v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM= -github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ= +github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= +github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY= github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs= github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= @@ -959,8 +959,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0= -github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A= +github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60= +github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= 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= @@ -1000,8 +1000,9 @@ github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -1217,8 +1218,8 @@ github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU= @@ -1288,8 +1289,6 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4 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= -github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5 h1:tQRHcLQwnwrPq2j2Qra/NnyjyESBGwdeBeVdAE9kXYg= -github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5/go.mod h1:vYT9HE7WCvL64iVeZylKmCsWKfE+JZ8105iuh2Trk8g= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -1354,8 +1353,8 @@ github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY= 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 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= @@ -1382,8 +1381,8 @@ github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2 github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU= +github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= @@ -1428,9 +1427,11 @@ github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6L github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c h1:8gOLsYwaY2JwlTMT4brS5/9XJdrdIbmk2obvQ748CC0= +github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c/go.mod h1:kwM/7r/rVluTE8qJbHAffduuqmSv4knVQT2IajGvSiA= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= @@ -1468,19 +1469,20 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY= -github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M= +github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= +github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb h1:bLo8hvc8XFm9J47r690TUKBzcjSWdJDxmjXJZ+/f92U= github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM= github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk= github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE= 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.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= @@ -1519,8 +1521,8 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8 github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/sylabs/sif/v2 v2.20.2 h1:HGEPzauCHhIosw5o6xmT3jczuKEuaFzSfdjAsH33vYw= github.com/sylabs/sif/v2 v2.20.2/go.mod h1:WyYryGRaR4Wp21SAymm5pK0p45qzZCSRiZMFvUZiuhc= -github.com/sylabs/squashfs v1.0.4 h1:uFSw7WXv7zjutPvU+JzY0nY494Vw8s4FAf4+7DhoMdI= -github.com/sylabs/squashfs v1.0.4/go.mod h1:PDgf8YmCntvN4d9Y8hBUBDCZL6qZOzOQwRGxnIdbERk= +github.com/sylabs/squashfs v1.0.5 h1:KExbrvScUVk/drh6gPfnZY7T7SZ+cQpcSJEOK32dppU= +github.com/sylabs/squashfs v1.0.5/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE= github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo= github.com/terminalstatic/go-xsd-validate v0.1.6/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= @@ -2441,8 +2443,8 @@ modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.35.0 h1:yQps4fegMnZFdphtzlfQTCNBWtS0CZv48pRpW3RFHRw= -modernc.org/sqlite v1.35.0/go.mod h1:9cr2sicr7jIaWTBKQmAxQLfBv9LL0su4ZTEV+utt3ic= +modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ= +modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= From 402067e958a4fa9d20384752351d6c54b0436ba1 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 17 Mar 2025 23:10:59 -0400 Subject: [PATCH 019/109] chore: attach snapshot build artifacts to each workflow (#2526) Signed-off-by: Keith Zantow --- .github/workflows/validations.yaml | 59 ++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index b3b540e69d8..575a3a93c8e 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -139,6 +139,46 @@ jobs: path: snapshot key: snapshot-build-${{ github.run_id }} + Upload-Snapshot-Artifacts: + name: "Upload snapshot artifacts" + needs: [Build-Snapshot-Artifacts] + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 + + - name: Download snapshot build + uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + with: + path: snapshot + key: snapshot-build-${{ github.run_id }} + + - run: npm install @actions/artifact@2.2.2 + + - uses: actions/github-script@v7 + with: + script: | + const { readdirSync } = require('fs') + const { DefaultArtifactClient } = require('@actions/artifact') + const artifact = new DefaultArtifactClient() + const ls = d => readdirSync(d, { withFileTypes: true }) + const baseDir = "./snapshot" + const dirs = ls(baseDir).filter(f => f.isDirectory()).map(f => f.name) + const uploads = [] + for (const dir of dirs) { + // uploadArtifact returns Promise<{id, size}> + uploads.push(artifact.uploadArtifact( + // name of the archive: + `${dir}`, + // array of all files to include: + ls(`${baseDir}/${dir}`).map(f => `${baseDir}/${dir}/${f.name}`), + // base directory to trim from entries: + `${baseDir}/${dir}`, + { retentionDays: 30 } + )) + } + // wait for all uploads to finish + Promise.all(uploads) + Acceptance-Linux: # Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline name: "Acceptance tests (Linux)" @@ -224,3 +264,22 @@ jobs: - name: Run CLI Tests (Linux) run: make cli + + Cleanup-Cache: + name: "Cleanup snapshot cache" + if: github.event.pull_request.head.repo.full_name == github.repository + runs-on: ubuntu-24.04 + permissions: + actions: write + needs: + - Acceptance-Linux + - Acceptance-Mac + - Cli-Linux + - Upload-Snapshot-Artifacts + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 + + - name: Delete snapshot cache + run: gh cache delete "snapshot-build-${{ github.run_id }}" + env: + GH_TOKEN: ${{ github.token }} From ee6d91d8e0d3d8254d54d78398237724ba9f7f66 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Wed, 19 Mar 2025 08:53:05 -0400 Subject: [PATCH 020/109] add v5 namespace emulation to db search output (#2539) Signed-off-by: Alex Goodman --- .../internal/dbsearch/affected_packages.go | 18 +- .../dbsearch/affected_packages_test.go | 22 +- .../cli/commands/internal/dbsearch/matches.go | 16 +- .../commands/internal/dbsearch/versions.go | 3 +- grype/db/v6/vulnerability.go | 1 + schema/grype/db-search/json/schema-1.0.2.json | 529 ++++++++++++++++++ .../grype/db-search/json/schema-latest.json | 9 +- 7 files changed, 575 insertions(+), 23 deletions(-) create mode 100644 schema/grype/db-search/json/schema-1.0.2.json diff --git a/cmd/grype/cli/commands/internal/dbsearch/affected_packages.go b/cmd/grype/cli/commands/internal/dbsearch/affected_packages.go index cc5190dd0e7..17af2ba64d7 100644 --- a/cmd/grype/cli/commands/internal/dbsearch/affected_packages.go +++ b/cmd/grype/cli/commands/internal/dbsearch/affected_packages.go @@ -33,6 +33,10 @@ type AffectedPackageInfo struct { // CPE is a Common Platform Enumeration that is affected by the vulnerability CPE *CPE `json:"cpe,omitempty"` + // Namespace is a holdover value from the v5 DB schema that combines provider and search methods into a single value + // Deprecated: this field will be removed in a later version of the search schema + Namespace string `json:"namespace"` + // Detail is the detailed information about the affected package Detail v6.AffectedPackageBlob `json:"detail"` } @@ -110,10 +114,11 @@ func newAffectedPackageRows(affectedPkgs []affectedPackageWithDecorations, affec rows = append(rows, AffectedPackage{ Vulnerability: newVulnerabilityInfo(*pkg.Vulnerability, pkg.vulnerabilityDecorations), AffectedPackageInfo: AffectedPackageInfo{ - Model: &pkg.AffectedPackageHandle, - OS: toOS(pkg.OperatingSystem), - Package: toPackage(pkg.Package), - Detail: detail, + Model: &pkg.AffectedPackageHandle, + OS: toOS(pkg.OperatingSystem), + Package: toPackage(pkg.Package), + Namespace: v6.MimicV5Namespace(pkg.Vulnerability, &pkg.AffectedPackageHandle), + Detail: detail, }, }) } @@ -138,8 +143,9 @@ func newAffectedPackageRows(affectedPkgs []affectedPackageWithDecorations, affec // tracking model information is not possible with CPE handles Vulnerability: newVulnerabilityInfo(*ac.Vulnerability, ac.vulnerabilityDecorations), AffectedPackageInfo: AffectedPackageInfo{ - CPE: c, - Detail: detail, + CPE: c, + Namespace: v6.MimicV5Namespace(ac.Vulnerability, nil), // no affected package will default to NVD + Detail: detail, }, }) } diff --git a/cmd/grype/cli/commands/internal/dbsearch/affected_packages_test.go b/cmd/grype/cli/commands/internal/dbsearch/affected_packages_test.go index b67b32d2e3f..aa34e73a384 100644 --- a/cmd/grype/cli/commands/internal/dbsearch/affected_packages_test.go +++ b/cmd/grype/cli/commands/internal/dbsearch/affected_packages_test.go @@ -49,8 +49,9 @@ func TestAffectedPackageTableRowMarshalJSON(t *testing.T) { }, }, AffectedPackageInfo: AffectedPackageInfo{ - Package: &Package{Name: "pkg1", Ecosystem: "ecosystem1"}, - CPE: &CPE{Part: "a", Vendor: "vendor1", Product: "product1"}, + Package: &Package{Name: "pkg1", Ecosystem: "ecosystem1"}, + CPE: &CPE{Part: "a", Vendor: "vendor1", Product: "product1"}, + Namespace: "namespace1", Detail: v6.AffectedPackageBlob{ CVEs: []string{"CVE-1234-5678"}, Qualifiers: &v6.AffectedPackageQualifiers{ @@ -120,6 +121,7 @@ func TestAffectedPackageTableRowMarshalJSON(t *testing.T) { "ecosystem": "ecosystem1" }, "cpe": "cpe:2.3:a:vendor1:product1:*:*:*:*:*:*", + "namespace": "namespace1", "detail": { "cves": [ "CVE-1234-5678" @@ -301,8 +303,9 @@ func TestNewAffectedPackageRows(t *testing.T) { }, }, AffectedPackageInfo: AffectedPackageInfo{ - OS: &OperatingSystem{Name: "Linux", Version: "5.10"}, - Package: &Package{Name: "pkg1", Ecosystem: "ecosystem1"}, + OS: &OperatingSystem{Name: "Linux", Version: "5.10"}, + Package: &Package{Name: "pkg1", Ecosystem: "ecosystem1"}, + Namespace: "provider1:distro:Linux:5.10", Detail: v6.AffectedPackageBlob{ CVEs: []string{"CVE-1234-5678"}, Qualifiers: &v6.AffectedPackageQualifiers{ @@ -352,7 +355,8 @@ func TestNewAffectedPackageRows(t *testing.T) { }, }, AffectedPackageInfo: AffectedPackageInfo{ - CPE: &CPE{Part: "a", Vendor: "vendor1", Product: "product1"}, + CPE: &CPE{Part: "a", Vendor: "vendor1", Product: "product1"}, + Namespace: "nvd:cpe", Detail: v6.AffectedPackageBlob{ CVEs: []string{"CVE-9876-5432"}, Ranges: []v6.AffectedRange{ @@ -535,8 +539,9 @@ func TestAffectedPackages(t *testing.T) { }, }, AffectedPackageInfo: AffectedPackageInfo{ - OS: &OperatingSystem{Name: "Linux", Version: "5.10"}, - Package: &Package{Name: "pkg1", Ecosystem: "ecosystem1"}, + OS: &OperatingSystem{Name: "Linux", Version: "5.10"}, + Package: &Package{Name: "pkg1", Ecosystem: "ecosystem1"}, + Namespace: "provider1:distro:Linux:5.10", Detail: v6.AffectedPackageBlob{ CVEs: []string{"CVE-1234-5678"}, Ranges: []v6.AffectedRange{ @@ -582,7 +587,8 @@ func TestAffectedPackages(t *testing.T) { }, }, AffectedPackageInfo: AffectedPackageInfo{ - CPE: &CPE{Part: "a", Vendor: "vendor1", Product: "product1"}, + CPE: &CPE{Part: "a", Vendor: "vendor1", Product: "product1"}, + Namespace: "nvd:cpe", Detail: v6.AffectedPackageBlob{ CVEs: []string{"CVE-9876-5432"}, Ranges: []v6.AffectedRange{ diff --git a/cmd/grype/cli/commands/internal/dbsearch/matches.go b/cmd/grype/cli/commands/internal/dbsearch/matches.go index 8343adb1107..0e3aba8399f 100644 --- a/cmd/grype/cli/commands/internal/dbsearch/matches.go +++ b/cmd/grype/cli/commands/internal/dbsearch/matches.go @@ -41,7 +41,7 @@ func (m Matches) Flatten() []AffectedPackage { return rows } -func newMatchesRows(affectedPkgs []affectedPackageWithDecorations, affectedCPEs []affectedCPEWithDecorations) (rows []Match, retErr error) { +func newMatchesRows(affectedPkgs []affectedPackageWithDecorations, affectedCPEs []affectedCPEWithDecorations) (rows []Match, retErr error) { // nolint:funlen var affectedPkgsByVuln = make(map[v6.ID][]AffectedPackageInfo) var vulnsByID = make(map[v6.ID]v6.VulnerabilityHandle) var decorationsByID = make(map[v6.ID]vulnerabilityDecorations) @@ -62,10 +62,11 @@ func newMatchesRows(affectedPkgs []affectedPackageWithDecorations, affectedCPEs } aff := AffectedPackageInfo{ - Model: &pkg.AffectedPackageHandle, - OS: toOS(pkg.OperatingSystem), - Package: toPackage(pkg.Package), - Detail: detail, + Model: &pkg.AffectedPackageHandle, + OS: toOS(pkg.OperatingSystem), + Package: toPackage(pkg.Package), + Namespace: v6.MimicV5Namespace(pkg.Vulnerability, &pkg.AffectedPackageHandle), + Detail: detail, } affectedPkgsByVuln[pkg.Vulnerability.ID] = append(affectedPkgsByVuln[pkg.Vulnerability.ID], aff) @@ -94,8 +95,9 @@ func newMatchesRows(affectedPkgs []affectedPackageWithDecorations, affectedCPEs aff := AffectedPackageInfo{ // tracking model information is not possible with CPE handles - CPE: c, - Detail: detail, + CPE: c, + Namespace: v6.MimicV5Namespace(ac.Vulnerability, nil), // no affected package will default to NVD + Detail: detail, } affectedPkgsByVuln[ac.Vulnerability.ID] = append(affectedPkgsByVuln[ac.Vulnerability.ID], aff) diff --git a/cmd/grype/cli/commands/internal/dbsearch/versions.go b/cmd/grype/cli/commands/internal/dbsearch/versions.go index 292114f4b6e..fd795136133 100644 --- a/cmd/grype/cli/commands/internal/dbsearch/versions.go +++ b/cmd/grype/cli/commands/internal/dbsearch/versions.go @@ -2,11 +2,12 @@ package dbsearch const ( // MatchesSchemaVersion is the schema version for the `db search ` command - MatchesSchemaVersion = "1.0.1" + MatchesSchemaVersion = "1.0.2" // MatchesSchemaVersion Changelog: // 1.0.0 - Initial schema 🎉 // 1.0.1 - Add KEV and EPSS data to vulnerability matches + // 1.0.2 - Add v5 namespace emulation for affected packages // VulnerabilitiesSchemaVersion is the schema version for the `db search vuln` command VulnerabilitiesSchemaVersion = "1.0.1" diff --git a/grype/db/v6/vulnerability.go b/grype/db/v6/vulnerability.go index 336d7bb2222..e0c93772415 100644 --- a/grype/db/v6/vulnerability.go +++ b/grype/db/v6/vulnerability.go @@ -229,6 +229,7 @@ func MimicV5Namespace(vuln *VulnerabilityHandle, affected *AffectedPackageHandle return fmt.Sprintf("%s:distro:%s:%s", pr, family, ver) } + // this shouldn't happen and is not a valid v5 namespace, but some information is better than none return vuln.Provider.ID } diff --git a/schema/grype/db-search/json/schema-1.0.2.json b/schema/grype/db-search/json/schema-1.0.2.json new file mode 100644 index 00000000000..365c6583310 --- /dev/null +++ b/schema/grype/db-search/json/schema-1.0.2.json @@ -0,0 +1,529 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "anchore.io/schema/grype/db-search/json/1.0.2/matches", + "$ref": "#/$defs/Matches", + "$defs": { + "AffectedPackageBlob": { + "$defs": { + "cves": { + "description": "is a list of Common Vulnerabilities and Exposures (CVE) identifiers related to this vulnerability." + }, + "qualifiers": { + "description": "are package attributes that confirm the package is affected by the vulnerability." + }, + "ranges": { + "description": "specifies the affected version ranges and fixes if available." + } + }, + "properties": { + "cves": { + "items": { + "type": "string" + }, + "type": "array" + }, + "qualifiers": { + "$ref": "#/$defs/AffectedPackageQualifiers" + }, + "ranges": { + "items": { + "$ref": "#/$defs/AffectedRange" + }, + "type": "array" + } + }, + "type": "object" + }, + "AffectedPackageInfo": { + "$defs": { + "cpe": { + "description": "is a Common Platform Enumeration that is affected by the vulnerability" + }, + "detail": { + "description": "is the detailed information about the affected package" + }, + "namespace": { + "description": "is a holdover value from the v5 DB schema that combines provider and search methods into a single value\nDeprecated: this field will be removed in a later version of the search schema" + }, + "os": { + "description": "identifies the operating system release that the affected package is released for" + }, + "package": { + "description": "identifies the name of the package in a specific ecosystem affected by the vulnerability" + } + }, + "properties": { + "os": { + "$ref": "#/$defs/OperatingSystem" + }, + "package": { + "$ref": "#/$defs/Package" + }, + "cpe": { + "$ref": "#/$defs/CPE" + }, + "namespace": { + "type": "string" + }, + "detail": { + "$ref": "#/$defs/AffectedPackageBlob" + } + }, + "type": "object", + "required": [ + "namespace", + "detail" + ] + }, + "AffectedPackageQualifiers": { + "$defs": { + "platform_cpes": { + "description": "lists Common Platform Enumeration (CPE) identifiers for affected platforms." + }, + "rpm_modularity": { + "description": "indicates if the package follows RPM modularity for versioning." + } + }, + "properties": { + "rpm_modularity": { + "type": "string" + }, + "platform_cpes": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object" + }, + "AffectedRange": { + "$defs": { + "fix": { + "description": "provides details on the fix version and its state if available." + }, + "version": { + "description": "defines the version constraints for affected software." + } + }, + "properties": { + "version": { + "$ref": "#/$defs/AffectedVersion" + }, + "fix": { + "$ref": "#/$defs/Fix" + } + }, + "type": "object" + }, + "AffectedVersion": { + "$defs": { + "constraint": { + "description": "defines the version range constraint for affected versions." + }, + "type": { + "description": "specifies the versioning system used (e.g., 'semver', 'rpm')." + } + }, + "properties": { + "type": { + "type": "string" + }, + "constraint": { + "type": "string" + } + }, + "type": "object" + }, + "CPE": { + "properties": { + "ID": { + "type": "integer" + }, + "Part": { + "type": "string" + }, + "Vendor": { + "type": "string" + }, + "Product": { + "type": "string" + }, + "Edition": { + "type": "string" + }, + "Language": { + "type": "string" + }, + "SoftwareEdition": { + "type": "string" + }, + "TargetHardware": { + "type": "string" + }, + "TargetSoftware": { + "type": "string" + }, + "Other": { + "type": "string" + }, + "Packages": { + "items": { + "$ref": "#/$defs/Package" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "ID", + "Part", + "Vendor", + "Product", + "Edition", + "Language", + "SoftwareEdition", + "TargetHardware", + "TargetSoftware", + "Other", + "Packages" + ] + }, + "EPSS": { + "properties": { + "cve": { + "type": "string" + }, + "epss": { + "type": "number" + }, + "percentile": { + "type": "number" + }, + "date": { + "type": "string" + } + }, + "type": "object", + "required": [ + "cve", + "epss", + "percentile", + "date" + ] + }, + "Fix": { + "$defs": { + "detail": { + "description": "provides additional fix information, such as commit details." + }, + "state": { + "description": "represents the status of the fix (e.g., 'fixed', 'unaffected')." + }, + "version": { + "description": "is the version number of the fix." + } + }, + "properties": { + "version": { + "type": "string" + }, + "state": { + "type": "string" + }, + "detail": { + "$ref": "#/$defs/FixDetail" + } + }, + "type": "object" + }, + "FixDetail": { + "$defs": { + "git_commit": { + "description": "is the identifier for the Git commit associated with the fix." + }, + "references": { + "description": "contains URLs or identifiers for additional resources on the fix." + }, + "timestamp": { + "description": "is the date and time when the fix was committed." + } + }, + "properties": { + "git_commit": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "references": { + "items": { + "$ref": "#/$defs/Reference" + }, + "type": "array" + } + }, + "type": "object" + }, + "KnownExploited": { + "properties": { + "cve": { + "type": "string" + }, + "vendor_project": { + "type": "string" + }, + "product": { + "type": "string" + }, + "date_added": { + "type": "string" + }, + "required_action": { + "type": "string" + }, + "due_date": { + "type": "string" + }, + "known_ransomware_campaign_use": { + "type": "string" + }, + "notes": { + "type": "string" + }, + "urls": { + "items": { + "type": "string" + }, + "type": "array" + }, + "cwes": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "cve", + "known_ransomware_campaign_use" + ] + }, + "Match": { + "$defs": { + "packages": { + "description": "is the list of packages affected by the vulnerability." + }, + "vulnerability": { + "description": "is the core advisory record for a single known vulnerability from a specific provider." + } + }, + "properties": { + "vulnerability": { + "$ref": "#/$defs/VulnerabilityInfo" + }, + "packages": { + "items": { + "$ref": "#/$defs/AffectedPackageInfo" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "vulnerability", + "packages" + ] + }, + "Matches": { + "items": { + "$ref": "#/$defs/Match" + }, + "type": "array" + }, + "OperatingSystem": { + "properties": { + "name": { + "type": "string" + }, + "version": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "version" + ] + }, + "Package": { + "properties": { + "name": { + "type": "string" + }, + "ecosystem": { + "type": "string" + } + }, + "type": "object", + "required": [ + "name", + "ecosystem" + ] + }, + "Reference": { + "$defs": { + "tags": { + "description": "is a free-form organizational field to convey additional information about the reference" + }, + "url": { + "description": "is the external resource" + } + }, + "properties": { + "url": { + "type": "string" + }, + "tags": { + "items": { + "type": "string" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "url" + ] + }, + "Severity": { + "$defs": { + "rank": { + "description": "is a free-form organizational field to convey priority over other severities" + }, + "scheme": { + "description": "describes the quantitative method used to determine the Score, such as 'CVSS_V3'. Alternatively this makes\nclaim that Value is qualitative, for example 'HML' (High, Medium, Low), CHMLN (critical-high-medium-low-negligible)" + }, + "source": { + "description": "is the name of the source of the severity score (e.g. 'nvd@nist.gov' or 'security-advisories@github.com')" + }, + "value": { + "description": "is the severity score (e.g. '7.5', 'CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:L/VI:L/VA:N/SC:N/SI:N/SA:N', or 'high' )" + } + }, + "properties": { + "scheme": { + "type": "string" + }, + "value": true, + "source": { + "type": "string" + }, + "rank": { + "type": "integer" + } + }, + "type": "object", + "required": [ + "scheme", + "value", + "rank" + ] + }, + "VulnerabilityInfo": { + "$defs": { + "epss": { + "description": "is a list of Exploit Prediction Scoring System (EPSS) scores for the vulnerability" + }, + "known_exploited": { + "description": "is a list of known exploited vulnerabilities from the CISA KEV dataset" + }, + "modified_date": { + "description": "is the date the vulnerability record was last modified" + }, + "provider": { + "description": "is the upstream data processor (usually Vunnel) that is responsible for vulnerability records. Each provider\nshould be scoped to a specific vulnerability dataset, for instance, the 'ubuntu' provider for all records from\nCanonicals' Ubuntu Security Notices (for all Ubuntu distro versions)." + }, + "published_date": { + "description": "is the date the vulnerability record was first published" + }, + "status": { + "description": "conveys the actionability of the current record (one of 'active', 'analyzing', 'rejected', 'disputed')" + }, + "withdrawn_date": { + "description": "is the date the vulnerability record was withdrawn" + } + }, + "properties": { + "id": { + "type": "string" + }, + "assigner": { + "items": { + "type": "string" + }, + "type": "array" + }, + "description": { + "type": "string" + }, + "refs": { + "items": { + "$ref": "#/$defs/Reference" + }, + "type": "array" + }, + "aliases": { + "items": { + "type": "string" + }, + "type": "array" + }, + "severities": { + "items": { + "$ref": "#/$defs/Severity" + }, + "type": "array" + }, + "provider": { + "type": "string" + }, + "status": { + "type": "string" + }, + "published_date": { + "type": "string", + "format": "date-time" + }, + "modified_date": { + "type": "string", + "format": "date-time" + }, + "withdrawn_date": { + "type": "string", + "format": "date-time" + }, + "known_exploited": { + "items": { + "$ref": "#/$defs/KnownExploited" + }, + "type": "array" + }, + "epss": { + "items": { + "$ref": "#/$defs/EPSS" + }, + "type": "array" + } + }, + "type": "object", + "required": [ + "id", + "provider", + "status" + ] + } + } +} diff --git a/schema/grype/db-search/json/schema-latest.json b/schema/grype/db-search/json/schema-latest.json index d1ddb779dd3..365c6583310 100644 --- a/schema/grype/db-search/json/schema-latest.json +++ b/schema/grype/db-search/json/schema-latest.json @@ -1,6 +1,6 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "anchore.io/schema/grype/db-search/json/1.0.1/matches", + "$id": "anchore.io/schema/grype/db-search/json/1.0.2/matches", "$ref": "#/$defs/Matches", "$defs": { "AffectedPackageBlob": { @@ -42,6 +42,9 @@ "detail": { "description": "is the detailed information about the affected package" }, + "namespace": { + "description": "is a holdover value from the v5 DB schema that combines provider and search methods into a single value\nDeprecated: this field will be removed in a later version of the search schema" + }, "os": { "description": "identifies the operating system release that the affected package is released for" }, @@ -59,12 +62,16 @@ "cpe": { "$ref": "#/$defs/CPE" }, + "namespace": { + "type": "string" + }, "detail": { "$ref": "#/$defs/AffectedPackageBlob" } }, "type": "object", "required": [ + "namespace", "detail" ] }, From 213600f1813c1ddee8208dc8905d12dec91e652f Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Wed, 19 Mar 2025 15:12:09 -0400 Subject: [PATCH 021/109] fix: panic and improve non-db-v6 error messaging (#2546) Signed-off-by: Keith Zantow --- grype/db/v6/db.go | 4 +++- grype/db/v6/db_metadata_store_test.go | 11 +++++++++++ grype/db/v6/store.go | 11 +++++------ grype/db/v6/store_test.go | 22 ++++++++++++++++++++++ 4 files changed, 41 insertions(+), 7 deletions(-) diff --git a/grype/db/v6/db.go b/grype/db/v6/db.go index 795fd4c6295..43dba7a33f0 100644 --- a/grype/db/v6/db.go +++ b/grype/db/v6/db.go @@ -102,7 +102,9 @@ func Hydrater() func(string) error { // we don't pass any data initialization here because the data is already in the db archive and we do not want // to affect the entries themselves, only indexes and schema. s, err := newStore(Config{DBDirPath: path}, false, true) - log.CloseAndLogError(s, path) + if s != nil { + log.CloseAndLogError(s, path) + } return err } } diff --git a/grype/db/v6/db_metadata_store_test.go b/grype/db/v6/db_metadata_store_test.go index 8eb6ef12b21..048f007b26b 100644 --- a/grype/db/v6/db_metadata_store_test.go +++ b/grype/db/v6/db_metadata_store_test.go @@ -19,6 +19,17 @@ func TestDbMetadataStore_empty(t *testing.T) { require.NotNil(t, actualMetadata) } +func TestDbMetadataStore_oldDb(t *testing.T) { + db := setupTestStore(t).db + require.NoError(t, db.Where("true").Model(DBMetadata{}).Update("Model", "5").Error) // old database version + s := newDBMetadataStore(db) + + // attempt to fetch a non-existent record + actualMetadata, err := s.GetDBMetadata() + require.NoError(t, err) + require.NotNil(t, actualMetadata) +} + func TestDbMetadataStore(t *testing.T) { s := newDBMetadataStore(setupTestStore(t).db) diff --git a/grype/db/v6/store.go b/grype/db/v6/store.go index dfc39def34c..8a39bb5c678 100644 --- a/grype/db/v6/store.go +++ b/grype/db/v6/store.go @@ -65,17 +65,16 @@ func newStore(cfg Config, empty, writable bool) (*store, error) { } meta, err := metadataStore.GetDBMetadata() - if err != nil { + if err != nil || meta == nil || meta.Model != ModelVersion { // db.Close must be called, or we will get stale reads d, _ := db.DB() if d != nil { _ = d.Close() } - return nil, fmt.Errorf("failed to get db metadata: %w", err) - } - - if meta == nil { - return nil, fmt.Errorf("no DB metadata found") + if err != nil { + return nil, fmt.Errorf("not a v%d database: %w", ModelVersion, err) + } + return nil, fmt.Errorf("not a v%d database", ModelVersion) } dbVersion := newSchemaVerFromDBMetadata(*meta) diff --git a/grype/db/v6/store_test.go b/grype/db/v6/store_test.go index b508d6155a1..16261061a6f 100644 --- a/grype/db/v6/store_test.go +++ b/grype/db/v6/store_test.go @@ -1,10 +1,12 @@ package v6 import ( + "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "gorm.io/gorm" ) func TestStoreClose(t *testing.T) { @@ -54,3 +56,23 @@ func TestStoreClose(t *testing.T) { assert.Empty(t, indexes) }) } + +func Test_oldDbV5(t *testing.T) { + s := setupTestStore(t) + require.NoError(t, s.db.Where("true").Delete(&DBMetadata{}).Error) // delete all existing records + require.NoError(t, s.Close()) + s, err := newStore(s.config, false, true) + require.Nil(t, s) + require.ErrorIs(t, err, gorm.ErrRecordNotFound) + require.ErrorContains(t, err, fmt.Sprintf("not a v%d database", ModelVersion)) +} + +func Test_oldDbWithMetadata(t *testing.T) { + s := setupTestStore(t) + require.NoError(t, s.db.Where("true").Model(DBMetadata{}).Update("Model", "5").Error) // old database version + require.NoError(t, s.Close()) + s, err := newStore(s.config, false, true) + require.Nil(t, s) + require.NotErrorIs(t, err, gorm.ErrRecordNotFound) + require.ErrorContains(t, err, fmt.Sprintf("not a v%d database", ModelVersion)) +} From 4f9df87ddef22d515acd2370fdca9d3c4256211e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 09:53:24 -0400 Subject: [PATCH 022/109] chore(deps): bump actions/setup-go from 5.3.0 to 5.4.0 (#2545) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/f111f3307d8850f501ac008e886eec1fd1932a34...0aaccfd150d50ccaeb58ebd88d36e91967a5f35b) --- updated-dependencies: - dependency-name: actions/setup-go 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 08c339bdfc8..4483d1a265e 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: ${{ runner.os }}-go- - name: Set correct version of Golang to use during CodeQL run - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 with: go-version: '1.21' check-latest: true From 7f3bd0e266594573c656cfcfe97f5e5f05ae4110 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 09:53:34 -0400 Subject: [PATCH 023/109] chore(deps): bump github/codeql-action from 3.28.11 to 3.28.12 (#2544) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.11 to 3.28.12. - [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/6bb031afdd8eb862ea3fc1848194185e076637e5...5f8171a638ada777af81d42b55959a643bb29017) --- 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 | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4483d1a265e..f4829b3f5e0 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -63,7 +63,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 + uses: github/codeql-action/init@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -85,4 +85,4 @@ jobs: run: make grype - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.28.11 + uses: github/codeql-action/analyze@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index bbe27042998..3f1200015f9 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -38,6 +38,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@6bb031afdd8eb862ea3fc1848194185e076637e5 # v1.0.26 + uses: github/codeql-action/upload-sarif@5f8171a638ada777af81d42b55959a643bb29017 # v1.0.26 with: sarif_file: results.sarif From 26f78893d3939b79bf42e624002ac19bf71be347 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 09:53:56 -0400 Subject: [PATCH 024/109] chore(deps): bump actions/setup-go in /.github/actions/bootstrap (#2543) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.3.0 to 5.4.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/f111f3307d8850f501ac008e886eec1fd1932a34...0aaccfd150d50ccaeb58ebd88d36e91967a5f35b) --- updated-dependencies: - dependency-name: actions/setup-go 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/actions/bootstrap/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index 150284da53d..a9d13cfd262 100644 --- a/.github/actions/bootstrap/action.yaml +++ b/.github/actions/bootstrap/action.yaml @@ -32,7 +32,7 @@ runs: using: "composite" steps: # note: go mod and build is automatically cached on default with v4+ - - uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 + - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 if: inputs.go-version != '' with: go-version: ${{ inputs.go-version }} From fe28e84f38db8bc39d994c2b888d9b5b67d49d46 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Mar 2025 09:54:21 -0400 Subject: [PATCH 025/109] chore(deps): bump github.com/containerd/containerd from 1.7.26 to 1.7.27 (#2535) Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.26 to 1.7.27. - [Release notes](https://github.com/containerd/containerd/releases) - [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md) - [Commits](https://github.com/containerd/containerd/compare/v1.7.26...v1.7.27) --- updated-dependencies: - dependency-name: github.com/containerd/containerd dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index a289bcc4364..5fa30375662 100644 --- a/go.mod +++ b/go.mod @@ -123,7 +123,7 @@ require ( github.com/cloudflare/circl v1.6.0 // indirect github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/containerd/cgroups v1.1.0 // indirect - github.com/containerd/containerd v1.7.26 // indirect + github.com/containerd/containerd v1.7.27 // indirect github.com/containerd/containerd/api v1.8.0 // indirect github.com/containerd/continuity v0.4.4 // indirect github.com/containerd/errdefs v0.3.0 // indirect diff --git a/go.sum b/go.sum index 1d97ca85244..e00289b270c 100644 --- a/go.sum +++ b/go.sum @@ -821,8 +821,8 @@ github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8E github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= 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/containerd v1.7.26 h1:3cs8K2RHlMQaPifLqgRyI4VBkoldNdEw62cb7qQga7k= -github.com/containerd/containerd v1.7.26/go.mod h1:m4JU0E+h0ebbo9yXD7Hyt+sWnc8tChm7MudCjj4jRvQ= +github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII= +github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0= github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0= github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII= @@ -1414,8 +1414,8 @@ 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.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= 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= From 2686c1ffbe0519a2eb52a60ed464f515ef2b7ade 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, 20 Mar 2025 09:54:57 -0400 Subject: [PATCH 026/109] chore(deps): update tools to latest versions (#2536) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.binny.yaml b/.binny.yaml index ef56a9c84ee..05aaddf030b 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -26,7 +26,7 @@ tools: # used for linting - name: golangci-lint version: - want: v1.64.7 + want: v1.64.8 method: github-release with: repo: golangci/golangci-lint @@ -98,7 +98,7 @@ tools: # used for triggering a release - name: gh version: - want: v2.68.1 + want: v2.69.0 method: github-release with: repo: cli/cli From f4ea17241161afc4782663eb819d62684cbc1560 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 20 Mar 2025 13:21:26 -0400 Subject: [PATCH 027/109] remove mitchellh dependencies (#2550) Signed-off-by: Alex Goodman --- go.mod | 57 ++++++++------- go.sum | 96 +++++++++++++++----------- grype/db/v5/pkg/qualifier/from_json.go | 2 +- grype/match/details.go | 4 +- grype/match/fingerprint.go | 4 +- grype/pkg/package.go | 15 +++- grype/pkg/package_test.go | 30 ++++++++ grype/pkg/purl_provider.go | 2 +- grype/pkg/syft_sbom_provider.go | 2 +- grype/presenter/template/presenter.go | 2 +- internal/format/writer.go | 2 +- 11 files changed, 140 insertions(+), 76 deletions(-) diff --git a/go.mod b/go.mod index 5fa30375662..c7bf8621426 100644 --- a/go.mod +++ b/go.mod @@ -10,27 +10,30 @@ require ( github.com/adrg/xdg v0.5.3 github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 - github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837 + github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716 github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 - github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a + github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d + github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 - github.com/anchore/stereoscope v0.1.0 - github.com/anchore/syft v1.21.0 + github.com/anchore/stereoscope v0.1.1-0.20250320125929-d35c173d28fc + github.com/anchore/syft v1.21.1-0.20250320141945-410b85e1c959 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 github.com/charmbracelet/bubbletea v1.3.4 github.com/charmbracelet/lipgloss v1.1.0 github.com/dave/jennifer v1.7.1 - github.com/docker/docker v28.0.1+incompatible + github.com/docker/docker v28.0.2+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 github.com/gabriel-vasile/mimetype v1.4.8 github.com/gkampitakis/go-snaps v0.5.11 github.com/glebarez/sqlite v1.11.0 github.com/go-test/deep v1.1.1 + github.com/go-viper/mapstructure/v2 v2.2.1 + github.com/gohugoio/hashstructure v0.5.0 github.com/google/go-cmp v0.7.0 github.com/google/go-containerregistry v0.20.3 github.com/google/uuid v1.6.0 @@ -45,9 +48,7 @@ require ( github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08 - github.com/mitchellh/go-homedir v1.1.0 - github.com/mitchellh/hashstructure/v2 v2.0.2 - github.com/mitchellh/mapstructure v1.5.0 + github.com/muesli/termenv v0.16.0 github.com/olekukonko/tablewriter v0.0.5 github.com/openvex/go-vex v0.2.5 github.com/owenrumney/go-sarif v1.1.2-0.20231003122901-1000f5e05554 @@ -70,10 +71,6 @@ require ( gorm.io/gorm v1.25.12 ) -require github.com/DataDog/zstd v1.5.5 // indirect - -require github.com/muesli/termenv v0.16.0 - require ( cel.dev/expr v0.16.1 // indirect cloud.google.com/go v0.116.0 // indirect @@ -86,7 +83,8 @@ require ( dario.cat/mergo v1.0.1 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect + github.com/DataDog/zstd v1.5.5 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect @@ -96,9 +94,10 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.11.7 // indirect github.com/ProtonMail/go-crypto v1.1.5 // indirect + github.com/STARRY-S/zip v0.2.1 // indirect github.com/acobaugh/osrelease v0.1.0 // indirect github.com/agext/levenshtein v1.2.1 // indirect - github.com/anchore/fangs v0.0.0-20241014225144-4e1713cafd77 // indirect + github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2 // indirect github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/andybalholm/brotli v1.1.1 // indirect @@ -110,7 +109,11 @@ require ( github.com/becheran/wildmatch-go v1.0.0 // indirect github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef // indirect + github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect github.com/bmatcuk/doublestar/v4 v4.8.1 // indirect + github.com/bodgit/plumbing v1.3.0 // indirect + github.com/bodgit/sevenzip v1.6.0 // indirect + github.com/bodgit/windows v1.0.1 // indirect github.com/buger/jsonparser v1.1.1 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -143,7 +146,7 @@ require ( github.com/docker/go-connections v0.5.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/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/elliotchance/phpserialize v1.4.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect @@ -152,11 +155,11 @@ require ( github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/felixge/fgprof v0.9.5 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/github/go-spdx/v2 v2.3.2 // indirect github.com/gkampitakis/ciinfo v0.3.1 // indirect github.com/gkampitakis/go-diff v1.3.2 // indirect - github.com/glebarez/go-sqlite v1.21.2 // indirect + github.com/glebarez/go-sqlite v1.22.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect github.com/go-git/go-git/v5 v5.14.0 // indirect @@ -174,7 +177,7 @@ require ( github.com/googleapis/gax-go/v2 v2.14.1 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-safetemp v1.0.0 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl/v2 v2.23.0 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect @@ -192,7 +195,6 @@ require ( github.com/kr/text v0.2.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/magiconair/properties v1.8.9 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/maruel/natural v1.1.1 // indirect github.com/mattn/go-colorable v0.1.13 // indirect @@ -200,9 +202,11 @@ require ( github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect + github.com/mholt/archives v0.1.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect - github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // indirect + github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect @@ -216,6 +220,7 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nwaples/rardecode v1.1.3 // indirect + github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect @@ -223,7 +228,7 @@ require ( github.com/package-url/packageurl-go v0.1.1 // indirect github.com/pborman/indent v1.2.1 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pierrec/lz4/v4 v4.1.22 // indirect github.com/pjbgf/sha1cd v0.3.2 // indirect github.com/pkg/errors v0.9.1 // indirect @@ -235,20 +240,20 @@ require ( github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect github.com/saferwall/pe v1.5.6 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/sassoftware/go-rpmutils v0.4.0 // indirect github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect + github.com/sorairolake/lzip-go v0.3.5 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb // indirect github.com/spdx/tools-golang v0.5.5 // indirect - github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.19.0 // indirect + github.com/spf13/viper v1.20.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/sylabs/sif/v2 v2.20.2 // indirect @@ -278,6 +283,7 @@ require ( go.opentelemetry.io/otel/trace v1.33.0 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect + go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/mod v0.24.0 // indirect golang.org/x/net v0.37.0 // indirect @@ -293,7 +299,6 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect google.golang.org/grpc v1.67.3 // indirect google.golang.org/protobuf v1.36.3 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect modernc.org/libc v1.61.13 // indirect modernc.org/mathutil v1.7.1 // indirect diff --git a/go.sum b/go.sum index e00289b270c..05212b42ae0 100644 --- a/go.sum +++ b/go.sum @@ -629,8 +629,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/CycloneDX/cyclonedx-go v0.9.2 h1:688QHn2X/5nRezKe2ueIVCt+NRqf7fl3AVQk+vaFcIo= github.com/CycloneDX/cyclonedx-go v0.9.2/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg= @@ -664,6 +664,8 @@ github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8 github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg= +github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4= 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= @@ -684,14 +686,16 @@ github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 h1:yhk+P8lF3 github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51/go.mod h1:nwuGSd7aZp0rtYt79YggCGafz1RYsclE7pi3fhLwvuw= github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 h1:p0ZIe0htYOX284Y4axJaGBvXHU0VCCzLN5Wf5XbKStU= github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9/go.mod h1:3ZsFB9tzW3vl4gEiUeuSOMDnwroWxIxJelOOHUp8dSw= -github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837 h1:bIG3WsfosZsJ5LMC7PB9J/ekFM3a0j0ZEDvN3ID6GTI= -github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837/go.mod h1:tRQVKkjYeejrh9AdM0s1esbwtMU7rdHAHSQWkv4qskE= -github.com/anchore/fangs v0.0.0-20241014225144-4e1713cafd77 h1:h7+GCqazHVS5GDJYYS6wjjglYi8xFnVWMdSUukoImTM= -github.com/anchore/fangs v0.0.0-20241014225144-4e1713cafd77/go.mod h1:qbev5czQeyDO74fPNThiEKYkgt0mx1axb+5wQcxDPFY= +github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716 h1:2sIdYJlQESEnyk3Y0WD2vXWW5eD2iMz9Ev8fj1Z8LNA= +github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716/go.mod h1:Utb9i4kwiCWvqAIxZaJeMIXFO9uOgQXlvH2BfbfO/zI= +github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2 h1:GC2QaO0YsmjpsZ4rtVKv9DnproIxqqn+qkskpc+i8MA= +github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2/go.mod h1:XUbUECwVKuD3qYRUj+QZIOHjyyXua2gFmVjKA40iHXA= github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 h1:GjNGuwK5jWjJMyVppBjYS54eOiiSNv4Ba869k4wh72Q= github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8= -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-homedir v0.0.0-20250319154043-c29668562e4d h1:gT69osH9AsdpOfqxbRwtxcNnSZ1zg4aKy2BevO3ZBdc= +github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d/go.mod h1:PhSnuFYknwPZkOWKB1jXBNToChBA+l0FjwOxtViIc50= +github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 h1:2SqmFgE7h+Ql4VyBzhjLkRF/3gDrcpUBj8LjvvO6OOM= +github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722/go.mod h1:oFuE8YuTCM+spgMXhePGzk3asS94yO9biUfDzVTFqNw= 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= @@ -702,10 +706,10 @@ github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= -github.com/anchore/stereoscope v0.1.0 h1:waF2C5b/XRAyShKup3VoJz8Pdv7AiDxUbABTizmJdJ0= -github.com/anchore/stereoscope v0.1.0/go.mod h1:3vasimie0IJOXvMbMpjwvwIHBDA1+192QZDWNhJRqFQ= -github.com/anchore/syft v1.21.0 h1:JHmYOnEbCJsElROCCfg+3oIODw1LQLfXGkIrmXNZYsI= -github.com/anchore/syft v1.21.0/go.mod h1:8i8Yp/MiSOdqID0+6eiwE9bOJWM7fEBYitINZyr2G6s= +github.com/anchore/stereoscope v0.1.1-0.20250320125929-d35c173d28fc h1:wyiNipBKQApiCX56jeDNTrnkHddF4laJwze6ldYr+ZA= +github.com/anchore/stereoscope v0.1.1-0.20250320125929-d35c173d28fc/go.mod h1:o4Gy3h1KLi27+v53O58whB9yCqcrgGdhthRc3st4whk= +github.com/anchore/syft v1.21.1-0.20250320141945-410b85e1c959 h1:yywFcavCivMcW5Bjn7wO9XW+9D6GbLt6TRhdErn+KSs= +github.com/anchore/syft v1.21.1-0.20250320141945-410b85e1c959/go.mod h1:xQpHtL2mfQvMcEf8PqR/sbUlpcvaIlv3f/m2ETEooas= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -750,10 +754,18 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef h1:TSFnfbbu2oAOuWbeDDTtwXWE6z+PmpgbSsMBeV7l0ww= github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4= +github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI= github.com/bmatcuk/doublestar/v2 v2.0.4 h1:6I6oUiT/sU27eE2OFcWqBhL1SwjyvQuOssxT4a1yidI= github.com/bmatcuk/doublestar/v2 v2.0.4/go.mod h1:QMmcs3H2AUQICWhfzLXz+IYln8lRQmTZRptLie8RgRw= github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38= github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= +github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU= +github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs= +github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A= +github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc= +github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4= +github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M= @@ -863,8 +875,8 @@ github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 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 v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= -github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8= +github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -874,8 +886,8 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6Uezg 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/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY= -github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= +github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= +github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -929,8 +941,8 @@ github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzP github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= +github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -942,8 +954,8 @@ github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZ github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk= github.com/gkampitakis/go-snaps v0.5.11 h1:LFG0ggUKR+KEiiaOvFCmLgJ5NO2zf93AxxddkBn3LdQ= github.com/gkampitakis/go-snaps v0.5.11/go.mod h1:PcKmy8q5Se7p48ywpogN5Td13reipz1Iivah4wrTIvY= -github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo= -github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k= +github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ= +github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc= github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw= github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= @@ -983,6 +995,8 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U= github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= @@ -993,6 +1007,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x 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= +github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg= +github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= @@ -1160,7 +1176,8 @@ github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos= github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= @@ -1289,6 +1306,8 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4 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= +github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q= +github.com/mholt/archives v0.1.0/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= @@ -1302,15 +1321,11 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= -github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= -github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= +github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -1347,6 +1362,8 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= +github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 h1:MYzLheyVx1tJVDqfu3YnN4jtnyALNzLvwl+f58TcvQY= +github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= 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.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= @@ -1375,8 +1392,8 @@ github.com/pborman/indent v1.2.1/go.mod h1:FitS+t35kIYtB5xWTZAPhnmrxcciEEOdbyrrp github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= @@ -1434,14 +1451,13 @@ github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c h1:8 github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c/go.mod h1:kwM/7r/rVluTE8qJbHAffduuqmSv4knVQT2IajGvSiA= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/saferwall/pe v1.5.6 h1:DrRLnoQFxHWJ5lJUmrH7X2L0xeUu6SUS95Dc61eW2Yc= github.com/saferwall/pe v1.5.6/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= @@ -1471,6 +1487,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8= github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY= +github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg= +github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= @@ -1484,8 +1502,8 @@ github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcD github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= -github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4= github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= @@ -1494,8 +1512,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY= +github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -1637,6 +1655,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= +go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= @@ -2368,8 +2388,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV gopkg.in/cheggaaa/pb.v1 v1.0.27/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/grype/db/v5/pkg/qualifier/from_json.go b/grype/db/v5/pkg/qualifier/from_json.go index a06e76dc64f..dbb661ee4af 100644 --- a/grype/db/v5/pkg/qualifier/from_json.go +++ b/grype/db/v5/pkg/qualifier/from_json.go @@ -3,7 +3,7 @@ package qualifier import ( "encoding/json" - "github.com/mitchellh/mapstructure" + "github.com/go-viper/mapstructure/v2" "github.com/anchore/grype/grype/db/v5/pkg/qualifier/platformcpe" "github.com/anchore/grype/grype/db/v5/pkg/qualifier/rpmmodularity" diff --git a/grype/match/details.go b/grype/match/details.go index aeeabad98b2..5c7ac53fbe3 100644 --- a/grype/match/details.go +++ b/grype/match/details.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/mitchellh/hashstructure/v2" + "github.com/gohugoio/hashstructure" ) type Details []Detail @@ -43,7 +43,7 @@ func (m Details) Types() (tys []Type) { } func (m Detail) ID() string { - f, err := hashstructure.Hash(&m, hashstructure.FormatV2, &hashstructure.HashOptions{ + f, err := hashstructure.Hash(&m, &hashstructure.HashOptions{ ZeroNil: true, SlicesAsSets: true, }) diff --git a/grype/match/fingerprint.go b/grype/match/fingerprint.go index d4950ee65c3..cec830fcd09 100644 --- a/grype/match/fingerprint.go +++ b/grype/match/fingerprint.go @@ -3,7 +3,7 @@ package match import ( "fmt" - "github.com/mitchellh/hashstructure/v2" + "github.com/gohugoio/hashstructure" "github.com/anchore/grype/grype/pkg" ) @@ -24,7 +24,7 @@ func (m Fingerprint) String() string { } func (m Fingerprint) ID() string { - f, err := hashstructure.Hash(&m, hashstructure.FormatV2, &hashstructure.HashOptions{ + f, err := hashstructure.Hash(&m, &hashstructure.HashOptions{ ZeroNil: true, SlicesAsSets: true, }) diff --git a/grype/pkg/package.go b/grype/pkg/package.go index 5d3a0594f44..58ea7c8dd2d 100644 --- a/grype/pkg/package.go +++ b/grype/pkg/package.go @@ -204,6 +204,8 @@ func dataFromPkg(p syftPkg.Package) (interface{}, []UpstreamPackage) { metadata = golangMetadataFromPkg(p) case syftPkg.DpkgDBEntry: upstreams = dpkgDataFromPkg(p) + case syftPkg.DpkgArchiveEntry: + upstreams = dpkgDataFromPkg(p) case syftPkg.RpmArchive, syftPkg.RpmDBEntry: m, u := rpmDataFromPkg(p) upstreams = u @@ -277,16 +279,25 @@ func golangMetadataFromPkg(p syftPkg.Package) interface{} { } func dpkgDataFromPkg(p syftPkg.Package) (upstreams []UpstreamPackage) { - if value, ok := p.Metadata.(syftPkg.DpkgDBEntry); ok { + switch value := p.Metadata.(type) { + case syftPkg.DpkgDBEntry: if value.Source != "" { upstreams = append(upstreams, UpstreamPackage{ Name: value.Source, Version: value.SourceVersion, }) } - } else { + case syftPkg.DpkgArchiveEntry: + if value.Source != "" { + upstreams = append(upstreams, UpstreamPackage{ + Name: value.Source, + Version: value.SourceVersion, + }) + } + default: log.Warnf("unable to extract DPKG metadata for %s", p) } + return upstreams } diff --git a/grype/pkg/package_test.go b/grype/pkg/package_test.go index 0c02886b312..cd6192be3ea 100644 --- a/grype/pkg/package_test.go +++ b/grype/pkg/package_test.go @@ -68,6 +68,36 @@ func TestNew(t *testing.T) { }, }, }, + { + name: "dpkg archive with source info", + syftPkg: syftPkg.Package{ + Metadata: syftPkg.DpkgArchiveEntry{ + Package: "pkg-info", + Source: "src-info", + Version: "version-info", + SourceVersion: "src-version-info", + Architecture: "arch-info", + Maintainer: "maintainer-info", + InstalledSize: 10, + Files: []syftPkg.DpkgFileRecord{ + { + Path: "path-info", + Digest: &file.Digest{ + Algorithm: "algo-info", + Value: "digest-info", + }, + IsConfigFile: true, + }, + }, + }, + }, + upstreams: []UpstreamPackage{ + { + Name: "src-info", + Version: "src-version-info", + }, + }, + }, { name: "rpm archive with source info", syftPkg: syftPkg.Package{ diff --git a/grype/pkg/purl_provider.go b/grype/pkg/purl_provider.go index 692a3b10ba9..2b8f14f7496 100644 --- a/grype/pkg/purl_provider.go +++ b/grype/pkg/purl_provider.go @@ -7,9 +7,9 @@ import ( "os" "strings" - "github.com/mitchellh/go-homedir" "github.com/scylladb/go-set/strset" + "github.com/anchore/go-homedir" "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/linux" diff --git a/grype/pkg/syft_sbom_provider.go b/grype/pkg/syft_sbom_provider.go index 056742a94a9..4c7b88ba7a1 100644 --- a/grype/pkg/syft_sbom_provider.go +++ b/grype/pkg/syft_sbom_provider.go @@ -9,8 +9,8 @@ import ( "strings" "github.com/gabriel-vasile/mimetype" - "github.com/mitchellh/go-homedir" + "github.com/anchore/go-homedir" "github.com/anchore/grype/internal" "github.com/anchore/grype/internal/log" "github.com/anchore/syft/syft/format" diff --git a/grype/presenter/template/presenter.go b/grype/presenter/template/presenter.go index bf61c055b80..402b92017c5 100644 --- a/grype/presenter/template/presenter.go +++ b/grype/presenter/template/presenter.go @@ -8,9 +8,9 @@ import ( "text/template" "github.com/Masterminds/sprig/v3" - "github.com/mitchellh/go-homedir" "github.com/anchore/clio" + "github.com/anchore/go-homedir" "github.com/anchore/grype/grype/presenter/models" ) diff --git a/internal/format/writer.go b/internal/format/writer.go index a55351a77e4..03e65652ff4 100644 --- a/internal/format/writer.go +++ b/internal/format/writer.go @@ -9,8 +9,8 @@ import ( "strings" "github.com/hashicorp/go-multierror" - "github.com/mitchellh/go-homedir" + "github.com/anchore/go-homedir" "github.com/anchore/grype/grype/presenter/models" "github.com/anchore/grype/internal/bus" "github.com/anchore/grype/internal/log" From 1afca68c41d561058b745d93b0c3830999bf6777 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 20 Mar 2025 16:49:18 -0400 Subject: [PATCH 028/109] Fix db list url (#2553) * fix db list URL Signed-off-by: Alex Goodman * db list should fill out DB url Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- cmd/grype/cli/commands/db_list.go | 26 ++++++++++++++++-------- cmd/grype/cli/commands/db_list_test.go | 28 +++++++++++++++++++++----- 2 files changed, 41 insertions(+), 13 deletions(-) diff --git a/cmd/grype/cli/commands/db_list.go b/cmd/grype/cli/commands/db_list.go index f20ecd84f6b..3be6a4a9d7b 100644 --- a/cmd/grype/cli/commands/db_list.go +++ b/cmd/grype/cli/commands/db_list.go @@ -6,7 +6,6 @@ import ( "io" "net/url" "os" - "path" "github.com/spf13/cobra" @@ -62,28 +61,39 @@ func runDBList(opts dbListOptions) error { return fmt.Errorf("unable to get database listing: %w", err) } - return presentDBList(opts.Output, opts.DB.UpdateURL, os.Stdout, latest) + u, err := c.ResolveArchiveURL(latest.Archive) + if err != nil { + return fmt.Errorf("unable to resolve database URL: %w", err) + } + + return presentDBList(opts.Output, u, opts.DB.UpdateURL, os.Stdout, latest) } -func presentDBList(format string, u string, writer io.Writer, latest *distribution.LatestDocument) error { +func presentDBList(format string, archiveURL, listingURL string, writer io.Writer, latest *distribution.LatestDocument) error { if latest == nil { return fmt.Errorf("no database listing found") } - parsedURL, err := url.Parse(u) + // remove query params + archiveURLObj, err := url.Parse(archiveURL) if err != nil { - return fmt.Errorf("failed to parse base URL: %w", err) + return fmt.Errorf("unable to parse db URL %q: %w", archiveURL, err) } - parsedURL.Path = path.Join(path.Dir(parsedURL.Path), latest.Path) + archiveURLObj.RawQuery = "" + + if listingURL == distribution.DefaultConfig().LatestURL { + // append on the schema + listingURL = fmt.Sprintf("%s/v%v/%s", listingURL, latest.SchemaVersion.Model, distribution.LatestFileName) + } switch format { case textOutputFormat: fmt.Fprintf(writer, "Status: %s\n", latest.Status) fmt.Fprintf(writer, "Schema: %s\n", latest.SchemaVersion.String()) fmt.Fprintf(writer, "Built: %s\n", latest.Built.String()) - fmt.Fprintf(writer, "Listing: %s\n", u) - fmt.Fprintf(writer, "DB URL: %s\n", parsedURL.String()) + fmt.Fprintf(writer, "Listing: %s\n", listingURL) + fmt.Fprintf(writer, "DB URL: %s\n", archiveURLObj.String()) fmt.Fprintf(writer, "Checksum: %s\n", latest.Checksum) case jsonOutputFormat, "raw": enc := json.NewEncoder(writer) diff --git a/cmd/grype/cli/commands/db_list_test.go b/cmd/grype/cli/commands/db_list_test.go index 30d24194c49..3492f8e4114 100644 --- a/cmd/grype/cli/commands/db_list_test.go +++ b/cmd/grype/cli/commands/db_list_test.go @@ -66,7 +66,6 @@ func Test_ListingUserAgent(t *testing.T) { } func TestPresentDBList(t *testing.T) { - baseURL := "http://localhost:8000/latest.json" latestDoc := &distribution.LatestDocument{ Status: "active", Archive: distribution.Archive{ @@ -82,20 +81,39 @@ func TestPresentDBList(t *testing.T) { tests := []struct { name string format string + baseURL string + archiveURL string latest *distribution.LatestDocument expectedText string expectedErr require.ErrorAssertionFunc }{ { - name: "valid text format", - format: textOutputFormat, - latest: latestDoc, + name: "valid text format", + format: textOutputFormat, + latest: latestDoc, + baseURL: "http://localhost:8000/latest.json", + archiveURL: "http://localhost:8000/vulnerability-db_v6.0.0_2024-11-25T01:31:56Z_1732718597.tar.zst", expectedText: `Status: active Schema: v6.0.0 Built: 2024-11-27T14:43:17Z Listing: http://localhost:8000/latest.json DB URL: http://localhost:8000/vulnerability-db_v6.0.0_2024-11-25T01:31:56Z_1732718597.tar.zst Checksum: sha256:16bcb6551c748056f752f299fcdb4fa50fe61589d086be3889e670261ff21ca4 +`, + expectedErr: require.NoError, + }, + { + name: "complete default values", + format: textOutputFormat, + latest: latestDoc, + baseURL: "https://grype.anchore.io/databases", + archiveURL: "https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.0_2024-11-25T01:31:56Z_1732718597.tar.zst", + expectedText: `Status: active +Schema: v6.0.0 +Built: 2024-11-27T14:43:17Z +Listing: https://grype.anchore.io/databases/v6/latest.json +DB URL: https://grype.anchore.io/databases/v6/vulnerability-db_v6.0.0_2024-11-25T01:31:56Z_1732718597.tar.zst +Checksum: sha256:16bcb6551c748056f752f299fcdb4fa50fe61589d086be3889e670261ff21ca4 `, expectedErr: require.NoError, }, @@ -133,7 +151,7 @@ Checksum: sha256:16bcb6551c748056f752f299fcdb4fa50fe61589d086be3889e670261ff21ca t.Run(tt.name, func(t *testing.T) { writer := &bytes.Buffer{} - err := presentDBList(tt.format, baseURL, writer, tt.latest) + err := presentDBList(tt.format, tt.archiveURL, tt.baseURL, writer, tt.latest) if tt.expectedErr == nil { tt.expectedErr = require.NoError } From 97a855279b41a21cffeff73a8ea7941e439c7ad3 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 24 Mar 2025 14:01:42 -0400 Subject: [PATCH 029/109] Use data driven approach when detecting Alpine:edge and Debian:sid (#2556) * fix data driven approach to distro aliases for rolling kinds Signed-off-by: Alex Goodman * fix matcher mock Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- grype/db/v5/namespace/index.go | 248 ----------- grype/db/v5/namespace/index_test.go | 404 ------------------ grype/db/v6/affected_package_store.go | 24 +- grype/db/v6/affected_package_store_test.go | 6 +- grype/db/v6/data.go | 2 +- grype/db/v6/vulnerability_provider.go | 8 +- grype/distro/distro.go | 91 ++-- grype/distro/distro_test.go | 244 ++++++----- .../os/alpine-edge/etc/os-release | 6 + .../os/debian-sid/usr/lib/os-release | 7 + grype/matcher/apk/matcher_test.go | 15 +- grype/matcher/internal/distro.go | 2 +- grype/matcher/msrc/matcher_test.go | 12 +- .../qualifier/rpmmodularity/qualifier_test.go | 2 +- grype/presenter/models/distribution.go | 2 +- grype/search/distro.go | 41 +- 16 files changed, 268 insertions(+), 846 deletions(-) delete mode 100644 grype/db/v5/namespace/index.go delete mode 100644 grype/db/v5/namespace/index_test.go create mode 100644 grype/distro/test-fixtures/os/alpine-edge/etc/os-release create mode 100644 grype/distro/test-fixtures/os/debian-sid/usr/lib/os-release diff --git a/grype/db/v5/namespace/index.go b/grype/db/v5/namespace/index.go deleted file mode 100644 index 5c207cd4f0d..00000000000 --- a/grype/db/v5/namespace/index.go +++ /dev/null @@ -1,248 +0,0 @@ -package namespace - -import ( - "fmt" - "regexp" - "sort" - "strings" - - hashiVer "github.com/anchore/go-version" - "github.com/anchore/grype/grype/db/v5/namespace/cpe" - "github.com/anchore/grype/grype/db/v5/namespace/distro" - "github.com/anchore/grype/grype/db/v5/namespace/language" - grypeDistro "github.com/anchore/grype/grype/distro" - "github.com/anchore/grype/internal" - "github.com/anchore/grype/internal/log" - syftPkg "github.com/anchore/syft/syft/pkg" -) - -var simpleSemVer = regexp.MustCompile(`^(?P\d+)(\.(?P\d+)(\.(?P\d+(?P[^-_]+)*))?)?$`) - -type Index struct { - all []Namespace - byLanguage map[syftPkg.Language][]*language.Namespace - byDistroKey map[string][]*distro.Namespace - cpe []*cpe.Namespace -} - -func FromStrings(namespaces []string) (*Index, error) { - all := make([]Namespace, 0) - byLanguage := make(map[syftPkg.Language][]*language.Namespace) - byDistroKey := make(map[string][]*distro.Namespace) - cpeNamespaces := make([]*cpe.Namespace, 0) - - for _, n := range namespaces { - ns, err := FromString(n) - - if err != nil { - log.Warnf("unable to create namespace object from namespace=%s: %+v", n, err) - continue - } - - all = append(all, ns) - - switch nsObj := ns.(type) { - case *language.Namespace: - l := nsObj.Language() - if _, ok := byLanguage[l]; !ok { - byLanguage[l] = make([]*language.Namespace, 0) - } - - byLanguage[l] = append(byLanguage[l], nsObj) - case *distro.Namespace: - distroKey := fmt.Sprintf("%s:%s", nsObj.DistroType(), nsObj.Version()) - if _, ok := byDistroKey[distroKey]; !ok { - byDistroKey[distroKey] = make([]*distro.Namespace, 0) - } - - byDistroKey[distroKey] = append(byDistroKey[distroKey], nsObj) - case *cpe.Namespace: - cpeNamespaces = append(cpeNamespaces, nsObj) - default: - log.Warnf("unable to index namespace=%s", n) - continue - } - } - - return &Index{ - all: all, - byLanguage: byLanguage, - byDistroKey: byDistroKey, - cpe: cpeNamespaces, - }, nil -} - -func (i *Index) NamespacesForLanguage(l syftPkg.Language) []*language.Namespace { - if _, ok := i.byLanguage[l]; ok { - return i.byLanguage[l] - } - - return nil -} - -//nolint:funlen,gocognit -func (i *Index) NamespacesForDistro(d *grypeDistro.Distro) []*distro.Namespace { - if d == nil { - return nil - } - - dTy := DistroTypeString(d.Type) - - if d.IsRolling() { - distroKey := fmt.Sprintf("%s:%s", dTy, "rolling") - if v, ok := i.byDistroKey[distroKey]; ok { - return v - } - } - - var versionSegments []int - if d.Version != nil { - versionSegments = d.Version.Segments() - } - - switch d.Type { - case grypeDistro.Alpine: - if v := i.getAlpineMajorMinorNamespace(d, versionSegments); v != nil { - return v - } - - // Fall back to alpine:edge if no version segments found - // alpine:edge is labeled as alpine-x.x_alphaYYYYMMDD - distroKey := fmt.Sprintf("%s:%s", dTy, "edge") - if v, ok := i.byDistroKey[distroKey]; ok { - return v - } - case grypeDistro.Debian: - if v, ok := i.findClosestNamespace(d, versionSegments); ok { - return v - } - - if d.RawVersion == "unstable" { - distroKey := fmt.Sprintf("%s:%s", dTy, "unstable") - if v, ok := i.byDistroKey[distroKey]; ok { - return v - } - } - } - - if v, ok := i.findClosestNamespace(d, versionSegments); ok { - return v - } - - return nil -} - -func (i *Index) getAlpineMajorMinorNamespace(d *grypeDistro.Distro, versionSegments []int) []*distro.Namespace { - var hasPrerelease bool - if d.Version != nil { - hasPrerelease = d.Version.Prerelease() != "" - } - - if !hasPrerelease { - namespaces, done := i.findClosestNamespace(d, versionSegments) - if done { - return namespaces - } - } - // If the version does not match x.y.z then it is edge - // In this case it would have - or _ alpha,beta,etc - // note: later in processing we handle the alpine:edge case - return nil -} - -func (i *Index) findClosestNamespace(d *grypeDistro.Distro, versionSegments []int) ([]*distro.Namespace, bool) { - ty := DistroTypeString(d.Type) - - // look for exact match - distroKey := fmt.Sprintf("%s:%s", ty, d.FullVersion()) - if v, ok := i.byDistroKey[distroKey]; ok { - return v, true - } - - values := internal.MatchNamedCaptureGroups(simpleSemVer, d.RawVersion) - - switch { - case values["major"] == "": - // use edge - break - case values["minor"] == "": - namespaces, done := i.findHighestMatchingMajorVersionNamespaces(d, versionSegments) - if done { - return namespaces, true - } - - default: - - if len(versionSegments) >= 2 { - // try with only first two version components - distroKey = fmt.Sprintf("%s:%d.%d", ty, versionSegments[0], versionSegments[1]) - if v, ok := i.byDistroKey[distroKey]; ok { - return v, true - } - } - - if len(versionSegments) >= 1 { - // try using only major version component - distroKey = fmt.Sprintf("%s:%d", ty, versionSegments[0]) - if v, ok := i.byDistroKey[distroKey]; ok { - return v, true - } - } - } - return nil, false -} - -func (i *Index) findHighestMatchingMajorVersionNamespaces(d *grypeDistro.Distro, versionSegments []int) ([]*distro.Namespace, bool) { - // find the highest version that matches the major version - majorVersion := versionSegments[0] - - var all []*distro.Namespace - for _, vs := range i.byDistroKey { - for _, v := range vs { - if v.DistroType() == d.Type { - all = append(all, v) - } - } - } - - type namespaceVersion struct { - version *hashiVer.Version - namespace *distro.Namespace - } - - var valid []namespaceVersion - for _, v := range all { - if strings.HasPrefix(v.Version(), fmt.Sprintf("%d.", majorVersion)) { - ver, err := hashiVer.NewVersion(v.Version()) - if err != nil { - continue - } - valid = append(valid, namespaceVersion{ - version: ver, - namespace: v, - }) - } - } - - // return the highest version from valid - sort.Slice(valid, func(i, j int) bool { - return valid[i].version.GreaterThan(valid[j].version) - }) - - if len(valid) > 0 { - return []*distro.Namespace{valid[0].namespace}, true - } - return nil, false -} - -func (i *Index) CPENamespaces() []*cpe.Namespace { - return i.cpe -} - -func DistroTypeString(ty grypeDistro.Type) string { - switch ty { - case grypeDistro.CentOS, grypeDistro.RedHat, grypeDistro.Fedora, grypeDistro.RockyLinux, grypeDistro.AlmaLinux, grypeDistro.Gentoo: - return strings.ToLower(string(grypeDistro.RedHat)) - } - return strings.ToLower(string(ty)) -} diff --git a/grype/db/v5/namespace/index_test.go b/grype/db/v5/namespace/index_test.go deleted file mode 100644 index 248c02dba87..00000000000 --- a/grype/db/v5/namespace/index_test.go +++ /dev/null @@ -1,404 +0,0 @@ -package namespace - -import ( - "testing" - - "github.com/stretchr/testify/assert" - - "github.com/anchore/grype/grype/db/v5/namespace/cpe" - "github.com/anchore/grype/grype/db/v5/namespace/distro" - "github.com/anchore/grype/grype/db/v5/namespace/language" - osDistro "github.com/anchore/grype/grype/distro" - syftPkg "github.com/anchore/syft/syft/pkg" -) - -func TestFromStringSlice(t *testing.T) { - tests := []struct { - namespaces []string - byLanguage map[syftPkg.Language][]*language.Namespace - byDistroKey map[string][]*distro.Namespace - cpe []*cpe.Namespace - }{ - { - namespaces: []string{ - "github:language:python", - "github:language:python:conda", - "debian:distro:debian:8", - "alpine:distro:alpine:3.15", - "alpine:distro:alpine:3.16", - "msrc:distro:windows:12345", - "nvd:cpe", - "github:language:ruby", - "abc.xyz:language:ruby", - "github:language:rust", - "something:language:rust", - "1234.4567:language:unknown", - "---:cpe", - "another-provider:distro:alpine:3.15", - "another-provider:distro:alpine:3.16", - }, - byLanguage: map[syftPkg.Language][]*language.Namespace{ - syftPkg.Python: { - language.NewNamespace("github", syftPkg.Python, ""), - language.NewNamespace("github", syftPkg.Python, syftPkg.Type("conda")), - }, - syftPkg.Ruby: { - language.NewNamespace("github", syftPkg.Ruby, ""), - language.NewNamespace("abc.xyz", syftPkg.Ruby, ""), - }, - syftPkg.Rust: { - language.NewNamespace("github", syftPkg.Rust, ""), - language.NewNamespace("something", syftPkg.Rust, ""), - }, - syftPkg.Language("unknown"): { - language.NewNamespace("1234.4567", syftPkg.Language("unknown"), ""), - }, - }, - byDistroKey: map[string][]*distro.Namespace{ - "debian:8": { - distro.NewNamespace("debian", osDistro.Debian, "8"), - }, - "alpine:3.15": { - distro.NewNamespace("alpine", osDistro.Alpine, "3.15"), - distro.NewNamespace("another-provider", osDistro.Alpine, "3.15"), - }, - "alpine:3.16": { - distro.NewNamespace("alpine", osDistro.Alpine, "3.16"), - distro.NewNamespace("another-provider", osDistro.Alpine, "3.16"), - }, - "windows:12345": { - distro.NewNamespace("msrc", osDistro.Windows, "12345"), - }, - }, - cpe: []*cpe.Namespace{ - cpe.NewNamespace("---"), - cpe.NewNamespace("nvd"), - }, - }, - } - - for _, test := range tests { - result, _ := FromStrings(test.namespaces) - assert.Len(t, result.all, len(test.namespaces)) - - for l, elems := range result.byLanguage { - assert.Contains(t, test.byLanguage, l) - assert.ElementsMatch(t, elems, test.byLanguage[l]) - } - - for d, elems := range result.byDistroKey { - assert.Contains(t, test.byDistroKey, d) - assert.ElementsMatch(t, elems, test.byDistroKey[d]) - } - - assert.ElementsMatch(t, result.cpe, test.cpe) - } -} - -func TestIndex_CPENamespaces(t *testing.T) { - tests := []struct { - namespaces []string - cpe []*cpe.Namespace - }{ - { - namespaces: []string{"nvd:cpe", "another-source:cpe", "x:distro:y:10"}, - cpe: []*cpe.Namespace{ - cpe.NewNamespace("nvd"), - cpe.NewNamespace("another-source"), - }, - }, - } - - for _, test := range tests { - result, _ := FromStrings(test.namespaces) - assert.Len(t, result.all, len(test.namespaces)) - assert.ElementsMatch(t, result.CPENamespaces(), test.cpe) - } -} - -func newDistro(t *testing.T, dt osDistro.Type, v string, idLikes []string) *osDistro.Distro { - d, err := osDistro.New(dt, v, idLikes...) - assert.NoError(t, err) - return d -} - -func TestIndex_NamespacesForDistro(t *testing.T) { - namespaceIndex, err := FromStrings([]string{ - "alpine:distro:alpine:2.17", - "alpine:distro:alpine:3.15", - "alpine:distro:alpine:3.16", - "alpine:distro:alpine:4.13", - "alpine:distro:alpine:edge", - "debian:distro:debian:8", - "debian:distro:debian:unstable", - "amazon:distro:amazonlinux:2", - "amazon:distro:amazonlinux:2022", - "abc.xyz:distro:unknown:123.456", - "redhat:distro:redhat:8", - "redhat:distro:redhat:9", - "other-provider:distro:debian:8", - "other-provider:distro:redhat:9", - "suse:distro:sles:12.5", - "mariner:distro:mariner:2.0", - "mariner:distro:azurelinux:3.0", - "msrc:distro:windows:471816", - "ubuntu:distro:ubuntu:18.04", - "ubuntu:distro:ubuntu:18.10", - "ubuntu:distro:ubuntu:20.04", - "ubuntu:distro:ubuntu:20.10", - "oracle:distro:oraclelinux:8", - "wolfi:distro:wolfi:rolling", - "chainguard:distro:chainguard:rolling", - "archlinux:distro:archlinux:rolling", - }) - - assert.NoError(t, err) - - tests := []struct { - name string - distro *osDistro.Distro - namespaces []*distro.Namespace - }{ - { - name: "alpine patch version matches minor version namespace", - distro: newDistro(t, osDistro.Alpine, "3.15.4", []string{"alpine"}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("alpine", osDistro.Alpine, "3.15"), - }, - }, - { - name: "alpine missing patch version matches with minor version", - distro: newDistro(t, osDistro.Alpine, "3.16", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("alpine", osDistro.Alpine, "3.16"), - }, - }, - { - name: "alpine missing minor version uses latest minor version", - distro: newDistro(t, osDistro.Alpine, "3", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("alpine", osDistro.Alpine, "3.16"), - }, - }, - { - name: "ubuntu missing minor version uses latest minor version", - distro: newDistro(t, osDistro.Ubuntu, "18", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("ubuntu", osDistro.Ubuntu, "18.10"), - }, - }, - { - name: "alpine rc version with no patch should match edge", - distro: newDistro(t, osDistro.Alpine, "3.16.4-r4", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("alpine", osDistro.Alpine, "edge"), - }, - }, - - { - name: "alpine edge version matches edge namespace", - distro: &osDistro.Distro{Type: osDistro.Alpine, Version: nil, RawVersion: "3.17.1_alpha20221002", IDLike: []string{"alpine"}}, - namespaces: []*distro.Namespace{ - distro.NewNamespace("alpine", osDistro.Alpine, "edge"), - }, - }, - { - name: "alpine raw version matches edge with - character", - distro: &osDistro.Distro{Type: osDistro.Alpine, Version: nil, RawVersion: "3.17.1-alpha20221002", IDLike: []string{"alpine"}}, - namespaces: []*distro.Namespace{ - distro.NewNamespace("alpine", osDistro.Alpine, "edge"), - }, - }, - { - name: "alpine raw version matches edge with - character no sha", - distro: newDistro(t, osDistro.Alpine, "3.17.1-alpha", []string{"alpine"}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("alpine", osDistro.Alpine, "edge"), - }, - }, - { - name: "alpine raw version matches edge with _ character no sha", - // we don't create a newDistro from this since parsing the version fails - distro: &osDistro.Distro{Type: osDistro.Alpine, Version: nil, RawVersion: "3.17.1_alpha", IDLike: []string{"alpine"}}, - namespaces: []*distro.Namespace{ - distro.NewNamespace("alpine", osDistro.Alpine, "edge"), - }, - }, - { - name: "alpine malformed version matches with closest", - distro: newDistro(t, osDistro.Alpine, "3.16.4.5", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("alpine", osDistro.Alpine, "3.16"), - }, - }, - { - name: "Debian minor version matches debian and other-provider namespaces", - distro: newDistro(t, osDistro.Debian, "8.5", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("debian", osDistro.Debian, "8"), - distro.NewNamespace("other-provider", osDistro.Debian, "8"), - }, - }, - { - name: "Redhat minor version matches redhat and other-provider namespaces", - distro: newDistro(t, osDistro.RedHat, "9.5", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("redhat", osDistro.RedHat, "9"), - distro.NewNamespace("other-provider", osDistro.RedHat, "9"), - }, - }, - { - name: "Centos minor version matches redhat and other-provider namespaces", - distro: newDistro(t, osDistro.CentOS, "9.5", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("redhat", osDistro.RedHat, "9"), - distro.NewNamespace("other-provider", osDistro.RedHat, "9"), - }, - }, - { - name: "Alma Linux minor version matches redhat and other-provider namespaces", - distro: newDistro(t, osDistro.AlmaLinux, "9.5", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("redhat", osDistro.RedHat, "9"), - distro.NewNamespace("other-provider", osDistro.RedHat, "9"), - }, - }, - { - name: "Rocky Linux minor version matches redhat and other-provider namespaces", - distro: newDistro(t, osDistro.RockyLinux, "9.5", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("redhat", osDistro.RedHat, "9"), - distro.NewNamespace("other-provider", osDistro.RedHat, "9"), - }, - }, - { - name: "SLES minor version matches suse namespace", - distro: newDistro(t, osDistro.SLES, "12.5", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("suse", osDistro.SLES, "12.5"), - }, - }, - { - name: "Windows version object matches msrc namespace with exact version", - distro: newDistro(t, osDistro.Windows, "471816", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("msrc", osDistro.Windows, "471816"), - }, - }, - { - name: "Ubuntu minor semvar matches ubuntu namespace with exact version", - distro: newDistro(t, osDistro.Ubuntu, "18.04", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("ubuntu", osDistro.Ubuntu, "18.04"), - }, - }, - { - name: "Fedora minor semvar will not match a namespace", - distro: newDistro(t, osDistro.Fedora, "31.4", []string{}), - namespaces: nil, - }, - { - name: "Amazon Linux Major semvar matches amazon namespace with exact version", - distro: newDistro(t, osDistro.AmazonLinux, "2", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("amazon", osDistro.AmazonLinux, "2"), - }, - }, - { - name: "Amazon Linux year version matches amazon namespace with exact uear", - distro: newDistro(t, osDistro.AmazonLinux, "2022", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("amazon", osDistro.AmazonLinux, "2022"), - }, - }, - { - name: "Mariner minor semvar matches no namespace", - distro: newDistro(t, osDistro.Mariner, "20.1", []string{}), - namespaces: nil, - }, - { - name: "Mariner 2.0 matches mariner namespace", - distro: newDistro(t, osDistro.Mariner, "2.0", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("mariner", "mariner", "2.0"), - }, - }, - { - name: "azurelinux 3 is matched by mariner 3 namespace", - distro: newDistro(t, osDistro.Azure, "3.0", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("mariner", osDistro.Azure, "3.0"), - }, - }, - { - name: "Oracle Linux Major semvar matches oracle namespace with exact version", - distro: newDistro(t, osDistro.OracleLinux, "8", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("oracle", osDistro.OracleLinux, "8"), - }, - }, - { - - name: "Arch Linux matches archlinux rolling namespace", - distro: newDistro(t, osDistro.ArchLinux, "", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("archlinux", osDistro.ArchLinux, "rolling"), - }, - }, - { - - name: "Wolfi matches wolfi rolling namespace", - distro: newDistro(t, osDistro.Wolfi, "20221011", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("wolfi", osDistro.Wolfi, "rolling"), - }, - }, - { - - name: "Chainguard matches chainguard rolling namespace", - distro: newDistro(t, osDistro.Chainguard, "20230214", []string{}), - namespaces: []*distro.Namespace{ - distro.NewNamespace("chainguard", osDistro.Chainguard, "rolling"), - }, - }, - { - - name: "Gentoo doesn't match any namespace since the gentoo rolling namespace doesn't exist in index", - distro: newDistro(t, osDistro.Gentoo, "", []string{}), - namespaces: nil, - }, - { - name: "Open Suse Leap semvar matches no namespace", - distro: newDistro(t, osDistro.OpenSuseLeap, "100", []string{}), - namespaces: nil, - }, - { - name: "Photon minor semvar no namespace", - distro: newDistro(t, osDistro.Photon, "20.1", []string{}), - namespaces: nil, - }, - { - name: "Busybox minor semvar matches no namespace", - distro: newDistro(t, osDistro.Busybox, "20.1", []string{}), - namespaces: nil, - }, - { - name: "debian unstable", - distro: &osDistro.Distro{ - Type: osDistro.Debian, - RawVersion: "unstable", - Version: nil, - }, - namespaces: []*distro.Namespace{ - distro.NewNamespace("debian", osDistro.Debian, "unstable"), - }, - }, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - namespaces := namespaceIndex.NamespacesForDistro(test.distro) - assert.ElementsMatch(t, test.namespaces, namespaces) - }) - } -} diff --git a/grype/db/v6/affected_package_store.go b/grype/db/v6/affected_package_store.go index e902ba4c6fe..59ef9da44b5 100644 --- a/grype/db/v6/affected_package_store.go +++ b/grype/db/v6/affected_package_store.go @@ -97,6 +97,9 @@ type OSSpecifier struct { // MinorVersion is the second field in the VERSION_ID field in /etc/os-release (e.g. 0 in "7.0.1406") MinorVersion string + // RemainingVersion is anything after the minor version in the VERSION_ID field in /etc/os-release (e.g. 1406 in "7.0.1406") + RemainingVersion string + // LabelVersion is a string that represents a floating version (e.g. "edge" or "unstable") or is the CODENAME field in /etc/os-release (e.g. "wheezy" for debian 7) LabelVersion string } @@ -112,10 +115,7 @@ func (d *OSSpecifier) String() string { var version string if d.MajorVersion != "" { - version = d.MajorVersion - if d.MinorVersion != "" { - version += "." + d.MinorVersion - } + version = d.version() } else { version = d.LabelVersion } @@ -132,19 +132,17 @@ func (d *OSSpecifier) String() string { } func (d OSSpecifier) version() string { - if d.MajorVersion != "" && d.MinorVersion != "" { - return d.MajorVersion + "." + d.MinorVersion - } - if d.MajorVersion != "" { + if d.MinorVersion != "" { + if d.RemainingVersion != "" { + return d.MajorVersion + "." + d.MinorVersion + "." + d.RemainingVersion + } + return d.MajorVersion + "." + d.MinorVersion + } return d.MajorVersion } - if d.LabelVersion != "" { - return d.LabelVersion - } - - return "" + return d.LabelVersion } func (d OSSpecifiers) String() string { diff --git a/grype/db/v6/affected_package_store_test.go b/grype/db/v6/affected_package_store_test.go index c8f1d8a2b39..3f7ede29f4e 100644 --- a/grype/db/v6/affected_package_store_test.go +++ b/grype/db/v6/affected_package_store_test.go @@ -992,7 +992,7 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) { debian10 := &OperatingSystem{Name: "debian", ReleaseID: "debian", MajorVersion: "10"} alpine318 := &OperatingSystem{Name: "alpine", ReleaseID: "alpine", MajorVersion: "3", MinorVersion: "18"} alpineEdge := &OperatingSystem{Name: "alpine", ReleaseID: "alpine", LabelVersion: "edge"} - debianTrixie := &OperatingSystem{Name: "debian", ReleaseID: "debian", LabelVersion: "trixie"} + debianUnstable := &OperatingSystem{Name: "debian", ReleaseID: "debian", LabelVersion: "unstable"} debian7 := &OperatingSystem{Name: "debian", ReleaseID: "debian", MajorVersion: "7", LabelVersion: "wheezy"} wolfi := &OperatingSystem{Name: "wolfi", ReleaseID: "wolfi", MajorVersion: "20230201"} arch := &OperatingSystem{Name: "arch", ReleaseID: "arch", MajorVersion: "20241110", MinorVersion: "0"} @@ -1010,7 +1010,7 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) { debian10, alpine318, alpineEdge, - debianTrixie, + debianUnstable, debian7, wolfi, arch, @@ -1130,7 +1130,7 @@ func TestAffectedPackageStore_ResolveDistro(t *testing.T) { MajorVersion: "13", LabelVersion: "trixie", }, - expected: []OperatingSystem{*debianTrixie}, + expected: []OperatingSystem{*debianUnstable}, }, { name: "debian by codename", diff --git a/grype/db/v6/data.go b/grype/db/v6/data.go index 42117c0d920..bbd1ac0979b 100644 --- a/grype/db/v6/data.go +++ b/grype/db/v6/data.go @@ -42,7 +42,7 @@ func KnownOperatingSystemSpecifierOverrides() []OperatingSystemSpecifierOverride // // depending where the team is during the development cycle you will see different behavior, making automating // this a little challenging. - {Alias: "debian", Codename: "trixie", Rolling: true}, // is currently sid, which is considered rolling + {Alias: "debian", Codename: "trixie", Rolling: true, ReplacementLabelVersion: strRef("unstable")}, // is currently sid, which is considered rolling } } diff --git a/grype/db/v6/vulnerability_provider.go b/grype/db/v6/vulnerability_provider.go index acf66c6dc97..5b6c1c9ad8b 100644 --- a/grype/db/v6/vulnerability_provider.go +++ b/grype/db/v6/vulnerability_provider.go @@ -250,9 +250,11 @@ func (vp vulnerabilityProvider) FindVulnerabilities(criteria ...vulnerability.Cr case *search.DistroCriteria: for _, d := range c.Distros { osSpecs = append(osSpecs, &OSSpecifier{ - Name: d.Name(), - MajorVersion: d.MajorVersion(), - MinorVersion: d.MinorVersion(), + Name: d.Name(), + MajorVersion: d.MajorVersion(), + MinorVersion: d.MinorVersion(), + RemainingVersion: d.RemainingVersion(), + LabelVersion: d.Codename, }) } applied = true diff --git a/grype/distro/distro.go b/grype/distro/distro.go index 17ac8dbb054..b7234442e10 100644 --- a/grype/distro/distro.go +++ b/grype/distro/distro.go @@ -11,29 +11,46 @@ import ( // Distro represents a Linux Distribution. type Distro struct { - Type Type - Version *hashiVer.Version - RawVersion string - IDLike []string + Type Type + Version string + Codename string + IDLike []string + + // fields populated in the constructor + + major string + minor string + remaining string } // New creates a new Distro object populated with the given values. -func New(t Type, version string, idLikes ...string) (*Distro, error) { - var verObj *hashiVer.Version - var err error - +func New(t Type, version, label string, idLikes ...string) (*Distro, error) { + var major, minor, remaining string if version != "" { - verObj, err = hashiVer.NewVersion(version) - if err != nil { - return nil, fmt.Errorf("unable to parse version: %w", err) + // if starts with a digit, then assume it's a version and extract the major, minor, and remaining versions + if version[0] >= '0' && version[0] <= '9' { + // extract the major, minor, and remaining versions + parts := strings.Split(version, ".") + if len(parts) > 0 { + major = parts[0] + if len(parts) > 1 { + minor = parts[1] + } + if len(parts) > 2 { + remaining = strings.Join(parts[2:], ".") + } + } } } return &Distro{ - Type: t, - Version: verObj, - RawVersion: version, - IDLike: idLikes, + Type: t, + major: major, + minor: minor, + remaining: remaining, + Version: version, + Codename: label, + IDLike: idLikes, }, nil } @@ -51,21 +68,18 @@ func NewFromRelease(release linux.Release) (*Distro, error) { continue } - if _, err := hashiVer.NewVersion(version); err == nil { + _, err := hashiVer.NewVersion(version) + if err == nil { selectedVersion = version break } } - if t == Debian && release.VersionID == "" && release.Version == "" && strings.Contains(release.PrettyName, "sid") { - return &Distro{ - Type: t, - RawVersion: "unstable", - IDLike: release.IDLike, - }, nil + if selectedVersion == "" { + selectedVersion = release.VersionID } - return New(t, selectedVersion, release.IDLike...) + return New(t, selectedVersion, release.VersionCodename, release.IDLike...) } func (d Distro) Name() string { @@ -74,46 +88,27 @@ func (d Distro) Name() string { // MajorVersion returns the major version value from the pseudo-semantically versioned distro version value. func (d Distro) MajorVersion() string { - if d.Version == nil { - return strings.Split(d.RawVersion, ".")[0] - } - return fmt.Sprintf("%d", d.Version.Segments()[0]) + return d.major } // MinorVersion returns the minor version value from the pseudo-semantically versioned distro version value. func (d Distro) MinorVersion() string { - if d.Version == nil { - parts := strings.Split(d.RawVersion, ".") - if len(parts) > 1 { - return parts[1] - } - return "" - } - parts := d.Version.Segments() - if len(parts) > 1 { - return fmt.Sprintf("%d", parts[1]) - } - return "" + return d.minor } -// FullVersion returns the original user version value. -func (d Distro) FullVersion() string { - return d.RawVersion +func (d Distro) RemainingVersion() string { + return d.remaining } // String returns a human-friendly representation of the Linux distribution. func (d Distro) String() string { versionStr := "(version unknown)" - if d.RawVersion != "" { - versionStr = d.RawVersion + if d.Version != "" { + versionStr = d.Version } return fmt.Sprintf("%s %s", d.Type, versionStr) } -func (d Distro) IsRolling() bool { - return d.Type == Wolfi || d.Type == Chainguard || d.Type == ArchLinux || d.Type == Gentoo -} - // Unsupported Linux distributions func (d Distro) Disabled() bool { switch { diff --git a/grype/distro/distro_test.go b/grype/distro/distro_test.go index c18be758fa1..e399b7d2732 100644 --- a/grype/distro/distro_test.go +++ b/grype/distro/distro_test.go @@ -3,6 +3,8 @@ package distro import ( "testing" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -14,12 +16,12 @@ import ( func Test_NewDistroFromRelease(t *testing.T) { tests := []struct { - name string - release linux.Release - expectedVersion string - expectedRawVersion string - expectedType Type - expectErr bool + name string + release linux.Release + expected *Distro + minor string + major string + expectErr require.ErrorAssertionFunc }{ { name: "go case: derive version from version-id", @@ -28,9 +30,12 @@ func Test_NewDistroFromRelease(t *testing.T) { VersionID: "8", Version: "7", }, - expectedType: CentOS, - expectedRawVersion: "8", - expectedVersion: "8.0.0", + expected: &Distro{ + Type: CentOS, + Version: "8", + }, + major: "8", + minor: "", }, { name: "fallback to release name when release id is missing", @@ -38,9 +43,12 @@ func Test_NewDistroFromRelease(t *testing.T) { Name: "windows", VersionID: "8", }, - expectedType: Windows, - expectedRawVersion: "8", - expectedVersion: "8.0.0", + expected: &Distro{ + Type: Windows, + Version: "8", + }, + major: "8", + minor: "", }, { name: "fallback to version when version-id missing", @@ -48,16 +56,22 @@ func Test_NewDistroFromRelease(t *testing.T) { ID: "centos", Version: "8", }, - expectedType: CentOS, - expectedRawVersion: "8", - expectedVersion: "8.0.0", + expected: &Distro{ + Type: CentOS, + Version: "8", + }, + major: "8", + minor: "", }, { - name: "missing version results in error", + // this enables matching on multiple OS versions at once + name: "missing version or label version is allowed", release: linux.Release{ ID: "centos", }, - expectedType: CentOS, + expected: &Distro{ + Type: CentOS, + }, }, { name: "bogus distro type results in error", @@ -65,7 +79,7 @@ func Test_NewDistroFromRelease(t *testing.T) { ID: "bogosity", VersionID: "8", }, - expectErr: true, + expectErr: require.Error, }, { // syft -o json debian:testing | jq .distro @@ -78,9 +92,12 @@ func Test_NewDistroFromRelease(t *testing.T) { VersionCodename: "trixie", Name: "Debian GNU/Linux", }, - expectedType: Debian, - expectedRawVersion: "unstable", - expectedVersion: "", + expected: &Distro{ + Type: Debian, + Codename: "trixie", + }, + major: "", + minor: "", }, { name: "azure linux 3", @@ -89,176 +106,194 @@ func Test_NewDistroFromRelease(t *testing.T) { Version: "3.0.20240417", VersionID: "3.0", }, - expectedType: Azure, - expectedRawVersion: "3.0", + expected: &Distro{ + Type: Azure, + Version: "3.0", + }, + major: "3", + minor: "0", }, } - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - d, err := NewFromRelease(test.release) - if test.expectErr { - require.Error(t, err) - return - } else { - require.NoError(t, err) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.expectErr == nil { + tt.expectErr = require.NoError } - assert.Equal(t, test.expectedType, d.Type) - if test.expectedVersion != "" { - assert.Equal(t, test.expectedVersion, d.Version.String()) + distro, err := NewFromRelease(tt.release) + tt.expectErr(t, err) + if err != nil { + return } - if test.expectedRawVersion != "" { - assert.Equal(t, test.expectedRawVersion, d.FullVersion()) + + if d := cmp.Diff(tt.expected, distro, cmpopts.IgnoreUnexported(Distro{})); d != "" { + t.Errorf("unexpected result: %s", d) } + assert.Equal(t, tt.major, distro.MajorVersion(), "unexpected major version") + assert.Equal(t, tt.minor, distro.MinorVersion(), "unexpected minor version") }) } } func Test_NewDistroFromRelease_Coverage(t *testing.T) { + observedDistros := stringutil.NewStringSet() + definedDistros := stringutil.NewStringSet() + + for _, distroType := range All { + definedDistros.Add(string(distroType)) + } + + // Somewhat cheating with Windows. There is no support for detecting/parsing a Windows OS, so it is not + // possible to comply with this test unless it is added manually to the "observed distros" + definedDistros.Remove(string(Windows)) + tests := []struct { - fixture string - Type Type - Version string + Name string + Type Type + Version string + LabelVersion string }{ { - fixture: "test-fixtures/os/alpine", + Name: "test-fixtures/os/alpine", Type: Alpine, Version: "3.11.6", }, { - fixture: "test-fixtures/os/amazon", + Name: "test-fixtures/os/alpine-edge", + Type: Alpine, + Version: "3.22.0_alpha20250108", + }, + { + Name: "test-fixtures/os/amazon", Type: AmazonLinux, - Version: "2.0.0", + Version: "2", }, { - fixture: "test-fixtures/os/busybox", + Name: "test-fixtures/os/busybox", Type: Busybox, Version: "1.31.1", }, { - fixture: "test-fixtures/os/centos", + Name: "test-fixtures/os/centos", Type: CentOS, - Version: "8.0.0", + Version: "8", }, { - fixture: "test-fixtures/os/debian", + Name: "test-fixtures/os/debian", Type: Debian, - Version: "8.0.0", + Version: "8", }, { - fixture: "test-fixtures/os/fedora", + Name: "test-fixtures/os/debian-sid", + Type: Debian, + LabelVersion: "trixie", + }, + { + Name: "test-fixtures/os/fedora", Type: Fedora, - Version: "31.0.0", + Version: "31", }, { - fixture: "test-fixtures/os/redhat", + Name: "test-fixtures/os/redhat", Type: RedHat, - Version: "7.3.0", + Version: "7.3", }, { - fixture: "test-fixtures/os/ubuntu", - Type: Ubuntu, - Version: "20.4.0", + Name: "test-fixtures/os/ubuntu", + Type: Ubuntu, + Version: "20.04", + LabelVersion: "focal", }, { - fixture: "test-fixtures/os/oraclelinux", + Name: "test-fixtures/os/oraclelinux", Type: OracleLinux, - Version: "8.3.0", + Version: "8.3", }, { - fixture: "test-fixtures/os/custom", + Name: "test-fixtures/os/custom", Type: RedHat, - Version: "8.0.0", + Version: "8", }, { - fixture: "test-fixtures/os/opensuse-leap", + Name: "test-fixtures/os/opensuse-leap", Type: OpenSuseLeap, - Version: "15.2.0", + Version: "15.2", }, { - fixture: "test-fixtures/os/sles", + Name: "test-fixtures/os/sles", Type: SLES, - Version: "15.2.0", + Version: "15.2", }, { - fixture: "test-fixtures/os/photon", + Name: "test-fixtures/os/photon", Type: Photon, - Version: "2.0.0", + Version: "2.0", }, { - fixture: "test-fixtures/os/arch", - Type: ArchLinux, + Name: "test-fixtures/os/arch", + Type: ArchLinux, }, { - fixture: "test-fixtures/partial-fields/missing-id", + Name: "test-fixtures/partial-fields/missing-id", Type: Debian, - Version: "8.0.0", + Version: "8", }, { - fixture: "test-fixtures/partial-fields/unknown-id", + Name: "test-fixtures/partial-fields/unknown-id", Type: Debian, - Version: "8.0.0", + Version: "8", }, { - fixture: "test-fixtures/os/centos6", + Name: "test-fixtures/os/centos6", Type: CentOS, - Version: "6.0.0", + Version: "6", }, { - fixture: "test-fixtures/os/centos5", + Name: "test-fixtures/os/centos5", Type: CentOS, - Version: "5.7.0", + Version: "5.7", }, { - fixture: "test-fixtures/os/mariner", + Name: "test-fixtures/os/mariner", Type: Mariner, - Version: "1.0.0", + Version: "1.0", }, { - fixture: "test-fixtures/os/azurelinux", + Name: "test-fixtures/os/azurelinux", Type: Azure, - Version: "3.0.0", + Version: "3.0", }, { - fixture: "test-fixtures/os/rockylinux", + Name: "test-fixtures/os/rockylinux", Type: RockyLinux, - Version: "8.4.0", + Version: "8.4", }, { - fixture: "test-fixtures/os/almalinux", + Name: "test-fixtures/os/almalinux", Type: AlmaLinux, - Version: "8.4.0", + Version: "8.4", }, { - fixture: "test-fixtures/os/gentoo", - Type: Gentoo, + Name: "test-fixtures/os/gentoo", + Type: Gentoo, }, { - fixture: "test-fixtures/os/wolfi", + Name: "test-fixtures/os/wolfi", Type: Wolfi, + Version: "20220914", }, { - fixture: "test-fixtures/os/chainguard", + Name: "test-fixtures/os/chainguard", Type: Chainguard, + Version: "20230214", }, } - observedDistros := stringutil.NewStringSet() - definedDistros := stringutil.NewStringSet() - - for _, distroType := range All { - definedDistros.Add(string(distroType)) - } - - // Somewhat cheating with Windows. There is no support for detecting/parsing a Windows OS, so it is not - // possible to comply with this test unless it is added manually to the "observed distros" - definedDistros.Remove(string(Windows)) - - for _, test := range tests { - t.Run(test.fixture, func(t *testing.T) { - s, err := directorysource.NewFromPath(test.fixture) + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + s, err := directorysource.NewFromPath(tt.Name) require.NoError(t, err) resolver, err := s.FileResolver(source.SquashedScope) @@ -274,10 +309,9 @@ func Test_NewDistroFromRelease_Coverage(t *testing.T) { observedDistros.Add(d.Type.String()) - assert.Equal(t, test.Type, d.Type) - if test.Version != "" { - assert.Equal(t, d.Version.String(), test.Version) - } + assert.Equal(t, tt.Type, d.Type, "unexpected distro type") + assert.Equal(t, tt.LabelVersion, d.Codename, "unexpected label version") + assert.Equal(t, tt.Version, d.Version, "unexpected version") }) } @@ -324,7 +358,7 @@ func TestDistro_FullVersion(t *testing.T) { Version: test.version, }) require.NoError(t, err) - assert.Equal(t, test.expected, d.FullVersion()) + assert.Equal(t, test.expected, d.Version) }) } diff --git a/grype/distro/test-fixtures/os/alpine-edge/etc/os-release b/grype/distro/test-fixtures/os/alpine-edge/etc/os-release new file mode 100644 index 00000000000..c7133dc2390 --- /dev/null +++ b/grype/distro/test-fixtures/os/alpine-edge/etc/os-release @@ -0,0 +1,6 @@ +NAME="Alpine Linux" +ID=alpine +VERSION_ID=3.22.0_alpha20250108 +PRETTY_NAME="Alpine Linux edge" +HOME_URL="https://alpinelinux.org/" +BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues" \ No newline at end of file diff --git a/grype/distro/test-fixtures/os/debian-sid/usr/lib/os-release b/grype/distro/test-fixtures/os/debian-sid/usr/lib/os-release new file mode 100644 index 00000000000..c32b48d1edd --- /dev/null +++ b/grype/distro/test-fixtures/os/debian-sid/usr/lib/os-release @@ -0,0 +1,7 @@ +PRETTY_NAME="Debian GNU/Linux trixie/sid" +NAME="Debian GNU/Linux" +VERSION_CODENAME=trixie +ID=debian +HOME_URL="https://www.debian.org/" +SUPPORT_URL="https://www.debian.org/support" +BUG_REPORT_URL="https://bugs.debian.org/" diff --git a/grype/matcher/apk/matcher_test.go b/grype/matcher/apk/matcher_test.go index 353c973ddb8..4f068cafd02 100644 --- a/grype/matcher/apk/matcher_test.go +++ b/grype/matcher/apk/matcher_test.go @@ -61,7 +61,7 @@ func TestSecDBOnlyMatch(t *testing.T) { SearchedBy: map[string]interface{}{ "distro": map[string]string{ "type": d.Type.String(), - "version": d.RawVersion, + "version": d.Version, }, "package": map[string]string{ "name": "libvncserver", @@ -140,7 +140,7 @@ func TestBothSecdbAndNvdMatches(t *testing.T) { SearchedBy: map[string]interface{}{ "distro": map[string]string{ "type": d.Type.String(), - "version": d.RawVersion, + "version": d.Version, }, "package": map[string]string{ "name": "libvncserver", @@ -226,7 +226,7 @@ func TestBothSecdbAndNvdMatches_DifferentFixInfo(t *testing.T) { SearchedBy: map[string]interface{}{ "distro": map[string]string{ "type": d.Type.String(), - "version": d.RawVersion, + "version": d.Version, }, "package": map[string]string{ "name": "libvncserver", @@ -306,7 +306,7 @@ func TestBothSecdbAndNvdMatches_DifferentPackageName(t *testing.T) { SearchedBy: map[string]interface{}{ "distro": map[string]string{ "type": d.Type.String(), - "version": d.RawVersion, + "version": d.Version, }, "package": map[string]string{ "name": "libvncserver", @@ -659,7 +659,7 @@ func TestNVDMatchCanceledByOriginPackageInSecDB(t *testing.T) { vp := mock.VulnerabilityProvider(nvdVuln, secDBVuln) m := Matcher{} - d, err := distro.New(distro.Wolfi, "") + d, err := distro.New(distro.Wolfi, "", "") if err != nil { t.Fatalf("failed to create a new distro: %+v", err) } @@ -734,7 +734,7 @@ func TestDistroMatchBySourceIndirection(t *testing.T) { SearchedBy: map[string]interface{}{ "distro": map[string]string{ "type": d.Type.String(), - "version": d.RawVersion, + "version": d.Version, }, "package": map[string]string{ "name": "musl", @@ -805,7 +805,7 @@ func TestSecDBMatchesStillCountedWithCpeErrors(t *testing.T) { SearchedBy: map[string]interface{}{ "distro": map[string]string{ "type": d.Type.String(), - "version": d.RawVersion, + "version": d.Version, }, "package": map[string]string{ "name": "musl", @@ -903,6 +903,7 @@ func assertMatches(t *testing.T, expected, actual []match.Match) { var opts = []cmp.Option{ cmpopts.IgnoreFields(vulnerability.Vulnerability{}, "Constraint"), cmpopts.IgnoreFields(pkg.Package{}, "Locations"), + cmpopts.IgnoreUnexported(distro.Distro{}), } if diff := cmp.Diff(expected, actual, opts...); diff != "" { diff --git a/grype/matcher/internal/distro.go b/grype/matcher/internal/distro.go index 1608522f9e8..fd2f7fbaa6a 100644 --- a/grype/matcher/internal/distro.go +++ b/grype/matcher/internal/distro.go @@ -54,7 +54,7 @@ func MatchPackageByDistro(provider vulnerability.Provider, p pkg.Package, upstre SearchedBy: map[string]interface{}{ "distro": map[string]string{ "type": p.Distro.Type.String(), - "version": p.Distro.RawVersion, + "version": p.Distro.Version, }, // why include the package information? The given package searched with may be a source package // for another package that is installed on the system. This makes it apparent exactly what diff --git a/grype/matcher/msrc/matcher_test.go b/grype/matcher/msrc/matcher_test.go index 0696dc807b2..f42e5a0a1d2 100644 --- a/grype/matcher/msrc/matcher_test.go +++ b/grype/matcher/msrc/matcher_test.go @@ -21,7 +21,7 @@ func TestMatches(t *testing.T) { // TODO: it would be ideal to test against something that constructs the namespace based on grype-db // and not break the adaption of grype-db - msrcNamespace := fmt.Sprintf("msrc:distro:windows:%s", d.RawVersion) + msrcNamespace := fmt.Sprintf("msrc:distro:windows:%s", d.Version) vp := mock.VulnerabilityProvider([]vulnerability.Vulnerability{ { @@ -29,7 +29,7 @@ func TestMatches(t *testing.T) { ID: "CVE-2016-3333", Namespace: msrcNamespace, }, - PackageName: d.RawVersion, + PackageName: d.Version, Constraint: version.MustGetConstraint("3200970 || 878787 || base", version.KBFormat), }, { @@ -38,7 +38,7 @@ func TestMatches(t *testing.T) { ID: "CVE-2020-made-up", Namespace: msrcNamespace, }, - PackageName: d.RawVersion, + PackageName: d.Version, Constraint: version.MustGetConstraint("778786 || 878787 || base", version.KBFormat), }, // Does not match the product ID @@ -61,7 +61,7 @@ func TestMatches(t *testing.T) { name: "direct KB match", pkg: pkg.Package{ ID: pkg.ID(uuid.NewString()), - Name: d.RawVersion, + Name: d.Version, Version: "3200970", Type: syftPkg.KbPkg, Distro: d, @@ -74,7 +74,7 @@ func TestMatches(t *testing.T) { name: "multiple direct KB match", pkg: pkg.Package{ ID: pkg.ID(uuid.NewString()), - Name: d.RawVersion, + Name: d.Version, Version: "878787", Type: syftPkg.KbPkg, Distro: d, @@ -88,7 +88,7 @@ func TestMatches(t *testing.T) { name: "no KBs found", pkg: pkg.Package{ ID: pkg.ID(uuid.NewString()), - Name: d.RawVersion, + Name: d.Version, // this is the assumed version if no KBs are found Version: "base", Type: syftPkg.KbPkg, diff --git a/grype/pkg/qualifier/rpmmodularity/qualifier_test.go b/grype/pkg/qualifier/rpmmodularity/qualifier_test.go index f8b14f1a175..994876bf760 100644 --- a/grype/pkg/qualifier/rpmmodularity/qualifier_test.go +++ b/grype/pkg/qualifier/rpmmodularity/qualifier_test.go @@ -11,7 +11,7 @@ import ( ) func TestRpmModularity_Satisfied(t *testing.T) { - oracle, _ := distro.New(distro.OracleLinux, "8") + oracle, _ := distro.New(distro.OracleLinux, "8", "") tests := []struct { name string diff --git a/grype/presenter/models/distribution.go b/grype/presenter/models/distribution.go index 7e5bf242e39..e105a8dac79 100644 --- a/grype/presenter/models/distribution.go +++ b/grype/presenter/models/distribution.go @@ -34,7 +34,7 @@ func newDistribution(r *linux.Release) distribution { return distribution{ Name: d.Name(), - Version: d.FullVersion(), + Version: d.Version, IDLike: cleanIDLike(d.IDLike), } } diff --git a/grype/search/distro.go b/grype/search/distro.go index da5ffa809d0..e371b1af0b7 100644 --- a/grype/search/distro.go +++ b/grype/search/distro.go @@ -63,13 +63,13 @@ func matchesDistro(d *distro.Distro, ns *distroNs.Namespace) bool { return false } - ty := namespace.DistroTypeString(d.Type) - - distroType := ns.DistroType() - if distroType != d.Type && distroType != distro.Type(ty) { + distroType := mimicV6DistroTypeOverrides(ns.DistroType()) + targetType := mimicV6DistroTypeOverrides(d.Type) + if distroType != targetType { return false } - return compatibleVersion(d.FullVersion(), ns.Version()) + + return compatibleVersion(d.Version, ns.Version()) } // compatibleVersion returns true when the versions are the same or the partial version describes the matching parts @@ -86,3 +86,34 @@ func compatibleVersion(fullVersion string, partialVersion string) bool { } return false } + +// TODO: this is a temporary workaround... in the long term the mock should more strongly enforce +// data overrides and not require this kind of logic being baked into mocks directly. +func mimicV6DistroTypeOverrides(t distro.Type) distro.Type { + overrideMap := map[string]string{ + "centos": "rhel", + "rocky": "rhel", + "rockylinux": "rhel", + "alma": "rhel", + "almalinux": "rhel", + "gentoo": "rhel", + "archlinux": "arch", + "oracle": "ol", + "oraclelinux": "ol", + "amazon": "amzn", + "amazonlinux": "amzn", + } + + applyMapping := func(i string) distro.Type { + if replacement, exists := distro.IDMapping[i]; exists { + return replacement + } + return distro.Type(i) + } + + if replacement, exists := overrideMap[string(t)]; exists { + return applyMapping(replacement) + } + + return applyMapping(string(t)) +} From d9a2cd27d568bab690c7b7e7e38e4e41cbf41b51 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Fri, 28 Mar 2025 14:56:29 -0400 Subject: [PATCH 030/109] add CVSS metrics in search JSON output (#2568) Signed-off-by: Alex Goodman --- .../internal/dbsearch/vulnerabilities.go | 42 +++++++ .../internal/dbsearch/vulnerabilities_test.go | 45 +++++-- grype/db/v6/severity.go | 73 +---------- grype/db/v6/severity_test.go | 101 --------------- internal/cvss/metrics.go | 75 ++++++++++++ internal/cvss/metrics_test.go | 115 ++++++++++++++++++ 6 files changed, 273 insertions(+), 178 deletions(-) create mode 100644 internal/cvss/metrics.go create mode 100644 internal/cvss/metrics_test.go diff --git a/cmd/grype/cli/commands/internal/dbsearch/vulnerabilities.go b/cmd/grype/cli/commands/internal/dbsearch/vulnerabilities.go index e53f30a7c39..6b4c7622af0 100644 --- a/cmd/grype/cli/commands/internal/dbsearch/vulnerabilities.go +++ b/cmd/grype/cli/commands/internal/dbsearch/vulnerabilities.go @@ -7,6 +7,7 @@ import ( "time" v6 "github.com/anchore/grype/grype/db/v6" + "github.com/anchore/grype/internal/cvss" "github.com/anchore/grype/internal/log" ) @@ -83,6 +84,23 @@ type EPSS struct { Date string `json:"date"` } +type CVSSSeverity struct { + // Vector is the CVSS assessment as a parameterized string + Vector string `json:"vector"` + + // Version is the CVSS version (e.g. "3.0") + Version string `json:"version,omitempty"` + + // Metrics is the CVSS quantitative assessment based on the vector + Metrics CvssMetrics `json:"metrics"` +} + +type CvssMetrics struct { + BaseScore float64 `json:"baseScore"` + ExploitabilityScore *float64 `json:"exploitabilityScore,omitempty"` + ImpactScore *float64 `json:"impactScore,omitempty"` +} + type vulnerabilityAffectedPackageJoin struct { Vulnerability v6.VulnerabilityHandle OperatingSystems []v6.OperatingSystem @@ -111,6 +129,7 @@ func newVulnerabilityInfo(vuln v6.VulnerabilityHandle, vc vulnerabilityDecoratio if vuln.BlobValue != nil { blob = *vuln.BlobValue } + patchCVSSMetrics(&blob) return VulnerabilityInfo{ Model: vuln, VulnerabilityBlob: blob, @@ -124,6 +143,29 @@ func newVulnerabilityInfo(vuln v6.VulnerabilityHandle, vc vulnerabilityDecoratio } } +func patchCVSSMetrics(blob *v6.VulnerabilityBlob) { + for i := range blob.Severities { + sev := &blob.Severities[i] + if val, ok := sev.Value.(v6.CVSSSeverity); ok { + met, err := cvss.ParseMetricsFromVector(val.Vector) + if err != nil { + log.WithFields("vector", val.Vector, "error", err).Debug("unable to parse CVSS vector") + continue + } + newSev := CVSSSeverity{ + Vector: val.Vector, + Version: val.Version, + Metrics: CvssMetrics{ + BaseScore: met.BaseScore, + ExploitabilityScore: met.ExploitabilityScore, + ImpactScore: met.ImpactScore, + }, + } + sev.Value = newSev + } + } +} + func newOperatingSystems(oss []v6.OperatingSystem) (os []OperatingSystem) { for _, o := range oss { os = append(os, OperatingSystem{ diff --git a/cmd/grype/cli/commands/internal/dbsearch/vulnerabilities_test.go b/cmd/grype/cli/commands/internal/dbsearch/vulnerabilities_test.go index 285addb5cf5..0536611388f 100644 --- a/cmd/grype/cli/commands/internal/dbsearch/vulnerabilities_test.go +++ b/cmd/grype/cli/commands/internal/dbsearch/vulnerabilities_test.go @@ -113,7 +113,20 @@ func TestVulnerabilities(t *testing.T) { PublishedDate: ptr(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)), ModifiedDate: ptr(time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC)), Provider: &v6.Provider{ID: "provider1"}, - BlobValue: &v6.VulnerabilityBlob{Description: "Test description"}, + BlobValue: &v6.VulnerabilityBlob{ + Description: "Test description", + Severities: []v6.Severity{ + { + Scheme: v6.SeveritySchemeCVSS, + Value: v6.CVSSSeverity{ + Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + Version: "3.1", + }, + Source: "nvd", + Rank: 1, + }, + }, + }, }, }, nil) @@ -156,12 +169,30 @@ func TestVulnerabilities(t *testing.T) { expected := []Vulnerability{ { VulnerabilityInfo: VulnerabilityInfo{ - VulnerabilityBlob: v6.VulnerabilityBlob{Description: "Test description"}, - Provider: "provider1", - Status: "active", - PublishedDate: ptr(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)), - ModifiedDate: ptr(time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC)), - WithdrawnDate: nil, + VulnerabilityBlob: v6.VulnerabilityBlob{ + Description: "Test description", + Severities: []v6.Severity{ + { + Scheme: "CVSS", + Value: CVSSSeverity{ + Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + Version: "3.1", + Metrics: CvssMetrics{ + BaseScore: 7.5, + ExploitabilityScore: ptr(3.9), + ImpactScore: ptr(3.6), + }, + }, + Source: "nvd", + Rank: 1, + }, + }, + }, + Provider: "provider1", + Status: "active", + PublishedDate: ptr(time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC)), + ModifiedDate: ptr(time.Date(2023, 2, 1, 0, 0, 0, 0, time.UTC)), + WithdrawnDate: nil, KnownExploited: []KnownExploited{ { CVE: "CVE-1234-5678", diff --git a/grype/db/v6/severity.go b/grype/db/v6/severity.go index 0c7c4b36979..74b14fefe18 100644 --- a/grype/db/v6/severity.go +++ b/grype/db/v6/severity.go @@ -2,15 +2,9 @@ package v6 import ( "fmt" - "math" - "strings" - - gocvss20 "github.com/pandatix/go-cvss/20" - gocvss30 "github.com/pandatix/go-cvss/30" - gocvss31 "github.com/pandatix/go-cvss/31" - gocvss40 "github.com/pandatix/go-cvss/40" "github.com/anchore/grype/grype/vulnerability" + "github.com/anchore/grype/internal/cvss" "github.com/anchore/grype/internal/log" ) @@ -35,7 +29,7 @@ func extractSeverity(severity any) (vulnerability.Severity, error) { case string: return vulnerability.ParseSeverity(sev), nil case CVSSSeverity: - metrics, err := parseCVSS(sev.Vector) + metrics, err := cvss.ParseMetricsFromVector(sev.Vector) if err != nil { return vulnerability.UnknownSeverity, fmt.Errorf("unable to parse CVSS vector: %w", err) } @@ -48,67 +42,6 @@ func extractSeverity(severity any) (vulnerability.Severity, error) { } } -func parseCVSS(vector string) (*vulnerability.CvssMetrics, error) { - switch { - case strings.HasPrefix(vector, "CVSS:3.0"): - cvss, err := gocvss30.ParseVector(vector) - if err != nil { - return nil, fmt.Errorf("unable to parse CVSS v3 vector: %w", err) - } - ex := roundScore(cvss.Exploitability()) - im := roundScore(cvss.Impact()) - return &vulnerability.CvssMetrics{ - BaseScore: roundScore(cvss.BaseScore()), - ExploitabilityScore: &ex, - ImpactScore: &im, - }, nil - case strings.HasPrefix(vector, "CVSS:3.1"): - cvss, err := gocvss31.ParseVector(vector) - if err != nil { - return nil, fmt.Errorf("unable to parse CVSS v3.1 vector: %w", err) - } - ex := roundScore(cvss.Exploitability()) - im := roundScore(cvss.Impact()) - return &vulnerability.CvssMetrics{ - BaseScore: roundScore(cvss.BaseScore()), - ExploitabilityScore: &ex, - ImpactScore: &im, - }, nil - case strings.HasPrefix(vector, "CVSS:4.0"): - cvss, err := gocvss40.ParseVector(vector) - if err != nil { - return nil, fmt.Errorf("unable to parse CVSS v4.0 vector: %w", err) - } - // there are no exploitability and impact scores in CVSS v4.0 - return &vulnerability.CvssMetrics{ - BaseScore: roundScore(cvss.Score()), - }, nil - default: - // should be CVSS v2.0 or is invalid - cvss, err := gocvss20.ParseVector(vector) - if err != nil { - return nil, fmt.Errorf("unable to parse CVSS v2 vector: %w", err) - } - ex := roundScore(cvss.Exploitability()) - im := roundScore(cvss.Impact()) - return &vulnerability.CvssMetrics{ - BaseScore: roundScore(cvss.BaseScore()), - ExploitabilityScore: &ex, - ImpactScore: &im, - }, nil - } -} - -// roundScore rounds the score to the nearest tenth based on first.org rounding rules -// see https://www.first.org/cvss/v3.1/specification-document#Appendix-A---Floating-Point-Rounding -func roundScore(score float64) float64 { - intInput := int(math.Round(score * 100000)) - if intInput%10000 == 0 { - return float64(intInput) / 100000.0 - } - return (math.Floor(float64(intInput)/10000.0) + 1) / 10.0 -} - func interpretCVSS(score float64, version string) vulnerability.Severity { switch version { case "2.0": @@ -178,7 +111,7 @@ func toCvss(severities ...Severity) []vulnerability.Cvss { } var usedMetrics vulnerability.CvssMetrics // though the DB has the base score, we parse the vector for all metrics - metrics, err := parseCVSS(cvssSev.Vector) + metrics, err := cvss.ParseMetricsFromVector(cvssSev.Vector) if err != nil { log.WithFields("vector", cvssSev.Vector, "error", err).Warn("unable to parse CVSS vector") continue diff --git a/grype/db/v6/severity_test.go b/grype/db/v6/severity_test.go index e8b3b41f63a..3b943d0f448 100644 --- a/grype/db/v6/severity_test.go +++ b/grype/db/v6/severity_test.go @@ -126,107 +126,6 @@ func TestExtractSeverity(t *testing.T) { } } -func TestParseCVSS(t *testing.T) { - tests := []struct { - name string - vector string - expectedMetrics *vulnerability.CvssMetrics - wantErr require.ErrorAssertionFunc - }{ - { - name: "valid CVSS 2.0", - vector: "AV:N/AC:L/Au:N/C:P/I:P/A:P", - expectedMetrics: &vulnerability.CvssMetrics{ - BaseScore: 7.5, - ExploitabilityScore: ptr(10.0), - ImpactScore: ptr(6.5), - }, - }, - { - name: "valid CVSS 3.0", - vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", - expectedMetrics: &vulnerability.CvssMetrics{ - BaseScore: 9.8, - ExploitabilityScore: ptr(3.9), - ImpactScore: ptr(5.9), - }, - }, - { - name: "valid CVSS 3.1", - vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", - expectedMetrics: &vulnerability.CvssMetrics{ - BaseScore: 9.8, - ExploitabilityScore: ptr(3.9), - ImpactScore: ptr(5.9), - }, - }, - { - name: "valid CVSS 4.0", - vector: "CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:N/VI:H/VA:L/SC:L/SI:H/SA:L/MAC:L/MAT:P/MPR:N/S:N/R:A/RE:L/U:Clear", - expectedMetrics: &vulnerability.CvssMetrics{ - BaseScore: 9.1, - }, - }, - { - name: "invalid CVSS 2.0", - vector: "AV:N/AC:INVALID", - wantErr: require.Error, - }, - { - name: "invalid CVSS 3.0", - vector: "CVSS:3.0/AV:INVALID", - wantErr: require.Error, - }, - { - name: "invalid CVSS 3.1", - vector: "CVSS:3.1/AV:INVALID", - wantErr: require.Error, - }, - { - name: "invalid CVSS 4.0", - vector: "CVSS:4.0/AV:INVALID", - wantErr: require.Error, - }, - { - name: "empty vector", - vector: "", - wantErr: require.Error, - }, - { - name: "malformed vector", - vector: "INVALID:VECTOR", - wantErr: require.Error, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.wantErr == nil { - tt.wantErr = require.NoError - } - result, err := parseCVSS(tt.vector) - tt.wantErr(t, err) - if err != nil { - assert.Nil(t, result) - return - } - - require.NotNil(t, result) - assert.Equal(t, tt.expectedMetrics.BaseScore, result.BaseScore, "given vector: %s", tt.vector) - - if tt.expectedMetrics.ExploitabilityScore != nil { - require.NotNil(t, result.ExploitabilityScore) - assert.Equal(t, *tt.expectedMetrics.ExploitabilityScore, *result.ExploitabilityScore, "given vector: %s", tt.vector) - } - - if tt.expectedMetrics.ImpactScore != nil { - require.NotNil(t, result.ImpactScore) - assert.Equal(t, *tt.expectedMetrics.ImpactScore, *result.ImpactScore, "given vector: %s", tt.vector) - } - }) - } -} - func TestExtractSeverities(t *testing.T) { tests := []struct { name string diff --git a/internal/cvss/metrics.go b/internal/cvss/metrics.go new file mode 100644 index 00000000000..2d1a0fcc925 --- /dev/null +++ b/internal/cvss/metrics.go @@ -0,0 +1,75 @@ +package cvss + +import ( + "fmt" + "math" + "strings" + + gocvss20 "github.com/pandatix/go-cvss/20" + gocvss30 "github.com/pandatix/go-cvss/30" + gocvss31 "github.com/pandatix/go-cvss/31" + gocvss40 "github.com/pandatix/go-cvss/40" + + "github.com/anchore/grype/grype/vulnerability" +) + +func ParseMetricsFromVector(vector string) (*vulnerability.CvssMetrics, error) { + switch { + case strings.HasPrefix(vector, "CVSS:3.0"): + cvss, err := gocvss30.ParseVector(vector) + if err != nil { + return nil, fmt.Errorf("unable to parse CVSS v3 vector: %w", err) + } + ex := roundScore(cvss.Exploitability()) + im := roundScore(cvss.Impact()) + return &vulnerability.CvssMetrics{ + BaseScore: roundScore(cvss.BaseScore()), + ExploitabilityScore: &ex, + ImpactScore: &im, + }, nil + case strings.HasPrefix(vector, "CVSS:3.1"): + cvss, err := gocvss31.ParseVector(vector) + if err != nil { + return nil, fmt.Errorf("unable to parse CVSS v3.1 vector: %w", err) + } + ex := roundScore(cvss.Exploitability()) + im := roundScore(cvss.Impact()) + return &vulnerability.CvssMetrics{ + BaseScore: roundScore(cvss.BaseScore()), + ExploitabilityScore: &ex, + ImpactScore: &im, + }, nil + case strings.HasPrefix(vector, "CVSS:4.0"): + cvss, err := gocvss40.ParseVector(vector) + if err != nil { + return nil, fmt.Errorf("unable to parse CVSS v4.0 vector: %w", err) + } + // there are no exploitability and impact scores in CVSS v4.0 + return &vulnerability.CvssMetrics{ + BaseScore: roundScore(cvss.Score()), + }, nil + default: + // should be CVSS v2.0 or is invalid + cvss, err := gocvss20.ParseVector(vector) + if err != nil { + return nil, fmt.Errorf("unable to parse CVSS v2 vector: %w", err) + } + ex := roundScore(cvss.Exploitability()) + im := roundScore(cvss.Impact()) + return &vulnerability.CvssMetrics{ + BaseScore: roundScore(cvss.BaseScore()), + ExploitabilityScore: &ex, + ImpactScore: &im, + }, nil + } +} + +// roundScore rounds the score to the nearest tenth based on first.org rounding rules +// see https://www.first.org/cvss/v3.1/specification-document#Appendix-A---Floating-Point-Rounding +func roundScore(score float64) float64 { + intInput := int(math.Round(score * 100000)) + if intInput%10000 == 0 { + return float64(intInput) / 100000.0 + } + return (math.Floor(float64(intInput)/10000.0) + 1) / 10.0 +} diff --git a/internal/cvss/metrics_test.go b/internal/cvss/metrics_test.go new file mode 100644 index 00000000000..dedbc05d5a8 --- /dev/null +++ b/internal/cvss/metrics_test.go @@ -0,0 +1,115 @@ +package cvss + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/anchore/grype/grype/vulnerability" +) + +func TestParseMetricsFromVector(t *testing.T) { + tests := []struct { + name string + vector string + expectedMetrics *vulnerability.CvssMetrics + wantErr require.ErrorAssertionFunc + }{ + { + name: "valid CVSS 2.0", + vector: "AV:N/AC:L/Au:N/C:P/I:P/A:P", + expectedMetrics: &vulnerability.CvssMetrics{ + BaseScore: 7.5, + ExploitabilityScore: ptr(10.0), + ImpactScore: ptr(6.5), + }, + }, + { + name: "valid CVSS 3.0", + vector: "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + expectedMetrics: &vulnerability.CvssMetrics{ + BaseScore: 9.8, + ExploitabilityScore: ptr(3.9), + ImpactScore: ptr(5.9), + }, + }, + { + name: "valid CVSS 3.1", + vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + expectedMetrics: &vulnerability.CvssMetrics{ + BaseScore: 9.8, + ExploitabilityScore: ptr(3.9), + ImpactScore: ptr(5.9), + }, + }, + { + name: "valid CVSS 4.0", + vector: "CVSS:4.0/AV:N/AC:H/AT:P/PR:L/UI:N/VC:N/VI:H/VA:L/SC:L/SI:H/SA:L/MAC:L/MAT:P/MPR:N/S:N/R:A/RE:L/U:Clear", + expectedMetrics: &vulnerability.CvssMetrics{ + BaseScore: 9.1, + }, + }, + { + name: "invalid CVSS 2.0", + vector: "AV:N/AC:INVALID", + wantErr: require.Error, + }, + { + name: "invalid CVSS 3.0", + vector: "CVSS:3.0/AV:INVALID", + wantErr: require.Error, + }, + { + name: "invalid CVSS 3.1", + vector: "CVSS:3.1/AV:INVALID", + wantErr: require.Error, + }, + { + name: "invalid CVSS 4.0", + vector: "CVSS:4.0/AV:INVALID", + wantErr: require.Error, + }, + { + name: "empty vector", + vector: "", + wantErr: require.Error, + }, + { + name: "malformed vector", + vector: "INVALID:VECTOR", + wantErr: require.Error, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.wantErr == nil { + tt.wantErr = require.NoError + } + result, err := ParseMetricsFromVector(tt.vector) + tt.wantErr(t, err) + if err != nil { + assert.Nil(t, result) + return + } + + require.NotNil(t, result) + assert.Equal(t, tt.expectedMetrics.BaseScore, result.BaseScore, "given vector: %s", tt.vector) + + if tt.expectedMetrics.ExploitabilityScore != nil { + require.NotNil(t, result.ExploitabilityScore) + assert.Equal(t, *tt.expectedMetrics.ExploitabilityScore, *result.ExploitabilityScore, "given vector: %s", tt.vector) + } + + if tt.expectedMetrics.ImpactScore != nil { + require.NotNil(t, result.ImpactScore) + assert.Equal(t, *tt.expectedMetrics.ImpactScore, *result.ImpactScore, "given vector: %s", tt.vector) + } + }) + } +} + +func ptr(f float64) *float64 { + return &f +} From 248ed8f7671863cf3032f4afc6a58f30ebf823fd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:44:30 -0400 Subject: [PATCH 031/109] chore(deps): bump 8398a7/action-slack from 3.16.2 to 3.18.0 (#2567) Bumps [8398a7/action-slack](https://github.com/8398a7/action-slack) from 3.16.2 to 3.18.0. - [Release notes](https://github.com/8398a7/action-slack/releases) - [Commits](https://github.com/8398a7/action-slack/compare/28ba43ae48961b90635b50953d216767a6bea486...1750b5085f3ec60384090fb7c52965ef822e869e) --- updated-dependencies: - dependency-name: 8398a7/action-slack 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/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 04fe4ec4cfd..e2cd39ec005 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -163,7 +163,7 @@ jobs: with: artifact-name: sbom.spdx.json - - uses: 8398a7/action-slack@28ba43ae48961b90635b50953d216767a6bea486 # v3.16.2 + - uses: 8398a7/action-slack@1750b5085f3ec60384090fb7c52965ef822e869e # v3.18.0 continue-on-error: true with: status: ${{ job.status }} From 51603ef5e24c65f4ac9c7fda2fbfbe4cca89b91c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:44:36 -0400 Subject: [PATCH 032/109] chore(deps): bump github.com/docker/docker (#2565) Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.0.2+incompatible to 28.0.4+incompatible. - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v28.0.2...v28.0.4) --- 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 c7bf8621426..426486202d7 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/charmbracelet/bubbletea v1.3.4 github.com/charmbracelet/lipgloss v1.1.0 github.com/dave/jennifer v1.7.1 - github.com/docker/docker v28.0.2+incompatible + github.com/docker/docker v28.0.4+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 github.com/gabriel-vasile/mimetype v1.4.8 diff --git a/go.sum b/go.sum index 05212b42ae0..8b7fb2a8e3a 100644 --- a/go.sum +++ b/go.sum @@ -875,8 +875,8 @@ github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 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 v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8= -github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok= +github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= From d7cc16e8b24d845f92d1bf4fd8b226934874e190 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:44:43 -0400 Subject: [PATCH 033/109] chore(deps): bump github/codeql-action from 3.28.12 to 3.28.13 (#2562) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.12 to 3.28.13. - [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/5f8171a638ada777af81d42b55959a643bb29017...1b549b9259bda1cb5ddde3b41741a82a2d15a841) --- 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 | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index f4829b3f5e0..2aa25278cca 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -63,7 +63,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12 + uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -85,4 +85,4 @@ jobs: run: make grype - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@5f8171a638ada777af81d42b55959a643bb29017 # v3.28.12 + uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 3f1200015f9..e9b33c32b5d 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -38,6 +38,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@5f8171a638ada777af81d42b55959a643bb29017 # v1.0.26 + uses: github/codeql-action/upload-sarif@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v1.0.26 with: sarif_file: results.sarif From 45ebcd44aac35a25ff784d88e2f82629d89e4813 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:44:50 -0400 Subject: [PATCH 034/109] chore(deps): bump actions/cache from 4.2.2 to 4.2.3 (#2552) Bumps [actions/cache](https://github.com/actions/cache) from 4.2.2 to 4.2.3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/d4323d4df104b026a6aa633fdb11d772146be0bf...5a3ec84eff668545956fd18022155c47e93e2684) --- updated-dependencies: - dependency-name: actions/cache 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 | 2 +- .github/workflows/validations.yaml | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 2aa25278cca..3a630c82881 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -46,7 +46,7 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Utilize Go Module Cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: | ~/go/pkg/mod diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 575a3a93c8e..0ebae590caf 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -103,7 +103,7 @@ jobs: uses: ./.github/actions/bootstrap - name: Restore integration test cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3 with: path: ${{ github.workspace }}/test/integration/test-fixtures/cache key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }} @@ -134,7 +134,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@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + uses: actions/cache/save@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3 with: path: snapshot key: snapshot-build-${{ github.run_id }} @@ -147,7 +147,7 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 - name: Download snapshot build - uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3 with: path: snapshot key: snapshot-build-${{ github.run_id }} @@ -188,14 +188,14 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 - name: Download snapshot build - uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3 with: path: snapshot key: snapshot-build-${{ github.run_id }} - name: Restore install.sh test image cache id: install-test-image-cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3 with: path: ${{ github.workspace }}/test/install/cache key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }} @@ -223,14 +223,14 @@ jobs: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 - name: Download snapshot build - uses: actions/cache/restore@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3 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@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3 with: path: image.tar key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }} @@ -251,13 +251,13 @@ jobs: uses: ./.github/actions/bootstrap - name: Restore CLI test-fixture cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3 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@d4323d4df104b026a6aa633fdb11d772146be0bf #v4.2.2 + uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 #v4.2.3 with: path: snapshot key: snapshot-build-${{ github.run_id }} From b9ef3745392a0609aaa5c095f684b31da0c49d99 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:44:58 -0400 Subject: [PATCH 035/109] chore(deps): bump actions/upload-artifact from 4.6.1 to 4.6.2 (#2551) Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.1 to 4.6.2. - [Release notes](https://github.com/actions/upload-artifact/releases) - [Commits](https://github.com/actions/upload-artifact/compare/4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1...ea165f8d65b6e75b540449e92b4886f43607fa02) --- updated-dependencies: - dependency-name: actions/upload-artifact 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/validations.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index 0ebae590caf..c04758517ed 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -60,7 +60,7 @@ jobs: - name: Upload the provider state archive if: ${{ failure() }} - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: qg-capture-state path: qg-capture-state.tar.gz From e0dd3dcb2fece1b12945a11cf821666af7f9caf8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 10:45:16 -0400 Subject: [PATCH 036/109] chore(deps): bump actions/cache in /.github/actions/bootstrap (#2549) Bumps [actions/cache](https://github.com/actions/cache) from 4.2.2 to 4.2.3. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/d4323d4df104b026a6aa633fdb11d772146be0bf...5a3ec84eff668545956fd18022155c47e93e2684) --- updated-dependencies: - dependency-name: actions/cache 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/actions/bootstrap/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index a9d13cfd262..21422105ebe 100644 --- a/.github/actions/bootstrap/action.yaml +++ b/.github/actions/bootstrap/action.yaml @@ -43,7 +43,7 @@ runs: - name: Restore tool cache id: tool-cache - uses: actions/cache@d4323d4df104b026a6aa633fdb11d772146be0bf # v4.2.2 + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 if: inputs.tools == 'true' with: path: ${{ github.workspace }}/.tool From a75e7e9f5e2e803bdf83192f6df5a206ecb9598d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:58:07 -0400 Subject: [PATCH 037/109] chore(deps): bump actions/setup-python in /.github/actions/bootstrap (#2564) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.4.0 to 5.5.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/42375524e23c412d93fb67b49958b491fce71c38...8d9ed9ac5c53483de85588cdf95a591a75ab9f55) --- updated-dependencies: - dependency-name: actions/setup-python 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/actions/bootstrap/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index 21422105ebe..7967eb2ad16 100644 --- a/.github/actions/bootstrap/action.yaml +++ b/.github/actions/bootstrap/action.yaml @@ -37,7 +37,7 @@ runs: with: go-version: ${{ inputs.go-version }} - - uses: actions/setup-python@42375524e23c412d93fb67b49958b491fce71c38 # v5.4.0 + - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 with: python-version: ${{ inputs.python-version }} From 0ca1080d18f23fb21c89073651af316e532d39c5 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 1 Apr 2025 09:39:43 -0400 Subject: [PATCH 038/109] unpin go version in ci (#2572) Signed-off-by: Alex Goodman --- .github/actions/bootstrap/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index 7967eb2ad16..ebf9508cb9e 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.24.x" + default: ">= 1.24" python-version: description: "Python version to install" required: true From 57d4a7c965704c186e246ce71967464fd95948a3 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, 1 Apr 2025 11:27:24 -0400 Subject: [PATCH 039/109] chore(deps): update anchore dependencies (#2570) * chore(deps): update anchore dependencies Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix integration tests Signed-off-by: Alex Goodman --------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Alex Goodman Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com> Co-authored-by: Alex Goodman --- go.mod | 30 +++++------ go.sum | 72 ++++++++++++------------- test/integration/match_by_image_test.go | 16 +++++- 3 files changed, 63 insertions(+), 55 deletions(-) diff --git a/go.mod b/go.mod index 426486202d7..1c61697cea0 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,8 @@ require ( github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 - github.com/anchore/stereoscope v0.1.1-0.20250320125929-d35c173d28fc - github.com/anchore/syft v1.21.1-0.20250320141945-410b85e1c959 + github.com/anchore/stereoscope v0.1.2 + github.com/anchore/syft v1.22.0 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 @@ -64,7 +64,7 @@ require ( github.com/wagoodman/go-presenter v0.0.0-20211015174752-f9c01afc824b github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 + golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 golang.org/x/time v0.11.0 golang.org/x/tools v0.31.0 gopkg.in/yaml.v3 v3.0.1 @@ -93,13 +93,14 @@ require ( github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.11.7 // indirect - github.com/ProtonMail/go-crypto v1.1.5 // indirect + github.com/ProtonMail/go-crypto v1.1.6 // indirect github.com/STARRY-S/zip v0.2.1 // indirect github.com/acobaugh/osrelease v0.1.0 // indirect github.com/agext/levenshtein v1.2.1 // indirect github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2 // indirect github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect + github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6 // indirect github.com/andybalholm/brotli v1.1.1 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/aquasecurity/go-version v0.0.1 // indirect @@ -140,14 +141,13 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/cli v28.0.1+incompatible // indirect + github.com/docker/cli v28.0.4+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-connections v0.5.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.20230904184137-39efe44ab707 // indirect - github.com/edsrzf/mmap-go v1.1.0 // indirect github.com/elliotchance/phpserialize v1.4.0 // indirect github.com/emirpasic/gods v1.18.1 // indirect github.com/envoyproxy/go-control-plane v0.13.1 // indirect @@ -171,7 +171,7 @@ require ( github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/licensecheck v0.3.1 // indirect - github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect + github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect github.com/google/s2a-go v0.1.8 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect github.com/googleapis/gax-go/v2 v2.14.1 // indirect @@ -239,11 +239,9 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect - github.com/saferwall/pe v1.5.6 // indirect github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/sassoftware/go-rpmutils v0.4.0 // indirect - github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/skeema/knownhosts v1.3.1 // indirect @@ -256,8 +254,8 @@ require ( github.com/spf13/viper v1.20.0 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/sylabs/sif/v2 v2.20.2 // indirect - github.com/sylabs/squashfs v1.0.5 // indirect + github.com/sylabs/sif/v2 v2.21.1 // indirect + github.com/sylabs/squashfs v1.0.6 // indirect github.com/therootcompany/xz v1.0.1 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect @@ -286,7 +284,7 @@ require ( go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.37.0 // indirect + golang.org/x/net v0.38.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect golang.org/x/sync v0.12.0 // indirect golang.org/x/sys v0.31.0 // indirect @@ -298,12 +296,12 @@ require ( google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect google.golang.org/grpc v1.67.3 // indirect - google.golang.org/protobuf v1.36.3 // indirect + google.golang.org/protobuf v1.36.4 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect - modernc.org/libc v1.61.13 // indirect + modernc.org/libc v1.62.1 // indirect modernc.org/mathutil v1.7.1 // indirect - modernc.org/memory v1.8.2 // indirect - modernc.org/sqlite v1.36.1 // indirect + modernc.org/memory v1.9.1 // indirect + modernc.org/sqlite v1.37.0 // indirect ) // this is a breaking change, so we need to pin the version until glebarez/go-sqlite is updated to use internal/libc diff --git a/go.sum b/go.sum index 8b7fb2a8e3a..d16e05ae3ee 100644 --- a/go.sum +++ b/go.sum @@ -662,8 +662,8 @@ github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4= -github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= +github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg= github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -700,16 +700,18 @@ github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIj 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= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= +github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6 h1:Ha+LSCVuXYSYGi7wIkJK6G8g6jI3LH7y6LbyEVyp4Io= +github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6/go.mod h1:+9oM3XUy8iea/vWj9FhZ9bQGUBN8JpPxxJm5Wbcx9XM= github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0vW0nnNKJfJieyH/TZ9UYAnTZs5/gHTdAe8= github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ= github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE+o2gozGEBoUMpX27lsku+xrMwlmBZJtbg= github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= -github.com/anchore/stereoscope v0.1.1-0.20250320125929-d35c173d28fc h1:wyiNipBKQApiCX56jeDNTrnkHddF4laJwze6ldYr+ZA= -github.com/anchore/stereoscope v0.1.1-0.20250320125929-d35c173d28fc/go.mod h1:o4Gy3h1KLi27+v53O58whB9yCqcrgGdhthRc3st4whk= -github.com/anchore/syft v1.21.1-0.20250320141945-410b85e1c959 h1:yywFcavCivMcW5Bjn7wO9XW+9D6GbLt6TRhdErn+KSs= -github.com/anchore/syft v1.21.1-0.20250320141945-410b85e1c959/go.mod h1:xQpHtL2mfQvMcEf8PqR/sbUlpcvaIlv3f/m2ETEooas= +github.com/anchore/stereoscope v0.1.2 h1:0+Jcf7hoImYKfrH2XzN6vvbmbpm68A/woabC43gZ4eU= +github.com/anchore/stereoscope v0.1.2/go.mod h1:kU4LkiocOimxuSnlmJgilHCfDMCR5t/UroKIpzI8jNw= +github.com/anchore/syft v1.22.0 h1:lmNuy1uQ9GZeO6HPms3ik3W0hTE9MxP46vtC3vssBe4= +github.com/anchore/syft v1.22.0/go.mod h1:5U0av1cf7i/O2VusM29HTsVYUsARjBvgPCbP4RtSEyk= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -871,8 +873,8 @@ github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4i github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs= -github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A= +github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 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 v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok= @@ -892,8 +894,6 @@ github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdf github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= 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 v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o= github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE= github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY= @@ -1100,8 +1100,8 @@ github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo= -github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs= +github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM= github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA= @@ -1453,8 +1453,6 @@ github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfF github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/saferwall/pe v1.5.6 h1:DrRLnoQFxHWJ5lJUmrH7X2L0xeUu6SUS95Dc61eW2Yc= -github.com/saferwall/pe v1.5.6/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= @@ -1472,8 +1470,6 @@ github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY= github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI= -github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d h1:RQqyEogx5J6wPdoxqL132b100j8KjcVHO1c0KLRoIhc= -github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d/go.mod h1:PegD7EVqlN88z7TpCqH92hHP+GBpfomGCCnw1PFtNOA= 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.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8= @@ -1537,10 +1533,10 @@ github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/sylabs/sif/v2 v2.20.2 h1:HGEPzauCHhIosw5o6xmT3jczuKEuaFzSfdjAsH33vYw= -github.com/sylabs/sif/v2 v2.20.2/go.mod h1:WyYryGRaR4Wp21SAymm5pK0p45qzZCSRiZMFvUZiuhc= -github.com/sylabs/squashfs v1.0.5 h1:KExbrvScUVk/drh6gPfnZY7T7SZ+cQpcSJEOK32dppU= -github.com/sylabs/squashfs v1.0.5/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE= +github.com/sylabs/sif/v2 v2.21.1 h1:GZ0b5//AFAqJEChd8wHV/uSKx/l1iuGYwjR8nx+4wPI= +github.com/sylabs/sif/v2 v2.21.1/go.mod h1:YoqEGQnb5x/ItV653bawXHZJOXQaEWpGwHsSD3YePJI= +github.com/sylabs/squashfs v1.0.6 h1:PvJcDzxr+vIm2kH56mEMbaOzvGu79gK7P7IX+R7BDZI= +github.com/sylabs/squashfs v1.0.6/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE= github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo= github.com/terminalstatic/go-xsd-validate v0.1.6/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw= github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw= @@ -1692,8 +1688,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0 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-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= +golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= 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= @@ -1810,8 +1806,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= -golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 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= @@ -2377,8 +2373,8 @@ google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= -google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -2419,21 +2415,21 @@ lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.2/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= modernc.org/cc/v3 v3.36.3/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI= -modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0= -modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= +modernc.org/cc/v4 v4.25.2 h1:T2oH7sZdGvTaie0BRNFbIYsabzCxUQg8nLqCdQ2i0ic= +modernc.org/cc/v4 v4.25.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0= modernc.org/ccgo/v3 v3.0.0-20220428102840-41399a37e894/go.mod h1:eI31LL8EwEBKPpNpA4bU1/i+sKOwOrQy8D87zWUcRZc= modernc.org/ccgo/v3 v3.0.0-20220430103911-bc99d88307be/go.mod h1:bwdAnOoaIt8Ax9YdWGjxWsdkPcZyRPHqrOvJxaKAKGw= modernc.org/ccgo/v3 v3.16.4/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.6/go.mod h1:tGtX0gE9Jn7hdZFeU88slbTh1UtCYKusWOoCJuvkWsQ= modernc.org/ccgo/v3 v3.16.8/go.mod h1:zNjwkizS+fIFDrDjIAgBSCLkWbJuHF+ar3QRn+Z9aws= modernc.org/ccgo/v3 v3.16.9/go.mod h1:zNMzC9A9xeNUepy6KuZBbugn3c0Mc9TeiJO4lgvkJDo= -modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo= -modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo= +modernc.org/ccgo/v4 v4.25.1 h1:TFSzPrAGmDsdnhT9X2UrcPMI3N/mJ9/X9ykKXwLhDsU= +modernc.org/ccgo/v4 v4.25.1/go.mod h1:njjuAYiPflywOOrm3B7kCB444ONP5pAVr8PIEoE0uDw= modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE= modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ= -modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw= -modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= +modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI= +modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito= modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= modernc.org/libc v0.0.0-20220428101251-2d5f3daf273b/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.16.0/go.mod h1:N4LD6DBE9cf+Dzf9buBlzVJndKr/iJHG97vGLHYnb5A= @@ -2442,8 +2438,8 @@ modernc.org/libc v1.16.17/go.mod h1:hYIV5VZczAmGZAnG15Vdngn5HSF5cSkbvfz2B7GRuVU= modernc.org/libc v1.16.19/go.mod h1:p7Mg4+koNjc8jkqwcoFBJx7tXkpj00G77X7A72jXPXA= modernc.org/libc v1.17.0/go.mod h1:XsgLldpP4aWlPlsjqKRdHPqCxCjISdHfM/yeWC5GyW0= modernc.org/libc v1.17.1/go.mod h1:FZ23b+8LjxZs7XtFMbSzL/EhPxNbfZbErxEHc7cbD9s= -modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8= -modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E= +modernc.org/libc v1.62.1 h1:s0+fv5E3FymN8eJVmnk0llBe6rOxCu/DEU+XygRbS8s= +modernc.org/libc v1.62.1/go.mod h1:iXhATfJQLjG3NWy56a6WVU73lWOcdYVxsvwCgoPljuo= modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E= @@ -2452,8 +2448,8 @@ modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJ modernc.org/memory v1.1.1/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.0/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw= modernc.org/memory v1.2.1/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU= -modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI= -modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU= +modernc.org/memory v1.9.1 h1:V/Z1solwAVmMW1yttq3nDdZPJqV1rM05Ccq6KMSZ34g= +modernc.org/memory v1.9.1/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw= modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8= @@ -2461,8 +2457,8 @@ modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns= modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w= modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE= modernc.org/sqlite v1.18.1/go.mod h1:6ho+Gow7oX5V+OiOQ6Tr4xeqbx13UZ6t+Fw9IRUG4d4= -modernc.org/sqlite v1.36.1 h1:bDa8BJUH4lg6EGkLbahKe/8QqoF8p9gArSc6fTqYhyQ= -modernc.org/sqlite v1.36.1/go.mod h1:7MPwH7Z6bREicF9ZVUR78P1IKuxfZ8mRIDHD0iD+8TU= +modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI= +modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM= modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw= modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0= diff --git a/test/integration/match_by_image_test.go b/test/integration/match_by_image_test.go index 6b43eb9cf45..254fa2459e6 100644 --- a/test/integration/match_by_image_test.go +++ b/test/integration/match_by_image_test.go @@ -177,7 +177,21 @@ func addPythonMatches(t *testing.T, theSource source.Source, catalog *syftPkg.Co func addDotnetMatches(t *testing.T, theSource source.Source, catalog *syftPkg.Collection, provider vulnerability.Provider, theResult *match.Matches) { packages := catalog.PackagesByPath("/dotnet/TestLibrary.deps.json") - if len(packages) != 2 { // TestLibrary + AWSSDK.Core + // 55caef8df7ac822e Pkg(name="TestLibrary" version="1.0.0" type="dotnet" id="55caef8df7ac822e") + // 0012329cdebba0ea Pkg(name="AWSSDK.Core" version="3.7.10.6" type="dotnet" id="0012329cdebba0ea") + // 07ec6fb2adb2cf8f Pkg(name="Microsoft.Extensions.DependencyInjection.Abstractions" version="6.0.0" type="dotnet" id="07ec6fb2adb2cf8f") + // ff03e77b91acca32 Pkg(name="Microsoft.Extensions.DependencyInjection" version="6.0.0" type="dotnet" id="ff03e77b91acca32") + // a1ea42c8f064083e Pkg(name="Microsoft.Extensions.Logging.Abstractions" version="6.0.0" type="dotnet" id="a1ea42c8f064083e") + // aaef85a2649e5d15 Pkg(name="Microsoft.Extensions.Logging" version="6.0.0" type="dotnet" id="aaef85a2649e5d15") + // 4af0fb6a81ba0423 Pkg(name="Microsoft.Extensions.Options" version="6.0.0" type="dotnet" id="4af0fb6a81ba0423") + // cb41a8aefdf40c3a Pkg(name="Microsoft.Extensions.Primitives" version="6.0.0" type="dotnet" id="cb41a8aefdf40c3a") + // 5ee80fba9caa3ab3 Pkg(name="Newtonsoft.Json" version="13.0.1" type="dotnet" id="5ee80fba9caa3ab3") + // df4b5dc73acd1f36 Pkg(name="Serilog.Sinks.Console" version="4.0.1" type="dotnet" id="df4b5dc73acd1f36") + // 023b9ba74c5c5ef5 Pkg(name="Serilog" version="2.10.0" type="dotnet" id="023b9ba74c5c5ef5") + // 430e4d4304a3ff55 Pkg(name="System.Diagnostics.DiagnosticSource" version="6.0.0" type="dotnet" id="430e4d4304a3ff55") + // 42021023d8f87661 Pkg(name="System.Runtime.CompilerServices.Unsafe" version="6.0.0" type="dotnet" id="42021023d8f87661") + // 2bb01d8c22df1e95 Pkg(name="TestCommon" version="1.0.0" type="dotnet" id="2bb01d8c22df1e95") + if len(packages) != 14 { for _, p := range packages { t.Logf("Dotnet Package: %s %+v", p.ID(), p) } From c6be668aca798ce707053508a0c9764638eb6c1f 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, 1 Apr 2025 13:44:50 -0400 Subject: [PATCH 040/109] test: update quality gate db to latest version (#2576) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com> --- test/quality/test-db | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/quality/test-db b/test/quality/test-db index 3073ef8e0e6..9fdc4001a56 100644 --- a/test/quality/test-db +++ b/test/quality/test-db @@ -1 +1 @@ -vulnerability-db_v6.0.2_2025-02-28T01:30:50Z_1740715588.tar.zst +vulnerability-db_v6.0.2_2025-04-01T01:31:39Z_1743480497.tar.zst From 4b35276c4b064f74df0c637bf60c93fafc6f9934 Mon Sep 17 00:00:00 2001 From: Alexandre Barone <48306674+devodev@users.noreply.github.com> Date: Wed, 2 Apr 2025 11:30:06 -0400 Subject: [PATCH 041/109] feat: return exit code 2 for --fail-on errors (#2575) Signed-off-by: Alexandre Barone --- README.md | 6 ++++-- cmd/grype/cli/cli.go | 14 ++++++++++++++ cmd/grype/cli/commands/db_check.go | 7 ++----- go.mod | 13 ++++++------- go.sum | 26 ++++++++++++-------------- grype/grypeerr/errors.go | 6 +++++- 6 files changed, 43 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index e1073c764d1..03c9093835e 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ grype --add-cpes-if-none --distro alpine:3.10 sbom:some-alpine-3.10.spdx.json Software updates are always applied to the latest version of Grype; fixes are not backported to any previous versions of Grype. -In terms of database updates, any version of Grype before v0.51.0 (Oct 2022, before schema v5) will not receive +In terms of database updates, any version of Grype before v0.51.0 (Oct 2022, before schema v5) will not receive vulnerability database updates. You can still build vulnerability databases for unsupported Grype releases by using previous releases of [vunnel](https://github.com/anchore/vunnel) to gather the upstream data and [grype-db](https://github.com/anchore/grype-db) to build databases for unsupported schemas. @@ -353,6 +353,8 @@ For example, here's how you could trigger a CI pipeline failure if any vulnerabi grype ubuntu:latest --fail-on medium ``` +**Note:** Grype returns exit code `2` on vulnerability errors. + ### Specifying matches to ignore If you're seeing Grype report **false positives** or any other vulnerability matches that you just don't want to see, you can tell Grype to **ignore** matches by specifying one or more _"ignore rules"_ in your Grype configuration file (e.g. `~/.grype.yaml`). This causes Grype not to report any vulnerability matches that meet the criteria specified by any of your ignore rules. @@ -709,7 +711,7 @@ default-image-pull-source: "" # same as --name; set the name of the target being analyzed name: "" -# upon scanning, if a severity is found at or above the given severity then the return code will be 1 +# upon scanning, if a severity is found at or above the given severity then the return code will be 2 # default is unset which will skip this validation (options: negligible, low, medium, high, critical) # same as --fail-on ; GRYPE_FAIL_ON_SEVERITY env var fail-on-severity: "" diff --git a/cmd/grype/cli/cli.go b/cmd/grype/cli/cli.go index f19bbb2ef99..164eb94107b 100644 --- a/cmd/grype/cli/cli.go +++ b/cmd/grype/cli/cli.go @@ -1,6 +1,7 @@ package cli import ( + "errors" "os" "runtime/debug" @@ -11,6 +12,7 @@ import ( grypeHandler "github.com/anchore/grype/cmd/grype/cli/ui" "github.com/anchore/grype/cmd/grype/internal/ui" v6 "github.com/anchore/grype/grype/db/v6" + "github.com/anchore/grype/grype/grypeerr" "github.com/anchore/grype/internal/bus" "github.com/anchore/grype/internal/log" "github.com/anchore/grype/internal/redact" @@ -70,6 +72,18 @@ func create(id clio.Identification) (clio.Application, *cobra.Command) { ). WithPostRuns(func(_ *clio.State, _ error) { stereoscope.Cleanup() + }). + WithMapExitCode(func(err error) int { + // return exit code 2 to indicate when a vulnerability severity is discovered + // that is equal or above the given --fail-on severity value. + if errors.Is(err, grypeerr.ErrAboveSeverityThreshold) { + return 2 + } + // return exit code 100 to indicate a DB upgrade is available (cmd: db check). + if errors.Is(err, grypeerr.ErrDBUpgradeAvailable) { + return 100 + } + return 1 }) app := clio.New(*clioCfg) diff --git a/cmd/grype/cli/commands/db_check.go b/cmd/grype/cli/commands/db_check.go index 18bc49b89e5..20d035636c5 100644 --- a/cmd/grype/cli/commands/db_check.go +++ b/cmd/grype/cli/commands/db_check.go @@ -12,13 +12,10 @@ import ( "github.com/anchore/grype/cmd/grype/cli/options" db "github.com/anchore/grype/grype/db/v6" "github.com/anchore/grype/grype/db/v6/distribution" + "github.com/anchore/grype/grype/grypeerr" "github.com/anchore/grype/internal/log" ) -const ( - exitCodeOnDBUpgradeAvailable = 100 -) - type dbCheckOptions struct { Output string `yaml:"output" json:"output" mapstructure:"output"` options.DatabaseCommand `yaml:",inline" mapstructure:",squash"` @@ -85,7 +82,7 @@ func runDBCheck(opts dbCheckOptions) error { } if updateAvailable { - os.Exit(exitCodeOnDBUpgradeAvailable) //nolint:gocritic + return grypeerr.ErrDBUpgradeAvailable } return nil } diff --git a/go.mod b/go.mod index 1c61697cea0..485063d9955 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/adrg/xdg v0.5.3 github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 - github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716 + github.com/anchore/clio v0.0.0-20250401141128-4c1d6bd1e872 github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 @@ -97,7 +97,7 @@ require ( github.com/STARRY-S/zip v0.2.1 // indirect github.com/acobaugh/osrelease v0.1.0 // indirect github.com/agext/levenshtein v1.2.1 // indirect - github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2 // indirect + github.com/anchore/fangs v0.0.0-20250326231402-da263204d38e // indirect github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6 // indirect @@ -197,7 +197,7 @@ require ( github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/maruel/natural v1.1.1 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect @@ -239,7 +239,7 @@ require ( github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c // indirect - github.com/sagikazarmark/locafero v0.7.0 // indirect + github.com/sagikazarmark/locafero v0.9.0 // indirect github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect github.com/sassoftware/go-rpmutils v0.4.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect @@ -251,7 +251,7 @@ require ( github.com/spdx/tools-golang v0.5.5 // indirect github.com/spf13/cast v1.7.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.20.0 // indirect + github.com/spf13/viper v1.20.1 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/sylabs/sif/v2 v2.21.1 // indirect @@ -279,8 +279,7 @@ require ( go.opentelemetry.io/otel/sdk v1.33.0 // indirect go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect go.opentelemetry.io/otel/trace v1.33.0 // indirect - go.uber.org/atomic v1.9.0 // indirect - go.uber.org/multierr v1.9.0 // indirect + go.uber.org/multierr v1.11.0 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/crypto v0.36.0 // indirect golang.org/x/mod v0.24.0 // indirect diff --git a/go.sum b/go.sum index d16e05ae3ee..800aabee5fc 100644 --- a/go.sum +++ b/go.sum @@ -686,10 +686,10 @@ github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 h1:yhk+P8lF3 github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51/go.mod h1:nwuGSd7aZp0rtYt79YggCGafz1RYsclE7pi3fhLwvuw= github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 h1:p0ZIe0htYOX284Y4axJaGBvXHU0VCCzLN5Wf5XbKStU= github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9/go.mod h1:3ZsFB9tzW3vl4gEiUeuSOMDnwroWxIxJelOOHUp8dSw= -github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716 h1:2sIdYJlQESEnyk3Y0WD2vXWW5eD2iMz9Ev8fj1Z8LNA= -github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716/go.mod h1:Utb9i4kwiCWvqAIxZaJeMIXFO9uOgQXlvH2BfbfO/zI= -github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2 h1:GC2QaO0YsmjpsZ4rtVKv9DnproIxqqn+qkskpc+i8MA= -github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2/go.mod h1:XUbUECwVKuD3qYRUj+QZIOHjyyXua2gFmVjKA40iHXA= +github.com/anchore/clio v0.0.0-20250401141128-4c1d6bd1e872 h1:iEF0xhHUuh3J8FrlPsZAQVaMpTa2j4lvLRI5XrXzge4= +github.com/anchore/clio v0.0.0-20250401141128-4c1d6bd1e872/go.mod h1:Utb9i4kwiCWvqAIxZaJeMIXFO9uOgQXlvH2BfbfO/zI= +github.com/anchore/fangs v0.0.0-20250326231402-da263204d38e h1:9hXsNmfBqo2exA4a90Qw33Edb+OROVmeibe9RzgS1wA= +github.com/anchore/fangs v0.0.0-20250326231402-da263204d38e/go.mod h1:vrcYMDps9YXwwx2a9AsvipM6Fi5H9//9bymGb8G8BIQ= github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 h1:GjNGuwK5jWjJMyVppBjYS54eOiiSNv4Ba869k4wh72Q= github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8= github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d h1:gT69osH9AsdpOfqxbRwtxcNnSZ1zg4aKy2BevO3ZBdc= @@ -1284,8 +1284,8 @@ github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVc github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= @@ -1454,8 +1454,8 @@ github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZ github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= -github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= -github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= +github.com/sagikazarmark/locafero v0.9.0 h1:GbgQGNtTrEmddYDSAH9QLRyfAHY12md+8YFTqyMTC9k= +github.com/sagikazarmark/locafero v0.9.0/go.mod h1:UBUyz37V+EdMS3hDF3QWIiVr/2dPrx49OMO0Bn0hJqk= github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA= @@ -1508,8 +1508,8 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY= -github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= +github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= +github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= 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/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -1643,13 +1643,11 @@ go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc= go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU= diff --git a/grype/grypeerr/errors.go b/grype/grypeerr/errors.go index a7a8a246366..f1baf28059a 100644 --- a/grype/grypeerr/errors.go +++ b/grype/grypeerr/errors.go @@ -1,6 +1,10 @@ package grypeerr var ( - // ErrAboveSeverityThreshold indicates when a vulnerability severity is discovered that is above the given --fail-on severity value + // ErrAboveSeverityThreshold indicates when a vulnerability severity is discovered that is equal + // or above the given --fail-on severity value. ErrAboveSeverityThreshold = NewExpectedErr("discovered vulnerabilities at or above the severity threshold") + + // ErrDBUpgradeAvailable indicates that a DB upgrade is available. + ErrDBUpgradeAvailable = NewExpectedErr("db upgrade available") ) From 7257c7184350ddd1f3d975a327adb6fc8749fbaa 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, 3 Apr 2025 15:15:12 +0000 Subject: [PATCH 042/109] chore(deps): update tools to latest versions (#2561) --------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- .binny.yaml | 4 +- .golangci.yaml | 69 +++++++++++-------- grype/db/v6/distribution/client.go | 4 +- grype/db/v6/distribution/latest.go | 8 +-- grype/db/v6/testutil/server.go | 8 +-- grype/db/v6/vulnerability_provider.go | 4 +- grype/distro/distro.go | 4 +- grype/presenter/sarif/presenter.go | 2 +- grype/search/distro.go | 2 +- grype/search/ecosystem.go | 2 +- grype/version/fuzzy_constraint.go | 2 + .../mock/vulnerability_provider.go | 4 +- grype/vulnerability_matcher.go | 4 +- 13 files changed, 64 insertions(+), 53 deletions(-) diff --git a/.binny.yaml b/.binny.yaml index 05aaddf030b..9e8aaadcabf 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -26,7 +26,7 @@ tools: # used for linting - name: golangci-lint version: - want: v1.64.8 + want: v2.0.2 method: github-release with: repo: golangci/golangci-lint @@ -58,7 +58,7 @@ tools: # used to release all artifacts - name: goreleaser version: - want: v2.8.1 + want: v2.8.2 method: github-release with: repo: goreleaser/goreleaser diff --git a/.golangci.yaml b/.golangci.yaml index 6521a59d3ac..68be2075aa3 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,57 +1,46 @@ -issues: - max-same-issues: 25 - uniq-by-line: false - - # TODO: enable this when we have coverage on docstring comments -# # The list of ids of default excludes to include or disable. -# include: -# - EXC0002 # disable excluding of issues about comments from golint - +version: "2" linters: - # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint - disable-all: true + # inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint + default: none enable: - asciicheck - bodyclose + - copyloopvar - dogsled - dupl - errcheck - - copyloopvar - funlen - gocognit - goconst - gocritic - gocyclo - - gofmt - - goimports - goprintffuncname - gosec - - gosimple - govet - ineffassign - misspell - nakedret - revive - staticcheck - - stylecheck - - typecheck - unconvert - unparam - unused - whitespace - -linters-settings: - funlen: - # Checks the number of lines in a function. - # If lower than 0, disable the check. - # Default: 60 - lines: 70 - # Checks the number of statements in a function. - # If lower than 0, disable the check. - # Default: 40 - statements: 50 -run: - timeout: 10m + settings: + funlen: + lines: 70 + statements: 50 + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + paths: + - third_party$ + - builtin$ + - examples$ # do not enable... # - deadcode # The owner seems to have abandoned the linter. Replaced by "unused". @@ -79,3 +68,23 @@ run: # - testpackage # - varcheck # The owner seems to have abandoned the linter. Replaced by "unused". # - wsl # this doens't have an auto-fixer yet and is pretty noisy (https://github.com/bombsimon/wsl/issues/90) + +issues: + max-same-issues: 25 + uniq-by-line: false + +# TODO: enable this when we have coverage on docstring comments +# # The list of ids of default excludes to include or disable. +# include: +# - EXC0002 # disable excluding of issues about comments from golint + +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/grype/db/v6/distribution/client.go b/grype/db/v6/distribution/client.go index 1f15435e4ac..15ef6fc027f 100644 --- a/grype/db/v6/distribution/client.go +++ b/grype/db/v6/distribution/client.go @@ -118,8 +118,8 @@ func (c client) isUpdateAvailable(current *v6.Description, candidate *LatestDocu } // compare created data to current db date - if isSupersededBy(current, candidate.Archive.Description) { - log.Debugf("database update available: %s", candidate.Archive.Description) + if isSupersededBy(current, candidate.Description) { + log.Debugf("database update available: %s", candidate.Description) return &candidate.Archive, message } diff --git a/grype/db/v6/distribution/latest.go b/grype/db/v6/distribution/latest.go index 176ad3e40bd..fb9e5a1a646 100644 --- a/grype/db/v6/distribution/latest.go +++ b/grype/db/v6/distribution/latest.go @@ -52,7 +52,7 @@ func NewLatestDocument(entries ...Archive) *LatestDocument { // sort from most recent to the least recent sort.SliceStable(validEntries, func(i, j int) bool { - return validEntries[i].Description.Built.After(entries[j].Description.Built.Time) + return validEntries[i].Built.After(entries[j].Built.Time) }) return &LatestDocument{ @@ -109,15 +109,15 @@ func (l LatestDocument) Write(writer io.Writer) error { l.Status = LifecycleStatus } - if l.Archive.Path == "" { + if l.Path == "" { return fmt.Errorf("missing archive path") } - if l.Archive.Checksum == "" { + if l.Checksum == "" { return fmt.Errorf("missing archive checksum") } - if l.Archive.Description.Built.Time.IsZero() { + if l.Built.IsZero() { return fmt.Errorf("missing built time") } diff --git a/grype/db/v6/testutil/server.go b/grype/db/v6/testutil/server.go index 1188e4c2043..69626857db8 100644 --- a/grype/db/v6/testutil/server.go +++ b/grype/db/v6/testutil/server.go @@ -109,10 +109,10 @@ func (s *ServerBuilder) Start() (url string) { case serverSubdir + s.LatestDocFile: latestDoc := *s.LatestDoc latestDoc.Built.Time = s.DBBuildTime - latestDoc.Archive.SchemaVersion = s.DBVersion - latestDoc.Archive.Built.Time = s.DBBuildTime - latestDoc.Archive.Path = archivePath - latestDoc.Archive.Checksum = sha(s.dbContents) + latestDoc.SchemaVersion = s.DBVersion + latestDoc.Built.Time = s.DBBuildTime + latestDoc.Path = archivePath + latestDoc.Checksum = sha(s.dbContents) w.WriteHeader(http.StatusOK) _ = json.NewEncoder(w).Encode(latestDoc) case serverSubdir + archivePath: diff --git a/grype/db/v6/vulnerability_provider.go b/grype/db/v6/vulnerability_provider.go index 5b6c1c9ad8b..2cf36b86223 100644 --- a/grype/db/v6/vulnerability_provider.go +++ b/grype/db/v6/vulnerability_provider.go @@ -412,7 +412,7 @@ func (vp vulnerabilityProvider) toVulnerabilities(packageHandles []AffectedPacka continue } - meta, err := getMetadata(packageHandle.Vulnerability, v.Reference.Namespace) + meta, err := getMetadata(packageHandle.Vulnerability, v.Namespace) if err != nil { log.WithFields("error", err, "vulnerability", v.String()).Debug("unable to fetch metadata for vulnerability") } else { @@ -435,7 +435,7 @@ func (vp vulnerabilityProvider) toVulnerabilities(packageHandles []AffectedPacka continue } - meta, err := getMetadata(c.Vulnerability, v.Reference.Namespace) + meta, err := getMetadata(c.Vulnerability, v.Namespace) if err != nil { log.WithFields("error", err, "vulnerability", v.String()).Debug("unable to fetch metadata for vulnerability") } else { diff --git a/grype/distro/distro.go b/grype/distro/distro.go index b7234442e10..13f95224366 100644 --- a/grype/distro/distro.go +++ b/grype/distro/distro.go @@ -111,8 +111,8 @@ func (d Distro) String() string { // Unsupported Linux distributions func (d Distro) Disabled() bool { - switch { - case d.Type == ArchLinux: + switch d.Type { + case ArchLinux: return true default: return false diff --git a/grype/presenter/sarif/presenter.go b/grype/presenter/sarif/presenter.go index ac7cdfc2190..57cbbbe7659 100644 --- a/grype/presenter/sarif/presenter.go +++ b/grype/presenter/sarif/presenter.go @@ -331,7 +331,7 @@ func securitySeverityValue(m models.Match) string { // subtitle generates a subtitle for the given match func subtitle(m models.Match) string { - subtitle := m.Vulnerability.VulnerabilityMetadata.Description + subtitle := m.Vulnerability.Description if subtitle != "" { return subtitle } diff --git a/grype/search/distro.go b/grype/search/distro.go index e371b1af0b7..65f526bf20e 100644 --- a/grype/search/distro.go +++ b/grype/search/distro.go @@ -24,7 +24,7 @@ type DistroCriteria struct { func (c *DistroCriteria) MatchesVulnerability(value vulnerability.Vulnerability) (bool, string, error) { ns, err := namespace.FromString(value.Namespace) if err != nil { - return false, fmt.Sprintf("unable to determine namespace for vulnerability %v: %v", value.Reference.ID, err), nil + return false, fmt.Sprintf("unable to determine namespace for vulnerability %v: %v", value.ID, err), nil } dns, ok := ns.(*distroNs.Namespace) if !ok || dns == nil { diff --git a/grype/search/ecosystem.go b/grype/search/ecosystem.go index 6ce6954166d..17aa8e8df91 100644 --- a/grype/search/ecosystem.go +++ b/grype/search/ecosystem.go @@ -25,7 +25,7 @@ type EcosystemCriteria struct { func (c *EcosystemCriteria) MatchesVulnerability(value vulnerability.Vulnerability) (bool, string, error) { ns, err := namespace.FromString(value.Namespace) if err != nil { - return false, fmt.Sprintf("unable to determine namespace for vulnerability %v: %v", value.Reference.ID, err), nil + return false, fmt.Sprintf("unable to determine namespace for vulnerability %v: %v", value.ID, err), nil } lang, ok := ns.(*language.Namespace) if !ok || lang == nil { diff --git a/grype/version/fuzzy_constraint.go b/grype/version/fuzzy_constraint.go index fd68b88fa5d..8cf0ac32b77 100644 --- a/grype/version/fuzzy_constraint.go +++ b/grype/version/fuzzy_constraint.go @@ -176,6 +176,8 @@ func parseVersionParts(v string) (int, int, int) { // !"#$%&'()*+,-./ are dec 33 to 47, :;<=>?@ are dec 58 to 64, [\]^_` are dec 91 to 96 and {|}~ are dec 123 to 126. // So, punctuation is in dec 33-126 range except 48-57, 65-90 and 97-122 gaps. // This inverse logic allows for early short-circuiting for most of the chars and shaves ~20ns in benchmarks. + // linters might yell about De Morgan's law here - we ignore them in this case + //nolint:staticcheck return b >= '!' && b <= '~' && !(b > '/' && b < ':' || b > '@' && b < '[' || diff --git a/grype/vulnerability/mock/vulnerability_provider.go b/grype/vulnerability/mock/vulnerability_provider.go index 346300db910..276daa95798 100644 --- a/grype/vulnerability/mock/vulnerability_provider.go +++ b/grype/vulnerability/mock/vulnerability_provider.go @@ -31,10 +31,10 @@ func (s *mockProvider) VulnerabilityMetadata(ref vulnerability.Reference) (*vuln for _, vuln := range s.Vulnerabilities { if vuln.ID == ref.ID && vuln.Namespace == ref.Namespace { var meta *vulnerability.Metadata - if m, ok := vuln.Reference.Internal.(vulnerability.Metadata); ok { + if m, ok := vuln.Internal.(vulnerability.Metadata); ok { meta = &m } - if m, ok := vuln.Reference.Internal.(*vulnerability.Metadata); ok { + if m, ok := vuln.Internal.(*vulnerability.Metadata); ok { meta = m } if meta != nil { diff --git a/grype/vulnerability_matcher.go b/grype/vulnerability_matcher.go index 307d88154c5..6c5266930fa 100644 --- a/grype/vulnerability_matcher.go +++ b/grype/vulnerability_matcher.go @@ -369,12 +369,12 @@ func ignoredMatchesDiff(subject []match.IgnoredMatch, other []match.IgnoredMatch otherMap := make(map[match.Fingerprint]struct{}) for _, a := range other { - otherMap[a.Match.Fingerprint()] = struct{}{} + otherMap[a.Fingerprint()] = struct{}{} } var diff []match.IgnoredMatch for _, b := range subject { - if _, ok := otherMap[b.Match.Fingerprint()]; !ok { + if _, ok := otherMap[b.Fingerprint()]; !ok { diff = append(diff, b) } } From a281590490329e577a1bb40da621d0971263ad5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:02:14 -0400 Subject: [PATCH 043/109] chore(deps): bump golang.org/x/tools from 0.31.0 to 0.32.0 (#2585) Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.31.0 to 0.32.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.31.0...v0.32.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-version: 0.32.0 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 | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 485063d9955..c754b8f2efc 100644 --- a/go.mod +++ b/go.mod @@ -66,7 +66,7 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 golang.org/x/time v0.11.0 - golang.org/x/tools v0.31.0 + golang.org/x/tools v0.32.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/gorm v1.25.12 ) @@ -281,14 +281,14 @@ require ( go.opentelemetry.io/otel/trace v1.33.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/crypto v0.36.0 // indirect + golang.org/x/crypto v0.37.0 // indirect golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.38.0 // indirect + golang.org/x/net v0.39.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect + golang.org/x/sync v0.13.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/term v0.31.0 // indirect + golang.org/x/text v0.24.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/api v0.215.0 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect diff --git a/go.sum b/go.sum index 800aabee5fc..43b5b176242 100644 --- a/go.sum +++ b/go.sum @@ -1669,8 +1669,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1804,8 +1804,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= 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= @@ -1858,8 +1858,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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= @@ -1963,8 +1963,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 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= @@ -1980,8 +1980,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= +golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= 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= @@ -2003,8 +2003,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= 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= @@ -2079,8 +2079,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= 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 031e5d906a46cd0ca27d52776ff97eb8ff24427e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Apr 2025 14:02:32 -0400 Subject: [PATCH 044/109] chore(deps): bump github/codeql-action from 3.28.13 to 3.28.15 (#2584) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.13 to 3.28.15. - [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/1b549b9259bda1cb5ddde3b41741a82a2d15a841...45775bd8235c68ba998cffa5171334d58593da47) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.28.15 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 | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 3a630c82881..e18939027a1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -63,7 +63,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13 + uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -85,4 +85,4 @@ jobs: run: make grype - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v3.28.13 + uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index e9b33c32b5d..3eb1c9e8972 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -38,6 +38,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@1b549b9259bda1cb5ddde3b41741a82a2d15a841 # v1.0.26 + uses: github/codeql-action/upload-sarif@45775bd8235c68ba998cffa5171334d58593da47 # v1.0.26 with: sarif_file: results.sarif From f100d5791958c11bf37b7f9ade711e229a2f7311 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, 8 Apr 2025 14:03:48 -0400 Subject: [PATCH 045/109] chore(deps): update tools to latest versions (#2583) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.binny.yaml b/.binny.yaml index 9e8aaadcabf..a16d66901f6 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -42,7 +42,7 @@ tools: # used for signing the checksums file at release - name: cosign version: - want: v2.4.3 + want: v2.5.0 method: github-release with: repo: sigstore/cosign From 52ce2f4d033fe2afd38f0ac13342cc18f0243962 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 10 Apr 2025 08:28:01 -0400 Subject: [PATCH 046/109] recover from panic within a matcher (#2590) Signed-off-by: Alex Goodman --- grype/vulnerability_matcher.go | 13 ++++++++++++- grype/vulnerability_matcher_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/grype/vulnerability_matcher.go b/grype/vulnerability_matcher.go index 6c5266930fa..e0889ae50e7 100644 --- a/grype/vulnerability_matcher.go +++ b/grype/vulnerability_matcher.go @@ -3,6 +3,7 @@ package grype import ( "errors" "fmt" + "runtime/debug" "strings" "github.com/wagoodman/go-partybus" @@ -179,7 +180,7 @@ func (m *VulnerabilityMatcher) searchDBForMatches( matchAgainst = []match.Matcher{defaultMatcher} } for _, theMatcher := range matchAgainst { - matches, ignoredMatches, err := theMatcher.Match(m.VulnerabilityProvider, p) + matches, ignoredMatches, err := callMatcherSafely(theMatcher, m.VulnerabilityProvider, p) if err != nil { if match.IsFatalError(err) { return match.Matches{}, err @@ -223,6 +224,16 @@ func (m *VulnerabilityMatcher) searchDBForMatches( return res, errors.Join(matcherErrs...) } +func callMatcherSafely(m match.Matcher, vp vulnerability.Provider, p pkg.Package) (matches []match.Match, ignoredMatches []match.IgnoredMatch, err error) { + // handle individual matcher panics + defer func() { + if e := recover(); e != nil { + err = match.NewFatalError(m.Type(), fmt.Errorf("%v at:\n%s", e, string(debug.Stack()))) + } + }() + return m.Match(vp, p) +} + func (m *VulnerabilityMatcher) findVEXMatches(context pkg.Context, remainingMatches *match.Matches, ignoredMatches []match.IgnoredMatch, progressMonitor *monitorWriter) (*match.Matches, []match.IgnoredMatch, error) { if m.VexProcessor == nil { log.Trace("no VEX documents provided, skipping VEX matching") diff --git a/grype/vulnerability_matcher_test.go b/grype/vulnerability_matcher_test.go index 9b3f66ad89b..baf5658b849 100644 --- a/grype/vulnerability_matcher_test.go +++ b/grype/vulnerability_matcher_test.go @@ -1385,6 +1385,34 @@ func Test_filterMatchesUsingDistroFalsePositives(t *testing.T) { } } +type panicyMatcher struct { + matcherType match.MatcherType +} + +func (m *panicyMatcher) PackageTypes() []syftPkg.Type { + return nil +} + +func (m *panicyMatcher) Type() match.MatcherType { + return m.matcherType +} + +func (m *panicyMatcher) Match(_ vulnerability.Provider, _ pkg.Package) ([]match.Match, []match.IgnoredMatch, error) { + panic("test panic message") +} + +func TestCallMatcherSafely_RecoverFromPanic(t *testing.T) { + matcher := &panicyMatcher{ + matcherType: "test-matcher", + } + _, _, err := callMatcherSafely(matcher, nil, pkg.Package{}) + + require.Error(t, err) + assert.True(t, match.IsFatalError(err)) + require.Contains(t, err.Error(), "test panic message", "missing message") + require.Contains(t, err.Error(), "test-matcher", "missing matcher name") +} + type busListener struct { matching monitor.Matching } From d4daf591a12050271cd806d5e628d8999897093d Mon Sep 17 00:00:00 2001 From: Joe Ton Date: Thu, 10 Apr 2025 06:13:27 -0700 Subject: [PATCH 047/109] Replace os.ReadDir with afero.ReadDir for consistency (#2579) Signed-off-by: Joe Ton --- internal/file/copy.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/file/copy.go b/internal/file/copy.go index 4508044420d..53fcb6547c8 100644 --- a/internal/file/copy.go +++ b/internal/file/copy.go @@ -11,7 +11,7 @@ import ( func CopyDir(fs afero.Fs, src string, dst string) error { var err error - var fds []os.DirEntry + var fds []os.FileInfo // <-- afero.ReadDir returns []os.FileInfo var srcinfo os.FileInfo if srcinfo, err = fs.Stat(src); err != nil { @@ -22,7 +22,7 @@ func CopyDir(fs afero.Fs, src string, dst string) error { return err } - if fds, err = os.ReadDir(src); err != nil { + if fds, err = afero.ReadDir(fs, src); err != nil { return err } for _, fd := range fds { From 88dd8d3c05351921dbd5c50cb5b94d578ffe2b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Borja=20Dom=C3=ADnguez?= Date: Thu, 10 Apr 2025 15:18:28 +0200 Subject: [PATCH 048/109] feat: add sarif result level (#2571) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Borja Domínguez Vázquez --- grype/presenter/sarif/presenter.go | 15 +++++++++++++++ grype/presenter/sarif/presenter_test.go | 2 ++ .../snapshot/TestSarifPresenter_directory.golden | 2 ++ .../snapshot/TestSarifPresenter_image.golden | 2 ++ 4 files changed, 21 insertions(+) diff --git a/grype/presenter/sarif/presenter.go b/grype/presenter/sarif/presenter.go index 57cbbbe7659..ac8d0b2843c 100644 --- a/grype/presenter/sarif/presenter.go +++ b/grype/presenter/sarif/presenter.go @@ -329,6 +329,20 @@ func securitySeverityValue(m models.Match) string { return "0.0" } +func levelValue(m models.Match) string { + severity := vulnerability.ParseSeverity(m.Vulnerability.Severity) + switch severity { + case vulnerability.CriticalSeverity: + return "error" + case vulnerability.HighSeverity: + return "error" + case vulnerability.MediumSeverity: + return "warning" + } + + return "note" +} + // subtitle generates a subtitle for the given match func subtitle(m models.Match) string { subtitle := m.Vulnerability.Description @@ -360,6 +374,7 @@ func (p Presenter) sarifResults() []*sarif.Result { for _, m := range p.document.Matches { out = append(out, &sarif.Result{ RuleID: sp(p.ruleID(m)), + Level: sp(levelValue(m)), Message: p.resultMessage(m), // According to the SARIF spec, it may be correct to use AnalysisTarget.URI to indicate a logical // file such as a "Dockerfile" but GitHub does not work well with this diff --git a/grype/presenter/sarif/presenter_test.go b/grype/presenter/sarif/presenter_test.go index 50693d09da0..cec3081863f 100644 --- a/grype/presenter/sarif/presenter_test.go +++ b/grype/presenter/sarif/presenter_test.go @@ -286,6 +286,7 @@ func TestToSarifReport(t *testing.T) { assert.Len(t, run.Results, 2) result := run.Results[0] assert.Equal(t, "CVE-1999-0001-package-1", *result.RuleID) + assert.Equal(t, "note", *result.Level) assert.Len(t, result.Locations, 1) location := result.Locations[0] expectedLocation, ok := tc.locations[*result.RuleID] @@ -296,6 +297,7 @@ func TestToSarifReport(t *testing.T) { result = run.Results[1] assert.Equal(t, "CVE-1999-0002-package-2", *result.RuleID) + assert.Equal(t, "error", *result.Level) assert.Len(t, result.Locations, 1) location = result.Locations[0] expectedLocation, ok = tc.locations[*result.RuleID] diff --git a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden index 9edb968a3a0..fa07c7fd78a 100644 --- a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden +++ b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden @@ -54,6 +54,7 @@ "results": [ { "ruleId": "CVE-1999-0001-package-1", + "level": "note", "message": { "text": "A low vulnerability in rpm package: package-1, version 1.1.1 was found at: /some/path/somefile-1.txt" }, @@ -78,6 +79,7 @@ }, { "ruleId": "CVE-1999-0002-package-2", + "level": "error", "message": { "text": "A critical vulnerability in deb package: package-2, version 2.2.2 was found at: /some/path/somefile-2.txt" }, diff --git a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden index 027cd809648..2de58e3ef4d 100644 --- a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden +++ b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden @@ -54,6 +54,7 @@ "results": [ { "ruleId": "CVE-1999-0001-package-1", + "level": "note", "message": { "text": "A low vulnerability in rpm package: package-1, version 1.1.1 was found in image user-input at: somefile-1.txt" }, @@ -84,6 +85,7 @@ }, { "ruleId": "CVE-1999-0002-package-2", + "level": "error", "message": { "text": "A critical vulnerability in deb package: package-2, version 2.2.2 was found in image user-input at: somefile-2.txt" }, From a9dee660e113625f1f12b619295b9fb2c86790ea Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 10 Apr 2025 10:08:53 -0400 Subject: [PATCH 049/109] fix cvss view in search (#2589) Signed-off-by: Alex Goodman --- cmd/grype/cli/commands/db_search_vuln.go | 73 +++--- cmd/grype/cli/commands/db_search_vuln_test.go | 228 ++++++++++++++++++ internal/cvss/metrics.go | 18 ++ internal/cvss/metrics_test.go | 80 ++++++ 4 files changed, 372 insertions(+), 27 deletions(-) create mode 100644 cmd/grype/cli/commands/db_search_vuln_test.go diff --git a/cmd/grype/cli/commands/db_search_vuln.go b/cmd/grype/cli/commands/db_search_vuln.go index f9ea918be39..22847c049fe 100644 --- a/cmd/grype/cli/commands/db_search_vuln.go +++ b/cmd/grype/cli/commands/db_search_vuln.go @@ -6,6 +6,7 @@ import ( "io" "sort" "strings" + "time" "github.com/hashicorp/go-multierror" "github.com/scylladb/go-set/strset" @@ -17,7 +18,9 @@ import ( v6 "github.com/anchore/grype/grype/db/v6" "github.com/anchore/grype/grype/db/v6/distribution" "github.com/anchore/grype/grype/db/v6/installation" + "github.com/anchore/grype/grype/vulnerability" "github.com/anchore/grype/internal/bus" + "github.com/anchore/grype/internal/cvss" ) type dbSearchVulnerabilityOptions struct { @@ -166,36 +169,14 @@ func renderDBSearchVulnerabilitiesTableRows(structuredRows []dbsearch.Vulnerabil versionsByRow := make(map[row][]string) for _, rr := range structuredRows { - // get the first severity value (which is ranked highest) - var sev string - if len(rr.Severities) > 0 { - sev = fmt.Sprintf("%s", rr.Severities[0].Value) - } - - prov := rr.Provider - var versions []string - for _, os := range rr.OperatingSystems { - versions = append(versions, os.Version) - } - - var published string - if rr.PublishedDate != nil && !rr.PublishedDate.IsZero() { - published = rr.PublishedDate.Format("2006-01-02") - } - - var ref string - if len(rr.References) > 0 { - ref = rr.References[0].URL - } - r := row{ Vuln: rr.ID, - ProviderWithoutVersions: prov, - PublishedDate: published, - Severity: sev, - Reference: ref, + ProviderWithoutVersions: rr.Provider, + PublishedDate: getDate(rr.PublishedDate), + Severity: getSeverity(rr.Severities), + Reference: getPrimaryReference(rr.References), } - versionsByRow[r] = append(versionsByRow[r], versions...) + versionsByRow[r] = append(versionsByRow[r], getOSVersions(rr.OperatingSystems)...) } var rows [][]string @@ -220,3 +201,41 @@ func renderDBSearchVulnerabilitiesTableRows(structuredRows []dbsearch.Vulnerabil return rows } + +func getOSVersions(oss []dbsearch.OperatingSystem) []string { + var versions []string + for _, os := range oss { + versions = append(versions, os.Version) + } + return versions +} + +func getPrimaryReference(refs []v6.Reference) string { + if len(refs) > 0 { + return refs[0].URL + } + + return "" +} + +func getDate(t *time.Time) string { + if t != nil && !t.IsZero() { + return t.Format("2006-01-02") + } + return "" +} + +func getSeverity(sevs []v6.Severity) string { + if len(sevs) == 0 { + return vulnerability.UnknownSeverity.String() + } + // get the first severity value (which is ranked highest) + switch v := sevs[0].Value.(type) { + case string: + return v + case dbsearch.CVSSSeverity: + return cvss.SeverityFromBaseScore(v.Metrics.BaseScore).String() + } + + return fmt.Sprintf("%v", sevs[0].Value) +} diff --git a/cmd/grype/cli/commands/db_search_vuln_test.go b/cmd/grype/cli/commands/db_search_vuln_test.go new file mode 100644 index 00000000000..742024438e7 --- /dev/null +++ b/cmd/grype/cli/commands/db_search_vuln_test.go @@ -0,0 +1,228 @@ +package commands + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/anchore/grype/cmd/grype/cli/commands/internal/dbsearch" + v6 "github.com/anchore/grype/grype/db/v6" + "github.com/anchore/grype/grype/vulnerability" +) + +func TestGetOSVersions(t *testing.T) { + tests := []struct { + name string + input []dbsearch.OperatingSystem + expected []string + }{ + { + name: "empty list", + input: []dbsearch.OperatingSystem{}, + expected: nil, + }, + { + name: "single os", + input: []dbsearch.OperatingSystem{ + { + Name: "debian", + Version: "11", + }, + }, + expected: []string{"11"}, + }, + { + name: "multiple os", + input: []dbsearch.OperatingSystem{ + { + Name: "ubuntu", + Version: "16.04", + }, + { + Name: "ubuntu", + Version: "22.04", + }, + { + Name: "ubuntu", + Version: "24.04", + }, + }, + expected: []string{"16.04", "22.04", "24.04"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := getOSVersions(tt.input) + require.Equal(t, tt.expected, actual) + }) + } +} + +func TestGetPrimaryReference(t *testing.T) { + tests := []struct { + name string + input []v6.Reference + expected string + }{ + { + name: "empty list", + input: []v6.Reference{}, + expected: "", + }, + { + name: "single reference", + input: []v6.Reference{ + { + URL: "https://example.com/vuln/123", + Tags: []string{"primary"}, + }, + }, + expected: "https://example.com/vuln/123", + }, + { + name: "multiple references", + input: []v6.Reference{ + { + URL: "https://example.com/vuln/123", + Tags: []string{"primary"}, + }, + { + URL: "https://example.com/advisory/123", + Tags: []string{"secondary"}, + }, + }, + expected: "https://example.com/vuln/123", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := getPrimaryReference(tt.input) + require.Equal(t, tt.expected, actual) + }) + } +} + +func TestGetDate(t *testing.T) { + tests := []struct { + name string + input *time.Time + expected string + }{ + { + name: "nil time", + input: nil, + expected: "", + }, + { + name: "zero time", + input: &time.Time{}, + expected: "", + }, + { + name: "valid time", + input: timePtr(time.Date(2023, 5, 15, 0, 0, 0, 0, time.UTC)), + expected: "2023-05-15", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := getDate(tt.input) + require.Equal(t, tt.expected, actual) + }) + } +} + +func TestGetSeverity(t *testing.T) { + tests := []struct { + name string + input []v6.Severity + expected string + }{ + { + name: "empty list", + input: []v6.Severity{}, + expected: vulnerability.UnknownSeverity.String(), + }, + { + name: "string severity", + input: []v6.Severity{ + { + Scheme: "HML", + Value: "high", + Source: "nvd@nist.gov", + Rank: 1, + }, + }, + expected: "high", + }, + { + name: "CVSS severity", + input: []v6.Severity{ + { + Scheme: "CVSS_V3", + Value: dbsearch.CVSSSeverity{ + Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + Version: "3.1", + Metrics: dbsearch.CvssMetrics{ + BaseScore: 9.8, + }, + }, + Source: "nvd@nist.gov", + Rank: 1, + }, + }, + expected: "critical", + }, + { + name: "other value type", + input: []v6.Severity{ + { + Scheme: "OTHER", + Value: 42.0, + Source: "custom", + Rank: 1, + }, + }, + expected: "42", + }, + { + name: "multiple severities", + input: []v6.Severity{ + { + Scheme: "HML", + Value: "high", + Source: "nvd@nist.gov", + Rank: 1, + }, + { + Scheme: "CVSS_V3", + Value: dbsearch.CVSSSeverity{ + Vector: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + Version: "3.1", + Metrics: dbsearch.CvssMetrics{ + BaseScore: 9.8, + }, + }, + Source: "nvd@nist.gov", + Rank: 2, + }, + }, + expected: "high", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + actual := getSeverity(tt.input) + require.Equal(t, tt.expected, actual) + }) + } +} + +func timePtr(t time.Time) *time.Time { + return &t +} diff --git a/internal/cvss/metrics.go b/internal/cvss/metrics.go index 2d1a0fcc925..0a8fe77ba48 100644 --- a/internal/cvss/metrics.go +++ b/internal/cvss/metrics.go @@ -64,6 +64,24 @@ func ParseMetricsFromVector(vector string) (*vulnerability.CvssMetrics, error) { } } +func SeverityFromBaseScore(bs float64) vulnerability.Severity { + switch { + case bs >= 10.0: + return vulnerability.UnknownSeverity + case bs >= 9.0: + return vulnerability.CriticalSeverity + case bs >= 7.0: + return vulnerability.HighSeverity + case bs >= 4.0: + return vulnerability.MediumSeverity + case bs >= 0.1: + return vulnerability.LowSeverity + case bs > 0: + return vulnerability.NegligibleSeverity + } + return vulnerability.UnknownSeverity +} + // roundScore rounds the score to the nearest tenth based on first.org rounding rules // see https://www.first.org/cvss/v3.1/specification-document#Appendix-A---Floating-Point-Rounding func roundScore(score float64) float64 { diff --git a/internal/cvss/metrics_test.go b/internal/cvss/metrics_test.go index dedbc05d5a8..83e4a19c1e1 100644 --- a/internal/cvss/metrics_test.go +++ b/internal/cvss/metrics_test.go @@ -110,6 +110,86 @@ func TestParseMetricsFromVector(t *testing.T) { } } +func TestSeverityFromBaseScore(t *testing.T) { + tests := []struct { + name string + score float64 + expected vulnerability.Severity + }{ + { + name: "unknown severity (exactly 10.0)", + score: 10.0, + expected: vulnerability.UnknownSeverity, + }, + { + name: "unknown severity (greater than 10.0)", + score: 10.1, + expected: vulnerability.UnknownSeverity, + }, + { + name: "critical severity (lower bound)", + score: 9.0, + expected: vulnerability.CriticalSeverity, + }, + { + name: "critical severity (upper bound)", + score: 9.9, + expected: vulnerability.CriticalSeverity, + }, + { + name: "high severity (lower bound)", + score: 7.0, + expected: vulnerability.HighSeverity, + }, + { + name: "high severity (upper bound)", + score: 8.9, + expected: vulnerability.HighSeverity, + }, + { + name: "medium severity (lower bound)", + score: 4.0, + expected: vulnerability.MediumSeverity, + }, + { + name: "medium severity (upper bound)", + score: 6.9, + expected: vulnerability.MediumSeverity, + }, + { + name: "low severity (lower bound)", + score: 0.1, + expected: vulnerability.LowSeverity, + }, + { + name: "low severity (upper bound)", + score: 3.9, + expected: vulnerability.LowSeverity, + }, + { + name: "negligible severity (between 0 and 0.1)", + score: 0.05, + expected: vulnerability.NegligibleSeverity, + }, + { + name: "unknown severity (exactly zero)", + score: 0.0, + expected: vulnerability.UnknownSeverity, + }, + { + name: "unknown severity (negative)", + score: -1.0, + expected: vulnerability.UnknownSeverity, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equal(t, tt.expected, SeverityFromBaseScore(tt.score)) + }) + } +} + func ptr(f float64) *float64 { return &f } From 263311216e5db1b6a736a6c61935cb91a6d7af19 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 10 Apr 2025 13:34:23 -0400 Subject: [PATCH 050/109] allow empty version (#2591) Signed-off-by: Alex Goodman --- grype/match/matches.go | 9 +- grype/matcher/internal/cpe.go | 22 ++- grype/matcher/internal/cpe_test.go | 160 +++++++++++++++++- grype/matcher/internal/distro.go | 19 ++- .../internal/only_vulnerable_versions.go | 2 +- 5 files changed, 188 insertions(+), 24 deletions(-) diff --git a/grype/match/matches.go b/grype/match/matches.go index 264703920e8..7ce27f6b852 100644 --- a/grype/match/matches.go +++ b/grype/match/matches.go @@ -100,7 +100,8 @@ func (r *Matches) addOrMerge(newMatch Match, newFp Fingerprint) { // case A if err := existingMatch.Merge(newMatch); err != nil { log.WithFields("original", existingMatch.String(), "new", newMatch.String(), "error", err).Warn("unable to merge matches") - // TODO: dropped match in this case, we should figure a way to handle this + // at least capture the additional details + existingMatch.Details = append(existingMatch.Details, newMatch.Details...) } r.byFingerprint[newFp] = existingMatch @@ -125,6 +126,8 @@ func (r *Matches) mergeCoreMatches(newMatch Match, newFp Fingerprint, existingFi // case B1 if replaced := r.replace(newMatch, existingFp, newFp, existingMatch.Details...); !replaced { log.WithFields("original", existingMatch.String(), "new", newMatch.String()).Trace("unable to replace match") + // at least capture the new details + existingMatch.Details = append(existingMatch.Details, newMatch.Details...) } else { return true } @@ -132,7 +135,9 @@ func (r *Matches) mergeCoreMatches(newMatch Match, newFp Fingerprint, existingFi // case B2 if err := existingMatch.Merge(newMatch); err != nil { - log.WithFields("original", existingMatch.String(), "new", newMatch.String(), "error", err).Warn("unable to merge matches") + log.WithFields("original", existingMatch.String(), "new", newMatch.String(), "error", err).Trace("unable to merge matches") + // at least capture the new details + existingMatch.Details = append(existingMatch.Details, newMatch.Details...) } else { return true } diff --git a/grype/matcher/internal/cpe.go b/grype/matcher/internal/cpe.go index 00de88f8758..0af04b69561 100644 --- a/grype/matcher/internal/cpe.go +++ b/grype/matcher/internal/cpe.go @@ -74,9 +74,13 @@ func MatchPackageByCPEs(provider vulnerability.Provider, p pkg.Package, upstream searchVersion = transformJvmVersion(searchVersion, c.Attributes.Update) } - verObj, err := version.NewVersion(searchVersion, format) - if err != nil { - return nil, fmt.Errorf("matcher failed to parse version pkg=%q ver=%q: %w", p.Name, p.Version, err) + var verObj *version.Version + var err error + if searchVersion != "" { + verObj, err = version.NewVersion(searchVersion, format) + if err != nil { + return nil, fmt.Errorf("matcher failed to parse version pkg=%q ver=%q: %w", p.Name, p.Version, err) + } } // find all vulnerability records in the DB for the given CPE (not including version comparisons) @@ -95,7 +99,7 @@ func MatchPackageByCPEs(provider vulnerability.Provider, p pkg.Package, upstream // relative to the current version information from the CPE (or the package) then the given package // is vulnerable. for _, vuln := range vulns { - addNewMatch(matchesByFingerprint, vuln, p, *verObj, upstreamMatcher, c) + addNewMatch(matchesByFingerprint, vuln, p, verObj, upstreamMatcher, c) } } @@ -110,7 +114,7 @@ func transformJvmVersion(searchVersion, updateCpeField string) string { return searchVersion } -func addNewMatch(matchesByFingerprint map[match.Fingerprint]match.Match, vuln vulnerability.Vulnerability, p pkg.Package, searchVersion version.Version, upstreamMatcher match.MatcherType, searchedByCPE cpe.CPE) { +func addNewMatch(matchesByFingerprint map[match.Fingerprint]match.Match, vuln vulnerability.Vulnerability, p pkg.Package, searchVersion *version.Version, upstreamMatcher match.MatcherType, searchedByCPE cpe.CPE) { candidateMatch := match.Match{ Vulnerability: vuln, @@ -186,7 +190,11 @@ func addMatchDetails(existingDetails []match.Detail, newDetails match.Detail) [] return existingDetails } -func filterCPEsByVersion(pkgVersion version.Version, allCPEs []cpe.CPE) (matchedCPEs []cpe.CPE) { +func filterCPEsByVersion(pkgVersion *version.Version, allCPEs []cpe.CPE) (matchedCPEs []cpe.CPE) { + if pkgVersion == nil { + // all CPEs are valid in the case when a version is not specified + return allCPEs + } for _, c := range allCPEs { if c.Attributes.Version == wfn.Any || c.Attributes.Version == wfn.NA { matchedCPEs = append(matchedCPEs, c) @@ -208,7 +216,7 @@ func filterCPEsByVersion(pkgVersion version.Version, allCPEs []cpe.CPE) (matched continue } - satisfied, err := constraint.Satisfied(&pkgVersion) + satisfied, err := constraint.Satisfied(pkgVersion) if err != nil || satisfied { // if we can't check for version satisfaction, don't filter out the CPE matchedCPEs = append(matchedCPEs, c) diff --git a/grype/matcher/internal/cpe_test.go b/grype/matcher/internal/cpe_test.go index 0a2a2155c9d..e61d44de7dc 100644 --- a/grype/matcher/internal/cpe_test.go +++ b/grype/matcher/internal/cpe_test.go @@ -216,17 +216,147 @@ func TestFindMatchesByPackageCPE(t *testing.T) { }, }, { - name: "suppress matching when missing version", + name: "return all possible matches when missing version", p: pkg.Package{ CPEs: []cpe.CPE{ - cpe.Must("cpe:2.3:*:activerecord:activerecord:unknown:rando1:*:ra:*:ruby:*:*", ""), - cpe.Must("cpe:2.3:*:activerecord:activerecord:unknown:rando4:*:re:*:rails:*:*", ""), + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando1:*:ra:*:ruby:*:*", ""), + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando4:*:re:*:rails:*:*", ""), }, Name: "activerecord", Version: "", Language: syftPkg.Ruby, Type: syftPkg.GemPkg, }, + expected: []match.Match{ + { + + Vulnerability: vulnerability.Vulnerability{ + Reference: vulnerability.Reference{ID: "CVE-2017-fake-1"}, + }, + Package: pkg.Package{ + CPEs: []cpe.CPE{ + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando1:*:ra:*:ruby:*:*", ""), + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando4:*:re:*:rails:*:*", ""), + }, + Name: "activerecord", + Version: "", // important! + Language: syftPkg.Ruby, + Type: syftPkg.GemPkg, + }, + + Details: []match.Detail{ + { + Type: match.CPEMatch, + Confidence: 0.9, + SearchedBy: match.CPEParameters{ + CPEs: []string{ + "cpe:2.3:*:activerecord:activerecord:*:rando4:*:re:*:rails:*:*", //important! + }, + Namespace: "nvd:cpe", + Package: match.CPEPackageParameter{ + Name: "activerecord", + Version: "", // important! + }, + }, + Found: match.CPEResult{ + CPEs: []string{"cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:rails:*:*"}, + VersionConstraint: "< 3.7.6 (semver)", + VulnerabilityID: "CVE-2017-fake-1", + }, + Matcher: matcher, + }, + }, + }, + { + + Vulnerability: vulnerability.Vulnerability{ + Reference: vulnerability.Reference{ID: "CVE-2017-fake-2"}, + }, + Package: pkg.Package{ + CPEs: []cpe.CPE{ + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando1:*:ra:*:ruby:*:*", ""), + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando4:*:re:*:rails:*:*", ""), + }, + Name: "activerecord", + Version: "", // important! + Language: syftPkg.Ruby, + Type: syftPkg.GemPkg, + }, + + Details: []match.Detail{ + { + Type: match.CPEMatch, + Confidence: 0.9, + SearchedBy: match.CPEParameters{ + CPEs: []string{"cpe:2.3:*:activerecord:activerecord:*:rando1:*:ra:*:ruby:*:*"}, //important! + Namespace: "nvd:cpe", + Package: match.CPEPackageParameter{ + Name: "activerecord", + Version: "", // important! + }, + }, + Found: match.CPEResult{ + CPEs: []string{"cpe:2.3:*:activerecord:activerecord:*:*:*:*:*:ruby:*:*"}, + VersionConstraint: "< 3.7.4 (semver)", + VulnerabilityID: "CVE-2017-fake-2", + }, + Matcher: matcher, + }, + }, + }, + { + + Vulnerability: vulnerability.Vulnerability{ + Reference: vulnerability.Reference{ID: "CVE-2017-fake-3"}, + }, + Package: pkg.Package{ + CPEs: []cpe.CPE{ + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando1:*:ra:*:ruby:*:*", ""), + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando4:*:re:*:rails:*:*", ""), + }, + Name: "activerecord", + Version: "", // important! + Language: syftPkg.Ruby, + Type: syftPkg.GemPkg, + }, + Details: []match.Detail{ + { + Type: match.CPEMatch, + Confidence: 0.9, + SearchedBy: match.CPEParameters{ + CPEs: []string{ + "cpe:2.3:*:activerecord:activerecord:*:rando1:*:ra:*:ruby:*:*", //important! + "cpe:2.3:*:activerecord:activerecord:*:rando4:*:re:*:rails:*:*", //important! + }, + Namespace: "nvd:cpe", + Package: match.CPEPackageParameter{ + Name: "activerecord", + Version: "", // important! + }, + }, + Found: match.CPEResult{ + CPEs: []string{"cpe:2.3:*:activerecord:activerecord:4.0.1:*:*:*:*:*:*:*"}, + VersionConstraint: "= 4.0.1 (semver)", + VulnerabilityID: "CVE-2017-fake-3", + }, + Matcher: matcher, + }, + }, + }, + }, + }, + { + name: "suppress matching when version is unknown", + p: pkg.Package{ + CPEs: []cpe.CPE{ + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando1:*:ra:*:ruby:*:*", ""), + cpe.Must("cpe:2.3:*:activerecord:activerecord:*:rando4:*:re:*:rails:*:*", ""), + }, + Name: "activerecord", + Version: "unknown", + Language: syftPkg.Ruby, + Type: syftPkg.GemPkg, + }, expected: []match.Match{}, }, { @@ -908,6 +1038,20 @@ func TestFilterCPEsByVersion(t *testing.T) { "cpe:2.3:*:multiple:multiple:1.0:*:*:*:*:*:*:*", }, }, + { + name: "do not filter on empty version", + version: "", // important! + vulnerabilityCPEs: []string{ + "cpe:2.3:*:multiple:multiple:*:*:*:*:*:*:*:*", + "cpe:2.3:*:multiple:multiple:1.0:*:*:*:*:*:*:*", + "cpe:2.3:*:multiple:multiple:2.0:*:*:*:*:*:*:*", + }, + expected: []string{ + "cpe:2.3:*:multiple:multiple:*:*:*:*:*:*:*:*", + "cpe:2.3:*:multiple:multiple:1.0:*:*:*:*:*:*:*", + "cpe:2.3:*:multiple:multiple:2.0:*:*:*:*:*:*:*", + }, + }, } for _, test := range tests { @@ -918,13 +1062,15 @@ func TestFilterCPEsByVersion(t *testing.T) { vulnerabilityCPEs[idx] = cpe.Must(c, "") } - versionObj, err := version.NewVersion(test.version, version.UnknownFormat) - if err != nil { - t.Fatalf("unable to get version: %+v", err) + var versionObj *version.Version + var err error + if test.version != "" { + versionObj, err = version.NewVersion(test.version, version.UnknownFormat) + require.NoError(t, err) } // run the test subject... - actual := filterCPEsByVersion(*versionObj, vulnerabilityCPEs) + actual := filterCPEsByVersion(versionObj, vulnerabilityCPEs) // format CPE objects to string... actualStrs := make([]string, len(actual)) diff --git a/grype/matcher/internal/distro.go b/grype/matcher/internal/distro.go index fd2f7fbaa6a..65179bc66ba 100644 --- a/grype/matcher/internal/distro.go +++ b/grype/matcher/internal/distro.go @@ -23,13 +23,18 @@ func MatchPackageByDistro(provider vulnerability.Provider, p pkg.Package, upstre return nil, nil, nil } - verObj, err := version.NewVersionFromPkg(p) - if err != nil { - if errors.Is(err, version.ErrUnsupportedVersion) { - log.WithFields("error", err).Tracef("skipping package '%s@%s'", p.Name, p.Version) - return nil, nil, nil + var verObj *version.Version + var err error + + if p.Version != "" { + verObj, err = version.NewVersionFromPkg(p) + if err != nil { + if errors.Is(err, version.ErrUnsupportedVersion) { + log.WithFields("error", err).Tracef("skipping package '%s@%s'", p.Name, p.Version) + return nil, nil, nil + } + return nil, nil, fmt.Errorf("matcher failed to parse version pkg=%q ver=%q: %w", p.Name, p.Version, err) } - return nil, nil, fmt.Errorf("matcher failed to parse version pkg=%q ver=%q: %w", p.Name, p.Version, err) } var matches []match.Match @@ -78,5 +83,5 @@ func MatchPackageByDistro(provider vulnerability.Provider, p pkg.Package, upstre } func isUnknownVersion(v string) bool { - return v == "" || strings.ToLower(v) == "unknown" + return strings.ToLower(v) == "unknown" } diff --git a/grype/matcher/internal/only_vulnerable_versions.go b/grype/matcher/internal/only_vulnerable_versions.go index 81b2315cdfd..bd480411aaa 100644 --- a/grype/matcher/internal/only_vulnerable_versions.go +++ b/grype/matcher/internal/only_vulnerable_versions.go @@ -8,7 +8,7 @@ import ( // onlyVulnerableVersion returns a criteria object that tests affected vulnerability ranges against the provided version func onlyVulnerableVersions(v *version.Version) vulnerability.Criteria { - if v == nil { + if v == nil || v.Raw == "" { // if no version is provided, match everything return search.ByFunc(func(_ vulnerability.Vulnerability) (bool, string, error) { return true, "", nil From e8ca0f577f30a4a612cfa105208a657e9ad6071c Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Tue, 15 Apr 2025 11:01:42 -0400 Subject: [PATCH 051/109] chore: add some timing info logs (#2597) Signed-off-by: Keith Zantow --- cmd/grype/cli/commands/root.go | 11 +++++++++++ grype/db/v6/installation/curator.go | 8 +++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/cmd/grype/cli/commands/root.go b/cmd/grype/cli/commands/root.go index 750522be5df..67fb099ef7c 100644 --- a/cmd/grype/cli/commands/root.go +++ b/cmd/grype/cli/commands/root.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "strings" + "time" "github.com/spf13/cobra" "github.com/wagoodman/go-partybus" @@ -150,11 +151,15 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs return nil }, func() (err error) { + startTime := time.Now() + defer func() { log.WithFields("time", time.Since(startTime)).Info("loaded DB") }() log.Debug("loading DB") vp, status, err = grype.LoadVulnerabilityDB(opts.ToClientConfig(), opts.ToCuratorConfig(), opts.DB.AutoUpdate) return validateDBLoad(err, status) }, func() (err error) { + startTime := time.Now() + defer func() { log.WithFields("time", time.Since(startTime)).Info("gathered packages") }() log.Debugf("gathering packages") // packages are grype.Package, not syft.Package // the SBOM is returned for downstream formatting concerns @@ -178,6 +183,7 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs return fmt.Errorf("applying vex rules: %w", err) } + startTime := time.Now() applyDistroHint(packages, &pkgContext, opts) vulnMatcher := grype.VulnerabilityMatcher{ @@ -200,6 +206,9 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs errs = appendErrors(errs, err) } + log.WithFields("time", time.Since(startTime)).Info("found vulnerability matches") + startTime = time.Now() + model, err := models.NewDocument(app.ID(), packages, pkgContext, *remainingMatches, ignoredMatches, vp, opts, dbInfo(status, vp), models.SortByPackage) if err != nil { return fmt.Errorf("failed to create document: %w", err) @@ -214,6 +223,8 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs errs = appendErrors(errs, err) } + log.WithFields("time", time.Since(startTime)).Trace("wrote vulnerability report") + return errs } diff --git a/grype/db/v6/installation/curator.go b/grype/db/v6/installation/curator.go index 704cfafc1be..cb408c5fbff 100644 --- a/grype/db/v6/installation/curator.go +++ b/grype/db/v6/installation/curator.go @@ -272,6 +272,7 @@ func (c curator) isUpdateCheckAllowed() bool { func (c curator) update(current *db.Description) (*distribution.Archive, error) { mon := newMonitor() defer mon.SetCompleted() + startTime := time.Now() mon.Set("checking for update") update, checkErr := c.client.IsUpdateAvailable(current) @@ -302,7 +303,7 @@ func (c curator) update(current *db.Description) (*distribution.Archive, error) return nil, fmt.Errorf("unable to update vulnerability database: %w", err) } - log.WithFields("url", url).Debug("obtained vulnerability DB archive") + log.WithFields("url", url, "time", time.Since(startTime)).Info("downloaded vulnerability DB") mon.downloadProgress.SetCompleted() if err = c.activate(dest, url, mon); err != nil { @@ -477,10 +478,15 @@ func isURL(reference string) bool { func (c curator) activate(dbDirPath, url string, mon monitor) error { defer mon.SetCompleted() + startTime := time.Now() if err := c.hydrate(dbDirPath, url, mon); err != nil { return fmt.Errorf("failed to hydrate database: %w", err) } + log.WithFields("time", time.Since(startTime)).Trace("hydrated db") + startTime = time.Now() + defer func() { log.WithFields("time", time.Since(startTime)).Trace("replaced db") }() + mon.Set("activating") return c.replaceDB(dbDirPath) From 86b9742fd36dbb97ee87e1aa68a7332f0cf00f8b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 15 Apr 2025 16:11:33 +0000 Subject: [PATCH 052/109] chore(deps): bump github.com/anchore/stereoscope from 0.1.2 to 0.1.3 (#2598) Bumps [github.com/anchore/stereoscope](https://github.com/anchore/stereoscope) from 0.1.2 to 0.1.3. - [Release notes](https://github.com/anchore/stereoscope/releases) - [Changelog](https://github.com/anchore/stereoscope/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/stereoscope/compare/v0.1.2...v0.1.3) --- updated-dependencies: - dependency-name: github.com/anchore/stereoscope dependency-version: 0.1.3 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 c754b8f2efc..7e405afe9a8 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.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 - github.com/anchore/stereoscope v0.1.2 + github.com/anchore/stereoscope v0.1.3 github.com/anchore/syft v1.22.0 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de diff --git a/go.sum b/go.sum index 43b5b176242..a8f00152571 100644 --- a/go.sum +++ b/go.sum @@ -708,8 +708,8 @@ github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= -github.com/anchore/stereoscope v0.1.2 h1:0+Jcf7hoImYKfrH2XzN6vvbmbpm68A/woabC43gZ4eU= -github.com/anchore/stereoscope v0.1.2/go.mod h1:kU4LkiocOimxuSnlmJgilHCfDMCR5t/UroKIpzI8jNw= +github.com/anchore/stereoscope v0.1.3 h1:qCbu9h7NCTIjWYyuAWwwEOuZia2UP5rvkCSA4reDzSg= +github.com/anchore/stereoscope v0.1.3/go.mod h1:kU4LkiocOimxuSnlmJgilHCfDMCR5t/UroKIpzI8jNw= github.com/anchore/syft v1.22.0 h1:lmNuy1uQ9GZeO6HPms3ik3W0hTE9MxP46vtC3vssBe4= github.com/anchore/syft v1.22.0/go.mod h1:5U0av1cf7i/O2VusM29HTsVYUsARjBvgPCbP4RtSEyk= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= From fed6de330a51f89ed197a05b27f37f348c365418 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Wed, 16 Apr 2025 11:12:09 -0400 Subject: [PATCH 053/109] fix: expand db cache dir homedir expressions (#2600) Signed-off-by: Keith Zantow --- cmd/grype/cli/options/database.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/cmd/grype/cli/options/database.go b/cmd/grype/cli/options/database.go index 73044576866..ccafe815920 100644 --- a/cmd/grype/cli/options/database.go +++ b/cmd/grype/cli/options/database.go @@ -4,6 +4,7 @@ import ( "time" "github.com/anchore/clio" + "github.com/anchore/go-homedir" "github.com/anchore/grype/grype/db/v6/distribution" "github.com/anchore/grype/grype/db/v6/installation" ) @@ -25,6 +26,7 @@ type Database struct { var _ interface { clio.FieldDescriber + clio.PostLoader } = (*Database)(nil) func DefaultDatabase(id clio.Identification) Database { @@ -64,3 +66,9 @@ This file is ~156KiB as of 2024-04-17 so the download should be quick; adjust as The DB is ~156MB as of 2024-04-17 so slower connections may exceed the default timeout; adjust as needed`) descriptions.Add(&cfg.MaxUpdateCheckFrequency, `Maximum frequency to check for vulnerability database updates`) } + +func (cfg *Database) PostLoad() error { + var err error + cfg.Dir, err = homedir.Expand(cfg.Dir) + return err +} From 450788f6e03dadfd5f53a7091d1524098e540ef5 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, 16 Apr 2025 11:12:56 -0400 Subject: [PATCH 054/109] chore(deps): update tools to latest versions (#2595) --- .binny.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.binny.yaml b/.binny.yaml index a16d66901f6..bb0755c7c7d 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -26,7 +26,7 @@ tools: # used for linting - name: golangci-lint version: - want: v2.0.2 + want: v2.1.2 method: github-release with: repo: golangci/golangci-lint @@ -98,7 +98,7 @@ tools: # used for triggering a release - name: gh version: - want: v2.69.0 + want: v2.70.0 method: github-release with: repo: cli/cli From 091828f0b016ae9464c2c0baf6ef4032bf07a595 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:27:00 -0400 Subject: [PATCH 055/109] chore(deps): bump github.com/docker/docker (#2602) Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.0.4+incompatible to 28.1.0+incompatible. - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v28.0.4...v28.1.0) --- updated-dependencies: - dependency-name: github.com/docker/docker dependency-version: 28.1.0+incompatible 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 | 5 +++-- go.sum | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 7e405afe9a8..12893fd09cc 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/charmbracelet/bubbletea v1.3.4 github.com/charmbracelet/lipgloss v1.1.0 github.com/dave/jennifer v1.7.1 - github.com/docker/docker v28.0.4+incompatible + github.com/docker/docker v28.1.0+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 github.com/gabriel-vasile/mimetype v1.4.8 @@ -210,8 +210,9 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect + github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect - github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/signal v0.7.0 // indirect github.com/moby/sys/user v0.3.0 // indirect github.com/moby/sys/userns v0.1.0 // indirect diff --git a/go.sum b/go.sum index a8f00152571..6070afe395f 100644 --- a/go.sum +++ b/go.sum @@ -877,8 +877,8 @@ github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1 github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 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 v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok= -github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.1.0+incompatible h1:4iqpcWQCt3Txcz7iWIb1U3SZ/n9ffo4U+ryY5/3eOp0= +github.com/docker/docker v28.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -1332,10 +1332,12 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 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/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= -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/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= 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/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= From 9f75891f4ce437fe6f77deea66d11b7f0d6e3ab1 Mon Sep 17 00:00:00 2001 From: Trevor Dunlap <16122665+tdunlap607@users.noreply.github.com> Date: Thu, 17 Apr 2025 12:49:36 -0400 Subject: [PATCH 056/109] fix(matchUpstreamMavenPackages): Only search Maven if the metadata POM artifact ID and group ID are missing. (#2547) * fix(matchUpstreamMavenPackages): Only search Maven if the metadata POM artifact ID and group ID are missing. Signed-off-by: tdunlap607 * fix(matchUpstreamMavenPackages): Added additional logging and integration test demonstrating skipped searches Signed-off-by: tdunlap607 * fix(matchUpstreamMavenPackages): added new shouldSearchMavenBySha to determine if we need to search Maven Signed-off-by: tdunlap607 * fix(matchUpstreamMavenPackages): Remove unecessary integration test Signed-off-by: tdunlap607 * fix(matchUpstreamMavenPackages): Do not return errors in shouldSearchMavenBySha Signed-off-by: tdunlap607 --------- Signed-off-by: tdunlap607 --- grype/matcher/java/matcher.go | 54 ++++- grype/matcher/java/matcher_mocks_test.go | 12 + grype/matcher/java/matcher_test.go | 285 ++++++++++++++++++++--- 3 files changed, 301 insertions(+), 50 deletions(-) diff --git a/grype/matcher/java/matcher.go b/grype/matcher/java/matcher.go index 6831ea57b71..218200aca8f 100644 --- a/grype/matcher/java/matcher.go +++ b/grype/matcher/java/matcher.go @@ -81,23 +81,53 @@ func (m *Matcher) matchUpstreamMavenPackages(store vulnerability.Provider, p pkg ctx := context.Background() - if metadata, ok := p.Metadata.(pkg.JavaMetadata); ok { - for _, digest := range metadata.ArchiveDigests { - if digest.Algorithm == "sha1" { - indirectPackage, err := m.GetMavenPackageBySha(ctx, digest.Value) - if err != nil { - return nil, err - } - indirectMatches, _, err := internal.MatchPackageByLanguage(store, *indirectPackage, m.Type()) - if err != nil { - return nil, err - } - matches = append(matches, indirectMatches...) + // Check if we need to search Maven by SHA + searchMaven, digests := m.shouldSearchMavenBySha(p) + if searchMaven { + // If the artifact and group ID exist are missing, attempt Maven lookup using SHA-1 + for _, digest := range digests { + log.Debugf("searching maven, POM data missing for %s", p.Name) + indirectPackage, err := m.GetMavenPackageBySha(ctx, digest) + if err != nil { + return nil, err + } + indirectMatches, _, err := internal.MatchPackageByLanguage(store, *indirectPackage, m.Type()) + if err != nil { + return nil, err } + matches = append(matches, indirectMatches...) } + } else { + log.Debugf("skipping maven search, POM data present for %s", p.Name) + indirectMatches, _, err := internal.MatchPackageByLanguage(store, p, m.Type()) + if err != nil { + return nil, err + } + matches = append(matches, indirectMatches...) } match.ConvertToIndirectMatches(matches, p) return matches, nil } + +func (m *Matcher) shouldSearchMavenBySha(p pkg.Package) (bool, []string) { + digests := []string{} + + if metadata, ok := p.Metadata.(pkg.JavaMetadata); ok { + // if either the PomArtifactID or PomGroupID is missing, we need to search Maven + if metadata.PomArtifactID == "" || metadata.PomGroupID == "" { + for _, digest := range metadata.ArchiveDigests { + if digest.Algorithm == "sha1" && digest.Value != "" { + digests = append(digests, digest.Value) + } + } + // if we need to search Maven but no valid SHA-1 digests exist, skip search + if len(digests) == 0 { + return false, digests + } + } + } + + return len(digests) > 0, digests +} diff --git a/grype/matcher/java/matcher_mocks_test.go b/grype/matcher/java/matcher_mocks_test.go index f9b703e0a10..c781c7618ae 100644 --- a/grype/matcher/java/matcher_mocks_test.go +++ b/grype/matcher/java/matcher_mocks_test.go @@ -23,6 +23,18 @@ func newMockProvider() vulnerability.Provider { Constraint: version.MustGetConstraint(">=5.0.1,<5.1.7", version.UnknownFormat), Reference: vulnerability.Reference{ID: "CVE-2013-fake-3", Namespace: "github:language:" + syftPkg.Java.String()}, }, + // Package name is expected to resolve to : if pom groupID and artifactID is present + // See JavaResolver.Names: https://github.com/anchore/grype/blob/402067e958a4fa9d20384752351d6c54b0436ba1/grype/db/v6/name/java.go#L19 + { + PackageName: "org.springframework:spring-webmvc", + Constraint: version.MustGetConstraint(">=5.0.0,<5.1.7", version.UnknownFormat), + Reference: vulnerability.Reference{ID: "CVE-2014-fake-2", Namespace: "github:language:" + syftPkg.Java.String()}, + }, + { + PackageName: "org.springframework:spring-webmvc", + Constraint: version.MustGetConstraint(">=5.0.1,<5.1.7", version.UnknownFormat), + Reference: vulnerability.Reference{ID: "CVE-2013-fake-3", Namespace: "github:language:" + syftPkg.Java.String()}, + }, // unexpected... { PackageName: "org.springframework.spring-webmvc", diff --git a/grype/matcher/java/matcher_test.go b/grype/matcher/java/matcher_test.go index ad9531de45c..83e2a4b4eb3 100644 --- a/grype/matcher/java/matcher_test.go +++ b/grype/matcher/java/matcher_test.go @@ -26,57 +26,266 @@ func TestMatcherJava_matchUpstreamMavenPackage(t *testing.T) { } store := newMockProvider() - p := pkg.Package{ - ID: pkg.ID(uuid.NewString()), - Name: "org.springframework.spring-webmvc", - Version: "5.1.5.RELEASE", - Language: syftPkg.Java, - Type: syftPkg.JavaPkg, - Metadata: pkg.JavaMetadata{ - ArchiveDigests: []pkg.Digest{ + // Define test cases + testCases := []struct { + testname string + testExpectRateLimit bool + packages []pkg.Package + }{ + { + testname: "do not search maven - metadata present", + testExpectRateLimit: false, + packages: []pkg.Package{ { - Algorithm: "sha1", - Value: "236e3bfdbdc6c86629237a74f0f11414adb4e211", + ID: pkg.ID(uuid.NewString()), + Name: "org.springframework.spring-webmvc", + Version: "5.1.5.RELEASE", + Language: syftPkg.Java, + Type: syftPkg.JavaPkg, + Metadata: pkg.JavaMetadata{ + PomArtifactID: "spring-webmvc", + PomGroupID: "org.springframework", + ArchiveDigests: []pkg.Digest{ + { + Algorithm: "sha1", + Value: "236e3bfdbdc6c86629237a74f0f11414adb4e211", + }, + }, + }, + }, + }, + }, + { + testname: "search maven - missing metadata", + testExpectRateLimit: false, + packages: []pkg.Package{ + { + ID: pkg.ID(uuid.NewString()), + Name: "org.springframework.spring-webmvc", + Version: "5.1.5.RELEASE", + Language: syftPkg.Java, + Type: syftPkg.JavaPkg, + Metadata: pkg.JavaMetadata{ + PomArtifactID: "", + PomGroupID: "", + ArchiveDigests: []pkg.Digest{ + { + Algorithm: "sha1", + Value: "236e3bfdbdc6c86629237a74f0f11414adb4e211", + }, + }, + }, + }, + }, + }, + { + testname: "search maven - missing sha1 error", + testExpectRateLimit: false, + packages: []pkg.Package{ + { + ID: pkg.ID(uuid.NewString()), + Name: "org.springframework.spring-webmvc", + Version: "5.1.5.RELEASE", + Language: syftPkg.Java, + Type: syftPkg.JavaPkg, + Metadata: pkg.JavaMetadata{ + PomArtifactID: "", + PomGroupID: "", + ArchiveDigests: []pkg.Digest{ + { + Algorithm: "sha1", + Value: "", + }, + }, + }, }, }, }, } t.Run("matching from maven search results", func(t *testing.T) { - matcher := newMatcher(mockMavenSearcher{ - pkg: p, - }) - actual, _ := matcher.matchUpstreamMavenPackages(store, p) - - assert.Len(t, actual, 2, "unexpected matches count") - - foundCVEs := stringutil.NewStringSet() - for _, v := range actual { - foundCVEs.Add(v.Vulnerability.ID) - - require.NotEmpty(t, v.Details) - for _, d := range v.Details { - assert.Equal(t, match.ExactIndirectMatch, d.Type, "indirect match not indicated") - assert.Equal(t, matcher.Type(), d.Matcher, "failed to capture matcher type") - } - assert.Equal(t, p.Name, v.Package.Name, "failed to capture original package name") - } + for _, p := range testCases { + // Adding test isolation + t.Run(p.testname, func(t *testing.T) { + matcher := newMatcher(mockMavenSearcher{ + pkg: p.packages[0], + }) + actual, _ := matcher.matchUpstreamMavenPackages(store, p.packages[0]) - for _, id := range []string{"CVE-2014-fake-2", "CVE-2013-fake-3"} { - if !foundCVEs.Contains(id) { - t.Errorf("missing discovered CVE: %s", id) - } - } - if t.Failed() { - t.Logf("discovered CVES: %+v", foundCVEs) + assert.Len(t, actual, 2, "unexpected matches count") + + foundCVEs := stringutil.NewStringSet() + for _, v := range actual { + foundCVEs.Add(v.Vulnerability.ID) + + require.NotEmpty(t, v.Details) + for _, d := range v.Details { + assert.Equal(t, match.ExactIndirectMatch, d.Type, "indirect match not indicated") + assert.Equal(t, matcher.Type(), d.Matcher, "failed to capture matcher type") + } + assert.Equal(t, p.packages[0].Name, v.Package.Name, "failed to capture original package name") + } + + for _, id := range []string{"CVE-2014-fake-2", "CVE-2013-fake-3"} { + if !foundCVEs.Contains(id) { + t.Errorf("missing discovered CVE: %s", id) + } + } + if t.Failed() { + t.Logf("discovered CVES: %+v", foundCVEs) + } + + }) } }) t.Run("handles maven rate limiting", func(t *testing.T) { - matcher := newMatcher(mockMavenSearcher{simulateRateLimiting: true}) + for _, p := range testCases { + // Adding test isolation + t.Run(p.testname, func(t *testing.T) { + matcher := newMatcher(mockMavenSearcher{simulateRateLimiting: true}) - _, err := matcher.matchUpstreamMavenPackages(store, p) + _, err := matcher.matchUpstreamMavenPackages(store, p.packages[0]) - assert.Errorf(t, err, "should have gotten an error from the rate limiting") + if p.testExpectRateLimit { + assert.Errorf(t, err, "should have gotten an error from the rate limiting") + } + }) + } + }) +} + +func TestMatcherJava_shouldSearchMavenBySha(t *testing.T) { + newMatcher := func(searcher MavenSearcher) *Matcher { + return &Matcher{ + cfg: MatcherConfig{ + ExternalSearchConfig: ExternalSearchConfig{ + SearchMavenUpstream: true, + }, + }, + MavenSearcher: searcher, + } + } + + // Define test cases + testCases := []struct { + testname string + expectedShouldSearchMaven bool + testExpectedError bool + packages []pkg.Package + }{ + { + testname: "do not search maven - metadata present", + expectedShouldSearchMaven: false, + testExpectedError: false, + packages: []pkg.Package{ + { + ID: pkg.ID(uuid.NewString()), + Name: "org.springframework.spring-webmvc", + Version: "5.1.5.RELEASE", + Language: syftPkg.Java, + Type: syftPkg.JavaPkg, + Metadata: pkg.JavaMetadata{ + PomArtifactID: "spring-webmvc", + PomGroupID: "org.springframework", + ArchiveDigests: []pkg.Digest{ + { + Algorithm: "sha1", + Value: "236e3bfdbdc6c86629237a74f0f11414adb4e211", + }, + }, + }, + }, + }, + }, + { + testname: "search maven - missing metadata", + expectedShouldSearchMaven: true, + testExpectedError: false, + packages: []pkg.Package{ + { + ID: pkg.ID(uuid.NewString()), + Name: "org.springframework.spring-webmvc", + Version: "5.1.5.RELEASE", + Language: syftPkg.Java, + Type: syftPkg.JavaPkg, + Metadata: pkg.JavaMetadata{ + PomArtifactID: "", + PomGroupID: "", + ArchiveDigests: []pkg.Digest{ + { + Algorithm: "sha1", + Value: "236e3bfdbdc6c86629237a74f0f11414adb4e211", + }, + }, + }, + }, + }, + }, + { + testname: "search maven - missing artifactId", + expectedShouldSearchMaven: true, + packages: []pkg.Package{ + { + ID: pkg.ID(uuid.NewString()), + Name: "org.springframework.spring-webmvc", + Version: "5.1.5.RELEASE", + Language: syftPkg.Java, + Type: syftPkg.JavaPkg, + Metadata: pkg.JavaMetadata{ + PomArtifactID: "", + PomGroupID: "org.springframework", + ArchiveDigests: []pkg.Digest{ + { + Algorithm: "sha1", + Value: "236e3bfdbdc6c86629237a74f0f11414adb4e211", + }, + }, + }, + }, + }, + }, + { + testname: "do not search maven - missing sha1", + expectedShouldSearchMaven: false, + packages: []pkg.Package{ + { + ID: pkg.ID(uuid.NewString()), + Name: "org.springframework.spring-webmvc", + Version: "5.1.5.RELEASE", + Language: syftPkg.Java, + Type: syftPkg.JavaPkg, + Metadata: pkg.JavaMetadata{ + PomArtifactID: "", + PomGroupID: "", + ArchiveDigests: []pkg.Digest{ + { + Algorithm: "sha1", + Value: "", + }, + }, + }, + }, + }, + }, + } + + t.Run("matching from Maven search results", func(t *testing.T) { + for _, p := range testCases { + // Adding test isolation + t.Run(p.testname, func(t *testing.T) { + matcher := newMatcher(mockMavenSearcher{ + pkg: p.packages[0], + }) + actual, digests := matcher.shouldSearchMavenBySha(p.packages[0]) + + assert.Equal(t, p.expectedShouldSearchMaven, actual, "unexpected decision to search Maven") + + if actual { + assert.NotEmpty(t, digests, "sha digests should not be empty when search is expected") + } + + }) + } }) } From 89a4da8c76542191962dee01777473361d41aa14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 13:42:11 -0400 Subject: [PATCH 057/109] chore(deps): bump github.com/gabriel-vasile/mimetype from 1.4.8 to 1.4.9 (#2605) Bumps [github.com/gabriel-vasile/mimetype](https://github.com/gabriel-vasile/mimetype) from 1.4.8 to 1.4.9. - [Release notes](https://github.com/gabriel-vasile/mimetype/releases) - [Commits](https://github.com/gabriel-vasile/mimetype/compare/v1.4.8...v1.4.9) --- updated-dependencies: - dependency-name: github.com/gabriel-vasile/mimetype dependency-version: 1.4.9 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 12893fd09cc..b484be39142 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/docker/docker v28.1.0+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 - github.com/gabriel-vasile/mimetype v1.4.8 + github.com/gabriel-vasile/mimetype v1.4.9 github.com/gkampitakis/go-snaps v0.5.11 github.com/glebarez/sqlite v1.11.0 github.com/go-test/deep v1.1.1 diff --git a/go.sum b/go.sum index 6070afe395f..19ec0c7d2a6 100644 --- a/go.sum +++ b/go.sum @@ -943,8 +943,8 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= -github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= +github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/github/go-spdx/v2 v2.3.2 h1:IfdyNHTqzs4zAJjXdVQfRnxt1XMfycXoHBE2Vsm1bjs= github.com/github/go-spdx/v2 v2.3.2/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ= From 867da1373d9dc4e1119a7e77d75b52d056885651 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Apr 2025 13:42:18 -0400 Subject: [PATCH 058/109] chore(deps): bump github.com/docker/docker (#2604) Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.1.0+incompatible to 28.1.1+incompatible. - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v28.1.0...v28.1.1) --- updated-dependencies: - dependency-name: github.com/docker/docker dependency-version: 28.1.1+incompatible 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 b484be39142..4680c88813d 100644 --- a/go.mod +++ b/go.mod @@ -25,7 +25,7 @@ require ( github.com/charmbracelet/bubbletea v1.3.4 github.com/charmbracelet/lipgloss v1.1.0 github.com/dave/jennifer v1.7.1 - github.com/docker/docker v28.1.0+incompatible + github.com/docker/docker v28.1.1+incompatible github.com/dustin/go-humanize v1.0.1 github.com/facebookincubator/nvdtools v0.1.5 github.com/gabriel-vasile/mimetype v1.4.9 diff --git a/go.sum b/go.sum index 19ec0c7d2a6..b78a8fbf068 100644 --- a/go.sum +++ b/go.sum @@ -877,8 +877,8 @@ github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1 github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 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 v28.1.0+incompatible h1:4iqpcWQCt3Txcz7iWIb1U3SZ/n9ffo4U+ryY5/3eOp0= -github.com/docker/docker v28.1.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I= +github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= From 2cc74044d2b7d3c97c449a39093a49ed4f45977c 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 Apr 2025 13:12:25 -0400 Subject: [PATCH 059/109] chore(deps): update tools to latest versions (#2609) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.binny.yaml b/.binny.yaml index bb0755c7c7d..7f1c45c1fa8 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -90,7 +90,7 @@ tools: # used for running all local and CI tasks - name: task version: - want: v3.42.1 + want: v3.43.2 method: github-release with: repo: go-task/task From ee0a33cea918f7799e4252b0bf933f238fe31216 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Wed, 23 Apr 2025 10:01:57 -0400 Subject: [PATCH 060/109] fix: use package language to search when type is unknown (#2610) Previously, this fallback was only used when the package type was blank, but Syft will set the package type to unknown if there is no package type, causing the fallback to language never to be entered, which resulted in some incorrect matches. Signed-off-by: Will Murphy --- grype/db/v6/vulnerability_provider.go | 2 +- grype/db/v6/vulnerability_provider_mocks_test.go | 7 +++++++ grype/db/v6/vulnerability_provider_test.go | 9 +++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/grype/db/v6/vulnerability_provider.go b/grype/db/v6/vulnerability_provider.go index 2cf36b86223..a655155d7bb 100644 --- a/grype/db/v6/vulnerability_provider.go +++ b/grype/db/v6/vulnerability_provider.go @@ -222,7 +222,7 @@ func (vp vulnerabilityProvider) FindVulnerabilities(criteria ...vulnerability.Cr pkgSpec = &PackageSpecifier{} } // the v6 store normalizes ecosystems around the syft package type, so that field is preferred - if c.PackageType != "" { + if c.PackageType != "" && c.PackageType != syftPkg.UnknownPkg { pkgSpec.Ecosystem = string(c.PackageType) pkgType = c.PackageType } else { diff --git a/grype/db/v6/vulnerability_provider_mocks_test.go b/grype/db/v6/vulnerability_provider_mocks_test.go index d2a76838be1..6f01bba69c8 100644 --- a/grype/db/v6/vulnerability_provider_mocks_test.go +++ b/grype/db/v6/vulnerability_provider_mocks_test.go @@ -103,6 +103,13 @@ func testVulnerabilityProvider(t *testing.T) vulnerability.Provider { "cpe:2.3:*:awesome:awesome:*:*:*:*:*:*:*:*", // shouldn't match on this }, }, + { + PackageName: "Newtonsoft.Json", + Namespace: "github:language:dotnet", + ID: "GHSA-5crp-9r3c-p9vr", + VersionFormat: "unknown", + VersionConstraint: "<13.0.1", + }, // poison the well! this is not a valid entry, but we want the matching process to survive and find other good results... { PackageName: "activerecord", diff --git a/grype/db/v6/vulnerability_provider_test.go b/grype/db/v6/vulnerability_provider_test.go index 668a2b8410c..3b5ebfbf313 100644 --- a/grype/db/v6/vulnerability_provider_test.go +++ b/grype/db/v6/vulnerability_provider_test.go @@ -17,6 +17,7 @@ import ( "github.com/anchore/grype/grype/version" "github.com/anchore/grype/grype/vulnerability" "github.com/anchore/syft/syft/cpe" + syftPkg "github.com/anchore/syft/syft/pkg" ) func Test_FindVulnerabilitiesByDistro(t *testing.T) { @@ -303,6 +304,14 @@ func Test_FindVulnerabilitiesByByID(t *testing.T) { require.Empty(t, actual) } +func Test_FindVulnerabilitiesByEcosystem_UnknownPackageType(t *testing.T) { + provider := testVulnerabilityProvider(t) + actual, err := provider.FindVulnerabilities(search.ByEcosystem(syftPkg.Dotnet, syftPkg.UnknownPkg)) + require.NoError(t, err) + require.NotEmpty(t, actual) + require.Equal(t, actual[0].Reference.ID, "GHSA-5crp-9r3c-p9vr") +} + func Test_DataSource(t *testing.T) { tests := []struct { name string From 6c45f2eee33d9bffdceec05a2142f050de406906 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, 24 Apr 2025 13:26:15 -0400 Subject: [PATCH 061/109] chore(deps): update tools to latest versions (#2613) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.binny.yaml b/.binny.yaml index 7f1c45c1fa8..8a58ad2b233 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -98,7 +98,7 @@ tools: # used for triggering a release - name: gh version: - want: v2.70.0 + want: v2.71.0 method: github-release with: repo: cli/cli From a29c5de1ac63f3e92c84e11741c638d2880e0acb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:26:18 -0400 Subject: [PATCH 062/109] chore(deps): bump actions/setup-python in /.github/actions/bootstrap (#2614) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5.5.0 to 5.6.0. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/8d9ed9ac5c53483de85588cdf95a591a75ab9f55...a26af69be951a213d495a4c3e4e4022e16d87065) --- updated-dependencies: - dependency-name: actions/setup-python dependency-version: 5.6.0 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/actions/bootstrap/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index ebf9508cb9e..06b3fb4fd16 100644 --- a/.github/actions/bootstrap/action.yaml +++ b/.github/actions/bootstrap/action.yaml @@ -37,7 +37,7 @@ runs: with: go-version: ${{ inputs.go-version }} - - uses: actions/setup-python@8d9ed9ac5c53483de85588cdf95a591a75ab9f55 # v5.5.0 + - uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version: ${{ inputs.python-version }} From 04a9d0f2f6361cc67fb9ac39468aa3bffd89cc2d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:26:27 -0400 Subject: [PATCH 063/109] chore(deps): bump sigstore/cosign-installer from 3.8.1 to 3.8.2 (#2612) Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.8.1 to 3.8.2. - [Release notes](https://github.com/sigstore/cosign-installer/releases) - [Commits](https://github.com/sigstore/cosign-installer/compare/d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a...3454372f43399081ed03b604cb2d021dabca52bb) --- updated-dependencies: - dependency-name: sigstore/cosign-installer dependency-version: 3.8.2 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/validations.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml index c04758517ed..75c3340bff5 100644 --- a/.github/workflows/validations.yaml +++ b/.github/workflows/validations.yaml @@ -218,7 +218,7 @@ jobs: runs-on: macos-latest steps: - name: Install Cosign - uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a #v3.8.1 + uses: sigstore/cosign-installer@3454372f43399081ed03b604cb2d021dabca52bb #v3.8.2 - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2 From 4bf616df3b7bbcaacf8d4887c44487030cf8172a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 13:26:33 -0400 Subject: [PATCH 064/109] chore(deps): bump github/codeql-action from 3.28.15 to 3.28.16 (#2611) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.15 to 3.28.16. - [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/45775bd8235c68ba998cffa5171334d58593da47...28deaeda66b76a05916b6923827895f2b14ab387) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.28.16 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 | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index e18939027a1..4f5b659c241 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -63,7 +63,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 + uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -85,4 +85,4 @@ jobs: run: make grype - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 + uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 3eb1c9e8972..b607ac7687b 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -38,6 +38,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@45775bd8235c68ba998cffa5171334d58593da47 # v1.0.26 + uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v1.0.26 with: sarif_file: results.sarif From d9f54570015071cdb5d5bdd0184c959728b211cd Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 24 Apr 2025 15:42:01 -0400 Subject: [PATCH 065/109] Add test for use statement (#2617) * add test for use statement Signed-off-by: Alex Goodman * bump syft Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- go.mod | 13 +++++++------ go.sum | 30 ++++++++++++++++-------------- grype/pkg/package_test.go | 9 +++++++++ 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 4680c88813d..78fc3b33dae 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 github.com/anchore/stereoscope v0.1.3 - github.com/anchore/syft v1.22.0 + github.com/anchore/syft v1.22.1-0.20250424173805-9af087d21318 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 @@ -118,13 +118,13 @@ require ( github.com/buger/jsonparser v1.1.1 // indirect github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/charmbracelet/bubbles v0.20.0 // indirect + github.com/charmbracelet/bubbles v0.21.0 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/harmonica v0.2.0 // indirect github.com/charmbracelet/x/ansi v0.8.0 // indirect github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect github.com/charmbracelet/x/term v0.2.1 // indirect - github.com/cloudflare/circl v1.6.0 // indirect + github.com/cloudflare/circl v1.6.1 // indirect github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 // indirect github.com/containerd/cgroups v1.1.0 // indirect github.com/containerd/containerd v1.7.27 // indirect @@ -162,7 +162,7 @@ require ( github.com/glebarez/go-sqlite v1.22.0 // indirect github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect github.com/go-git/go-billy/v5 v5.6.2 // indirect - github.com/go-git/go-git/v5 v5.14.0 // indirect + github.com/go-git/go-git/v5 v5.16.0 // indirect github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-restruct/restruct v1.2.0-alpha // indirect @@ -202,7 +202,8 @@ require ( github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect - github.com/mholt/archives v0.1.0 // indirect + github.com/mholt/archives v0.1.1 // indirect + github.com/minio/minlz v1.0.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect @@ -221,7 +222,7 @@ require ( github.com/muesli/cancelreader v0.2.2 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect github.com/nwaples/rardecode v1.1.3 // indirect - github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 // indirect + github.com/nwaples/rardecode/v2 v2.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect github.com/opencontainers/runtime-spec v1.1.0 // indirect diff --git a/go.sum b/go.sum index b78a8fbf068..5133145ac65 100644 --- a/go.sum +++ b/go.sum @@ -710,8 +710,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiE github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.3 h1:qCbu9h7NCTIjWYyuAWwwEOuZia2UP5rvkCSA4reDzSg= github.com/anchore/stereoscope v0.1.3/go.mod h1:kU4LkiocOimxuSnlmJgilHCfDMCR5t/UroKIpzI8jNw= -github.com/anchore/syft v1.22.0 h1:lmNuy1uQ9GZeO6HPms3ik3W0hTE9MxP46vtC3vssBe4= -github.com/anchore/syft v1.22.0/go.mod h1:5U0av1cf7i/O2VusM29HTsVYUsARjBvgPCbP4RtSEyk= +github.com/anchore/syft v1.22.1-0.20250424173805-9af087d21318 h1:V6PY55YVB/YtyUXtNiiTt1jl8YrUgp6UAWBC/zbSZTU= +github.com/anchore/syft v1.22.1-0.20250424173805-9af087d21318/go.mod h1:jMMy7PDu9fo6aygM0lle2LLFdUnCEy6QSXnBFHxOaco= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -786,8 +786,8 @@ github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= -github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= +github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= +github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= @@ -815,8 +815,8 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38 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= -github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= -github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= +github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0= +github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -971,8 +971,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4= github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII= -github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60= -github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k= +github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ= +github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8= 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= @@ -1271,8 +1271,8 @@ github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuz github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= github.com/lyft/protoc-gen-star/v2 v2.0.1/go.mod h1:RcCdONR2ScXaYnQC5tUzxzlpA3WVYF7/opLeUgcQs/o= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo= @@ -1306,13 +1306,15 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4 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= -github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q= -github.com/mholt/archives v0.1.0/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I= +github.com/mholt/archives v0.1.1 h1:c7J3qXN1FB54y0qiUXiq9Bxk4eCUc8pdXWwOhZdRzeY= +github.com/mholt/archives v0.1.1/go.mod h1:FQVz01Q2uXKB/35CXeW/QFO23xT+hSCGZHVtha78U4I= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8IeTMnF8JTXieKnO4Z6JCsikNEzj0DwauVzE= +github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ= +github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec= github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= @@ -1364,8 +1366,8 @@ github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdh github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= -github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 h1:MYzLheyVx1tJVDqfu3YnN4jtnyALNzLvwl+f58TcvQY= -github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY= +github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U= +github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw= 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.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k= diff --git a/grype/pkg/package_test.go b/grype/pkg/package_test.go index cd6192be3ea..f2950fac2f3 100644 --- a/grype/pkg/package_test.go +++ b/grype/pkg/package_test.go @@ -320,6 +320,15 @@ func TestNew(t *testing.T) { }, }, }, + { + name: "github-actions-use-statement", + syftPkg: syftPkg.Package{ + Metadata: syftPkg.GitHubActionsUseStatement{ + Value: "a", + Comment: "a", + }, + }, + }, { name: "golang-metadata", syftPkg: syftPkg.Package{ From 7bf06a8a5a3dcae945542561cecbe20d04e754ac 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, 24 Apr 2025 20:48:43 +0000 Subject: [PATCH 066/109] chore(deps): update anchore dependencies (#2616) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com> --- go.mod | 7 +++---- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index 78fc3b33dae..e861e01a560 100644 --- a/go.mod +++ b/go.mod @@ -17,8 +17,8 @@ require ( github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 - github.com/anchore/stereoscope v0.1.3 - github.com/anchore/syft v1.22.1-0.20250424173805-9af087d21318 + github.com/anchore/stereoscope v0.1.4 + github.com/anchore/syft v1.23.0 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 @@ -141,7 +141,7 @@ require ( github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/cli v28.0.4+incompatible // indirect + github.com/docker/cli v28.1.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.8.2 // indirect github.com/docker/go-connections v0.5.0 // indirect @@ -211,7 +211,6 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/locker v1.0.1 // indirect - github.com/moby/sys/atomicwriter v0.1.0 // indirect github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/sys/signal v0.7.0 // indirect diff --git a/go.sum b/go.sum index 5133145ac65..3862a5cceae 100644 --- a/go.sum +++ b/go.sum @@ -708,10 +708,10 @@ github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY= github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= -github.com/anchore/stereoscope v0.1.3 h1:qCbu9h7NCTIjWYyuAWwwEOuZia2UP5rvkCSA4reDzSg= -github.com/anchore/stereoscope v0.1.3/go.mod h1:kU4LkiocOimxuSnlmJgilHCfDMCR5t/UroKIpzI8jNw= -github.com/anchore/syft v1.22.1-0.20250424173805-9af087d21318 h1:V6PY55YVB/YtyUXtNiiTt1jl8YrUgp6UAWBC/zbSZTU= -github.com/anchore/syft v1.22.1-0.20250424173805-9af087d21318/go.mod h1:jMMy7PDu9fo6aygM0lle2LLFdUnCEy6QSXnBFHxOaco= +github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek= +github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw= +github.com/anchore/syft v1.23.0 h1:rRF3ZjLi6s4TUbNSME4S1bKbAVX3tcdKRGjBS82iX60= +github.com/anchore/syft v1.23.0/go.mod h1:vDV0VBC601wHZ2nGuxqoDjfYsiu87WmE0w8HG3RDI6k= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -873,8 +873,8 @@ github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4i github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A= -github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k= +github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 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 v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I= From 41a61961ed6018a4cb8b876355929bb2a4586487 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Fri, 25 Apr 2025 10:07:01 -0400 Subject: [PATCH 067/109] fix: only fallback to language if language is non-blank (#2621) * fix: only fallback to language if language is non-blank Signed-off-by: Will Murphy * test: add units for known pkg, unknown language, etc Signed-off-by: Will Murphy --------- Signed-off-by: Will Murphy --- grype/db/v6/vulnerability_provider.go | 15 ++++-- grype/db/v6/vulnerability_provider_test.go | 53 ++++++++++++++++++++-- 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/grype/db/v6/vulnerability_provider.go b/grype/db/v6/vulnerability_provider.go index a655155d7bb..6e012daac38 100644 --- a/grype/db/v6/vulnerability_provider.go +++ b/grype/db/v6/vulnerability_provider.go @@ -222,11 +222,20 @@ func (vp vulnerabilityProvider) FindVulnerabilities(criteria ...vulnerability.Cr pkgSpec = &PackageSpecifier{} } // the v6 store normalizes ecosystems around the syft package type, so that field is preferred - if c.PackageType != "" && c.PackageType != syftPkg.UnknownPkg { - pkgSpec.Ecosystem = string(c.PackageType) + switch { + case c.PackageType != "" && c.PackageType != syftPkg.UnknownPkg: + // prefer to match by a non-blank, known package type pkgType = c.PackageType - } else { + pkgSpec.Ecosystem = string(c.PackageType) + case c.Language != "": + // if there's no known package type, but there is a non-blank language + // try that. pkgSpec.Ecosystem = string(c.Language) + case c.PackageType == syftPkg.UnknownPkg: + // if language is blank, and package type is explicitly "UnknownPkg" and not + // just blank, use that. + pkgType = c.PackageType + pkgSpec.Ecosystem = string(c.PackageType) } applied = true case *search.IDCriteria: diff --git a/grype/db/v6/vulnerability_provider_test.go b/grype/db/v6/vulnerability_provider_test.go index 3b5ebfbf313..73b451ced82 100644 --- a/grype/db/v6/vulnerability_provider_test.go +++ b/grype/db/v6/vulnerability_provider_test.go @@ -305,11 +305,56 @@ func Test_FindVulnerabilitiesByByID(t *testing.T) { } func Test_FindVulnerabilitiesByEcosystem_UnknownPackageType(t *testing.T) { + tests := []struct { + name string + packageName string + packageType syftPkg.Type + language syftPkg.Language + expectedIDs []string + }{ + { + name: "known package type", + packageName: "Newtonsoft.Json", + packageType: syftPkg.DotnetPkg, + language: syftPkg.Java, // deliberately wrong to prove we're using package type + expectedIDs: []string{"GHSA-5crp-9r3c-p9vr"}, + }, + { + name: "unknown package type, known language", + packageName: "Newtonsoft.Json", + packageType: syftPkg.UnknownPkg, + language: syftPkg.Dotnet, + expectedIDs: []string{"GHSA-5crp-9r3c-p9vr"}, + }, + { + name: "unknown package type, unknown language", + packageName: "Newtonsoft.Json", + packageType: syftPkg.UnknownPkg, + language: syftPkg.UnknownLanguage, + // The vuln GHSA-5crp-9r3c-p9vr is specifically associated + // with the dotnet ecosystem, so it should not be returned here. + // In a real search for UnknownPkg + UnknownLanguage, there should + // be a separate search.ByCPE run that _does_ return it. + expectedIDs: []string{}, + }, + } provider := testVulnerabilityProvider(t) - actual, err := provider.FindVulnerabilities(search.ByEcosystem(syftPkg.Dotnet, syftPkg.UnknownPkg)) - require.NoError(t, err) - require.NotEmpty(t, actual) - require.Equal(t, actual[0].Reference.ID, "GHSA-5crp-9r3c-p9vr") + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual, err := provider.FindVulnerabilities( + search.ByEcosystem(test.language, test.packageType), + search.ByPackageName(test.packageName), + ) + require.NoError(t, err) + actualIDs := make([]string, len(actual)) + for idx, vuln := range actual { + actualIDs[idx] = vuln.ID + } + if d := cmp.Diff(test.expectedIDs, actualIDs); d != "" { + t.Errorf("diff: %+v", d) + } + }) + } } func Test_DataSource(t *testing.T) { From a4b86a65734df8e693bda068e855b8c23108730c 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, 25 Apr 2025 10:07:51 -0400 Subject: [PATCH 068/109] chore(deps): update tools to latest versions (#2619) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.binny.yaml b/.binny.yaml index 8a58ad2b233..b4f41f7e63a 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -26,7 +26,7 @@ tools: # used for linting - name: golangci-lint version: - want: v2.1.2 + want: v2.1.5 method: github-release with: repo: golangci/golangci-lint @@ -98,7 +98,7 @@ tools: # used for triggering a release - name: gh version: - want: v2.71.0 + want: v2.71.2 method: github-release with: repo: cli/cli From 7e6ba817350bdb922f35e4437aa01869cf0a8be0 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, 25 Apr 2025 15:26:41 +0000 Subject: [PATCH 069/109] chore(deps): update anchore dependencies (#2622) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: willmurphyscode <12529630+willmurphyscode@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 e861e01a560..f074664bbd1 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 github.com/anchore/stereoscope v0.1.4 - github.com/anchore/syft v1.23.0 + github.com/anchore/syft v1.23.1 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 diff --git a/go.sum b/go.sum index 3862a5cceae..85124785706 100644 --- a/go.sum +++ b/go.sum @@ -710,8 +710,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiE github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek= github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw= -github.com/anchore/syft v1.23.0 h1:rRF3ZjLi6s4TUbNSME4S1bKbAVX3tcdKRGjBS82iX60= -github.com/anchore/syft v1.23.0/go.mod h1:vDV0VBC601wHZ2nGuxqoDjfYsiu87WmE0w8HG3RDI6k= +github.com/anchore/syft v1.23.1 h1:QFWFMlwyJprl60RMrceSB6FJpZCsAQeWVLd9Ltzgwa0= +github.com/anchore/syft v1.23.1/go.mod h1:vDV0VBC601wHZ2nGuxqoDjfYsiu87WmE0w8HG3RDI6k= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= From 0d7bd4405af92f9ed5b3b0935e77d7b101e11e19 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 11:52:12 -0400 Subject: [PATCH 070/109] chore(deps): bump gorm.io/gorm from 1.25.12 to 1.26.0 (#2623) Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.25.12 to 1.26.0. - [Release notes](https://github.com/go-gorm/gorm/releases) - [Commits](https://github.com/go-gorm/gorm/compare/v1.25.12...v1.26.0) --- updated-dependencies: - dependency-name: gorm.io/gorm dependency-version: 1.26.0 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 f074664bbd1..e5dbedcebb4 100644 --- a/go.mod +++ b/go.mod @@ -68,7 +68,7 @@ require ( golang.org/x/time v0.11.0 golang.org/x/tools v0.32.0 gopkg.in/yaml.v3 v3.0.1 - gorm.io/gorm v1.25.12 + gorm.io/gorm v1.26.0 ) require ( diff --git a/go.sum b/go.sum index 85124785706..c7feead32ba 100644 --- a/go.sum +++ b/go.sum @@ -2400,8 +2400,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= -gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +gorm.io/gorm v1.26.0 h1:9lqQVPG5aNNS6AyHdRiwScAVnXHg/L/Srzx55G5fOgs= +gorm.io/gorm v1.26.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= 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= From 097b0882fd49281e488f80c10a97d4d3ee3281d2 Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Fri, 25 Apr 2025 14:03:10 -0400 Subject: [PATCH 071/109] chore: improve release quality gate (#2625) The release quality gate step polls to make sure the commit to be released has a green CI run. However if a PR is merged immediately before the release job is run, this step often times out. Raise the timeout to 20 minutes, which covers the time taken by recent CI runs. Additionally, include the "Quality tests" step from validations.yaml in the tests to block on. It should always have been included. Signed-off-by: Will Murphy --- .github/workflows/release.yaml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e2cd39ec005..8c2b49f9311 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -60,6 +60,17 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} # This check name is defined as the github action job name (in .github/workflows/testing.yaml) checkName: "Integration tests" + timeoutSeconds: 1200 # 20 minutes, it sometimes takes that long + ref: ${{ github.event.pull_request.head.sha || github.sha }} + + - name: Check integration test results + uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0 + id: quality_tests + with: + token: ${{ secrets.GITHUB_TOKEN }} + # This check name is defined as the github action job name (in .github/workflows/testing.yaml) + checkName: "Quality tests" + timeoutSeconds: 1200 # 20 minutes, it sometimes takes that long ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Check acceptance test results (linux) @@ -90,11 +101,12 @@ jobs: ref: ${{ github.event.pull_request.head.sha || github.sha }} - name: Quality gate - if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' || steps.integration.outputs.conclusion != 'success' || steps.cli-linux.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success' + if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' || steps.integration.outputs.conclusion != 'success' || steps.quality_tests.outputs.conclusion != 'success' || steps.cli-linux.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success' run: | echo "Static Analysis Status: ${{ steps.static-analysis.conclusion }}" echo "Unit Test Status: ${{ steps.unit.outputs.conclusion }}" echo "Integration Test Status: ${{ steps.integration.outputs.conclusion }}" + echo "Quality Test Status: ${{ steps.quality_tests.outputs.conclusion }}" echo "Acceptance Test (Linux) Status: ${{ steps.acceptance-linux.outputs.conclusion }}" echo "Acceptance Test (Mac) Status: ${{ steps.acceptance-mac.outputs.conclusion }}" echo "CLI Test (Linux) Status: ${{ steps.cli-linux.outputs.conclusion }}" From 6f48a794b34927836a6a0e33ecb5a683b243ee5e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 15:20:34 -0400 Subject: [PATCH 072/109] chore(deps): bump anchore/sbom-action from 0.18.0 to 0.19.0 (#2624) Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.18.0 to 0.19.0. - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/f325610c9f50a54015d37c8d16cb3b0e2c8f4de0...9f7302141466aa6482940f15371237e9d9f4c34a) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-version: 0.19.0 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/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 8c2b49f9311..0f1c1398f9a 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -170,7 +170,7 @@ jobs: # for updating brew formula in anchore/homebrew-syft GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }} - - uses: anchore/sbom-action@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0 + - uses: anchore/sbom-action@9f7302141466aa6482940f15371237e9d9f4c34a # v0.19.0 continue-on-error: true with: artifact-name: sbom.spdx.json From 8d756b1549a6ebfde6152b010cc114d01a564f06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:03:16 -0400 Subject: [PATCH 073/109] chore(deps): bump github.com/charmbracelet/bubbletea from 1.3.4 to 1.3.5 (#2629) Bumps [github.com/charmbracelet/bubbletea](https://github.com/charmbracelet/bubbletea) from 1.3.4 to 1.3.5. - [Release notes](https://github.com/charmbracelet/bubbletea/releases) - [Changelog](https://github.com/charmbracelet/bubbletea/blob/main/.goreleaser.yml) - [Commits](https://github.com/charmbracelet/bubbletea/compare/v1.3.4...v1.3.5) --- updated-dependencies: - dependency-name: github.com/charmbracelet/bubbletea dependency-version: 1.3.5 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 e5dbedcebb4..8960afe8b09 100644 --- a/go.mod +++ b/go.mod @@ -22,7 +22,7 @@ require ( github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 - github.com/charmbracelet/bubbletea v1.3.4 + github.com/charmbracelet/bubbletea v1.3.5 github.com/charmbracelet/lipgloss v1.1.0 github.com/dave/jennifer v1.7.1 github.com/docker/docker v28.1.1+incompatible diff --git a/go.sum b/go.sum index c7feead32ba..f6ec232fb46 100644 --- a/go.sum +++ b/go.sum @@ -788,8 +788,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs= github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg= -github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI= -github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo= +github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc= +github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs= github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk= github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ= From d4aacc0539a52701f3203a7e46dfec0eb8e8eb03 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, 1 May 2025 10:57:10 -0400 Subject: [PATCH 074/109] chore(deps): update tools to latest versions (#2626) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.binny.yaml b/.binny.yaml index b4f41f7e63a..d153b15f878 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -58,7 +58,7 @@ tools: # used to release all artifacts - name: goreleaser version: - want: v2.8.2 + want: v2.9.0 method: github-release with: repo: goreleaser/goreleaser @@ -90,7 +90,7 @@ tools: # used for running all local and CI tasks - name: task version: - want: v3.43.2 + want: v3.43.3 method: github-release with: repo: go-task/task @@ -98,7 +98,7 @@ tools: # used for triggering a release - name: gh version: - want: v2.71.2 + want: v2.72.0 method: github-release with: repo: cli/cli From bc5ea6fa816b8bc7a638e654d542b07669f6decf Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Thu, 1 May 2025 18:17:46 +0100 Subject: [PATCH 075/109] fix: v5 compatible namespace logic (#2634) Signed-off-by: Weston Steimel --- .../dbsearch/affected_packages_test.go | 4 +- grype/db/v6/vulnerability.go | 68 ++++++++++--------- .../v6/vulnerability_provider_mocks_test.go | 12 +++- grype/db/v6/vulnerability_test.go | 56 ++++++++++++--- 4 files changed, 96 insertions(+), 44 deletions(-) diff --git a/cmd/grype/cli/commands/internal/dbsearch/affected_packages_test.go b/cmd/grype/cli/commands/internal/dbsearch/affected_packages_test.go index aa34e73a384..5c6442f9581 100644 --- a/cmd/grype/cli/commands/internal/dbsearch/affected_packages_test.go +++ b/cmd/grype/cli/commands/internal/dbsearch/affected_packages_test.go @@ -356,7 +356,7 @@ func TestNewAffectedPackageRows(t *testing.T) { }, AffectedPackageInfo: AffectedPackageInfo{ CPE: &CPE{Part: "a", Vendor: "vendor1", Product: "product1"}, - Namespace: "nvd:cpe", + Namespace: "provider2:cpe", Detail: v6.AffectedPackageBlob{ CVEs: []string{"CVE-9876-5432"}, Ranges: []v6.AffectedRange{ @@ -588,7 +588,7 @@ func TestAffectedPackages(t *testing.T) { }, AffectedPackageInfo: AffectedPackageInfo{ CPE: &CPE{Part: "a", Vendor: "vendor1", Product: "product1"}, - Namespace: "nvd:cpe", + Namespace: "provider2:cpe", Detail: v6.AffectedPackageBlob{ CVEs: []string{"CVE-9876-5432"}, Ranges: []v6.AffectedRange{ diff --git a/grype/db/v6/vulnerability.go b/grype/db/v6/vulnerability.go index e0c93772415..c17f528bea9 100644 --- a/grype/db/v6/vulnerability.go +++ b/grype/db/v6/vulnerability.go @@ -146,39 +146,10 @@ func getPackageQualifiers(affected *AffectedPackageBlob) []qualifier.Qualifier { // //nolint:funlen func MimicV5Namespace(vuln *VulnerabilityHandle, affected *AffectedPackageHandle) string { - if affected == nil { // for CPE matches - return v5NvdNamespace - } - switch vuln.Provider.ID { - case "nvd": - return v5NvdNamespace - case "github": - language := affected.Package.Ecosystem - // normalize from purl type, github ecosystem types, and vunnel mappings - switch strings.ToLower(language) { - case "golang", string(pkg.GoModulePkg): - language = "go" - case "composer", string(pkg.PhpComposerPkg): - language = "php" - case "cargo", string(pkg.RustPkg): - language = "rust" - case "pub", string(pkg.DartPubPkg): - language = "dart" - case "nuget", string(pkg.DotnetPkg): - language = "dotnet" - case "maven", string(pkg.JavaPkg), string(pkg.JenkinsPluginPkg): - language = "java" - case "swifturl", string(pkg.SwiplPackPkg), string(pkg.SwiftPkg): - language = "swift" - case "node", string(pkg.NpmPkg): - language = "javascript" - case "pypi", "pip", string(pkg.PythonPkg): - language = "python" - case "rubygems", string(pkg.GemPkg): - language = "ruby" - } - return fmt.Sprintf("github:language:%s", language) + if affected == nil || affected.Package == nil { // for CPE matches + return fmt.Sprintf("%s:cpe", vuln.Provider.ID) } + if affected.OperatingSystem != nil { // distro family fixes family := affected.OperatingSystem.Name @@ -229,6 +200,39 @@ func MimicV5Namespace(vuln *VulnerabilityHandle, affected *AffectedPackageHandle return fmt.Sprintf("%s:distro:%s:%s", pr, family, ver) } + + if affected.Package != nil { + language := affected.Package.Ecosystem + // normalize from purl type, github ecosystem types, and vunnel mappings + switch strings.ToLower(language) { + case "golang", string(pkg.GoModulePkg): + language = "go" + case "composer", string(pkg.PhpComposerPkg): + language = "php" + case "cargo", string(pkg.RustPkg): + language = "rust" + case "pub", string(pkg.DartPubPkg): + language = "dart" + case "nuget", string(pkg.DotnetPkg): + language = "dotnet" + case "maven", string(pkg.JavaPkg), string(pkg.JenkinsPluginPkg): + language = "java" + case "swifturl", string(pkg.SwiplPackPkg), string(pkg.SwiftPkg): + language = "swift" + case "node", string(pkg.NpmPkg): + language = "javascript" + case "pypi", "pip", string(pkg.PythonPkg): + language = "python" + case "rubygems", string(pkg.GemPkg): + language = "ruby" + case "msrc", string(pkg.KbPkg): // msrc packages were previously modelled as distro + return fmt.Sprintf("%s:distro:windows:%s", vuln.Provider.ID, affected.Package.Name) + case "": // CPE + return fmt.Sprintf("%s:cpe", vuln.Provider.ID) + } + return fmt.Sprintf("%s:language:%s", vuln.Provider.ID, language) + } + // this shouldn't happen and is not a valid v5 namespace, but some information is better than none return vuln.Provider.ID } diff --git a/grype/db/v6/vulnerability_provider_mocks_test.go b/grype/db/v6/vulnerability_provider_mocks_test.go index 6f01bba69c8..339fe650fc5 100644 --- a/grype/db/v6/vulnerability_provider_mocks_test.go +++ b/grype/db/v6/vulnerability_provider_mocks_test.go @@ -29,7 +29,7 @@ func testVulnerabilityProvider(t *testing.T) vulnerability.Provider { aWeekAgo := time.Now().Add(-7 * 24 * time.Hour) twoWeeksAgo := time.Now().Add(-14 * 24 * time.Hour) - prov := &Provider{ + debianProvider := &Provider{ ID: "debian", Version: "1", Processor: "debian-processor", @@ -37,6 +37,14 @@ func testVulnerabilityProvider(t *testing.T) vulnerability.Provider { InputDigest: hex.EncodeToString([]byte("debian")), } + nvdProvider := &Provider{ + ID: "nvd", + Version: "1", + Processor: "nvd-processor", + DateCaptured: &aDayAgo, + InputDigest: hex.EncodeToString([]byte("nvd")), + } + v5vulns := []v5.Vulnerability{ // neutron { @@ -125,10 +133,12 @@ func testVulnerabilityProvider(t *testing.T) vulnerability.Provider { for _, v := range v5vulns { var os *OperatingSystem + prov := nvdProvider switch v.Namespace { case "nvd:cpe": case "debian:distro:debian:8": + prov = debianProvider os = &OperatingSystem{ Name: "debian", MajorVersion: "8", diff --git a/grype/db/v6/vulnerability_test.go b/grype/db/v6/vulnerability_test.go index 2feff56df19..5335a3db626 100644 --- a/grype/db/v6/vulnerability_test.go +++ b/grype/db/v6/vulnerability_test.go @@ -138,12 +138,13 @@ func TestV5Namespace(t *testing.T) { // +--------------------------------------+ type testCase struct { - name string - provider string // from Providers.id - ecosystem string // only used when provider is "github" - osName string // only used for OS-based providers - osVersion string // only used for OS-based providers - expected string + name string + provider string // from Providers.id + ecosystem string // only used when provider non-os provider + packageName string // only used for msrc + osName string // only used for OS-based providers + osVersion string // only used for OS-based providers + expected string // } tests := []testCase{ @@ -434,6 +435,39 @@ func TestV5Namespace(t *testing.T) { osVersion: "9.3.1", expected: "oracle:distro:oraclelinux:9", }, + // msrc is modeled as a distro for v5 but is just a package in v6 + { + name: "microsoft msrc-kb", + provider: "msrc", + ecosystem: "msrc-kb", + packageName: "10012", + expected: "msrc:distro:windows:10012", + }, + + // new provider existing ecosystem + { + name: "grizzly go-module", + provider: "grizzly", + ecosystem: "go-module", + expected: "grizzly:language:go", + }, + + // new provider new ecosystem + { + name: "armadillo pizza", + provider: "armadillo", + ecosystem: "pizza", + expected: "armadillo:language:pizza", + }, + + // new OS + { + name: "gothmog", + provider: "gothmog", + osName: "gothmoglinux", + osVersion: "zzzzzz11123", + expected: "gothmog:distro:gothmoglinux:zzzzzz11123", + }, } for _, tt := range tests { @@ -443,6 +477,7 @@ func TestV5Namespace(t *testing.T) { ID: tt.provider, }, } + pkg := &AffectedPackageHandle{} if tt.osName != "" { @@ -457,11 +492,14 @@ func TestV5Namespace(t *testing.T) { MinorVersion: minor, LabelVersion: label, } - } - - if tt.provider == "github" { + pkg.Package = &Package{ + Name: "os-package", + Ecosystem: "os-ecosystem", + } + } else if tt.ecosystem != "" { pkg.Package = &Package{ Ecosystem: tt.ecosystem, + Name: tt.packageName, } } From ea57f5e8e40f9103db4bbd0390def664b937cd00 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, 1 May 2025 15:01:18 -0400 Subject: [PATCH 076/109] test: update quality gate db to latest version (#2633) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com> --- test/quality/test-db | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/quality/test-db b/test/quality/test-db index 9fdc4001a56..75c7e136081 100644 --- a/test/quality/test-db +++ b/test/quality/test-db @@ -1 +1 @@ -vulnerability-db_v6.0.2_2025-04-01T01:31:39Z_1743480497.tar.zst +vulnerability-db_v6.0.2_2025-05-01T01:31:33Z_1746072708.tar.zst From 61b263f71cb1659b5895458b8c5462bafc95f1f7 Mon Sep 17 00:00:00 2001 From: cyw Date: Sat, 3 May 2025 03:31:32 +0800 Subject: [PATCH 077/109] fix: use namespace with name when purl is ecosystem golang in purl decoder (#2586) * fix: use namespace with name when purl is ecosystem golang in purl decoder (#2586) --------- Signed-off-by: goatwu1993 Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- grype/pkg/purl_provider.go | 9 +++- grype/pkg/purl_provider_test.go | 84 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/grype/pkg/purl_provider.go b/grype/pkg/purl_provider.go index 2b8f14f7496..56f830196f0 100644 --- a/grype/pkg/purl_provider.go +++ b/grype/pkg/purl_provider.go @@ -193,8 +193,13 @@ func purlToPackage(rawLine string) (*Package, *pkg.Package, string, string, erro version = fmt.Sprintf("%s:%s", epoch, purl.Version) } + name := purl.Name + if pkgType == pkg.GoModulePkg && purl.Namespace != "" { + name = purl.Namespace + "/" + name + } + syftPkg := pkg.Package{ - Name: purl.Name, + Name: name, Version: version, Type: pkgType, CPEs: cpes, @@ -206,7 +211,7 @@ func purlToPackage(rawLine string) (*Package, *pkg.Package, string, string, erro return &Package{ ID: ID(purl.String()), CPEs: cpes, - Name: purl.Name, + Name: name, Version: version, Type: pkgType, Language: pkg.LanguageByName(purl.Type), diff --git a/grype/pkg/purl_provider_test.go b/grype/pkg/purl_provider_test.go index ca909402b8b..10b605e2e17 100644 --- a/grype/pkg/purl_provider_test.go +++ b/grype/pkg/purl_provider_test.go @@ -375,6 +375,90 @@ func Test_PurlProvider(t *testing.T) { }, }, }, + { + name: "include namespace in name when purl is type Golang", + userInput: "pkg:golang/k8s.io/ingress-nginx@v1.11.2", + context: Context{ + Source: &source.Description{ + Metadata: PURLLiteralMetadata{PURL: "pkg:golang/k8s.io/ingress-nginx@v1.11.2"}, + }, + }, + pkgs: []Package{ + { + Name: "k8s.io/ingress-nginx", + Version: "v1.11.2", + Type: pkg.GoModulePkg, + PURL: "pkg:golang/k8s.io/ingress-nginx@v1.11.2", + }, + }, + sbom: &sbom.SBOM{ + Artifacts: sbom.Artifacts{ + Packages: pkg.NewCollection(pkg.Package{ + Name: "k8s.io/ingress-nginx", + Version: "v1.11.2", + Type: pkg.GoModulePkg, + Language: pkg.Go, + PURL: "pkg:golang/k8s.io/ingress-nginx@v1.11.2", + }), + }, + }, + }, + { + name: "include complex namespace in name when purl is type Golang", + userInput: "pkg:golang/github.com/wazuh/wazuh@v4.5.0", + context: Context{ + Source: &source.Description{ + Metadata: PURLLiteralMetadata{PURL: "pkg:golang/github.com/wazuh/wazuh@v4.5.0"}, + }, + }, + pkgs: []Package{ + { + Name: "github.com/wazuh/wazuh", + Version: "v4.5.0", + Type: pkg.GoModulePkg, + PURL: "pkg:golang/github.com/wazuh/wazuh@v4.5.0", + }, + }, + sbom: &sbom.SBOM{ + Artifacts: sbom.Artifacts{ + Packages: pkg.NewCollection(pkg.Package{ + Name: "github.com/wazuh/wazuh", + Version: "v4.5.0", + Type: pkg.GoModulePkg, + PURL: "pkg:golang/github.com/wazuh/wazuh@v4.5.0", + Language: pkg.Go, + }), + }, + }, + }, + { + name: "do not include namespace when given blank input blank", + userInput: "pkg:golang/wazuh@v4.5.0", + context: Context{ + Source: &source.Description{ + Metadata: PURLLiteralMetadata{PURL: "pkg:golang/wazuh@v4.5.0"}, + }, + }, + pkgs: []Package{ + { + Name: "wazuh", + Version: "v4.5.0", + Type: pkg.GoModulePkg, + PURL: "pkg:golang/wazuh@v4.5.0", + }, + }, + sbom: &sbom.SBOM{ + Artifacts: sbom.Artifacts{ + Packages: pkg.NewCollection(pkg.Package{ + Name: "wazuh", + Version: "v4.5.0", + Type: pkg.GoModulePkg, + PURL: "pkg:golang/wazuh@v4.5.0", + Language: pkg.Go, + }), + }, + }, + }, { name: "infer context when distro is present for multiple similar purls", userInput: "purl:test-fixtures/purl/homogeneous-os.txt", From 1e0dac23f39d1c6953ec93cdda6a029bb188dcd1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 2 May 2025 16:19:07 -0400 Subject: [PATCH 078/109] chore(deps): bump github/codeql-action from 3.28.16 to 3.28.17 (#2638) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.16 to 3.28.17. - [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/28deaeda66b76a05916b6923827895f2b14ab387...60168efe1c415ce0f5521ea06d5c2062adbeed1b) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.28.17 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 | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4f5b659c241..4d91cd336b7 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -63,7 +63,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 + uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -85,4 +85,4 @@ jobs: run: make grype - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@28deaeda66b76a05916b6923827895f2b14ab387 # v3.28.16 + uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index b607ac7687b..ed2e72967bd 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -38,6 +38,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@28deaeda66b76a05916b6923827895f2b14ab387 # v1.0.26 + uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v1.0.26 with: sarif_file: results.sarif From fa0bad8b47a9be17dd3903c7e9d58e31db19e4e3 Mon Sep 17 00:00:00 2001 From: Adam Chovanec Date: Fri, 2 May 2025 23:27:50 +0200 Subject: [PATCH 079/109] feat: determine OS/distro from purl (#2639) Signed-off-by: Adam Chovanec --- cmd/grype/cli/commands/root.go | 10 ++++--- grype/pkg/purl_provider.go | 51 ++++++++++++++++++++------------- grype/pkg/purl_provider_test.go | 11 +++++++ 3 files changed, 48 insertions(+), 24 deletions(-) diff --git a/cmd/grype/cli/commands/root.go b/cmd/grype/cli/commands/root.go index 67fb099ef7c..2a5efee0603 100644 --- a/cmd/grype/cli/commands/root.go +++ b/cmd/grype/cli/commands/root.go @@ -273,16 +273,18 @@ func applyDistroHint(pkgs []pkg.Package, context *pkg.Context, opts *options.Gry } } - hasOSPackage := false + hasOSPackageWithoutDistro := false for _, p := range pkgs { switch p.Type { case syftPkg.AlpmPkg, syftPkg.DebPkg, syftPkg.RpmPkg, syftPkg.KbPkg: - hasOSPackage = true + if p.Distro == nil { + hasOSPackageWithoutDistro = true + } } } - if context.Distro == nil && hasOSPackage { - log.Warnf("Unable to determine the OS distribution. This may result in missing vulnerabilities. " + + if context.Distro == nil && hasOSPackageWithoutDistro { + log.Warnf("Unable to determine the OS distribution of some packages. This may result in missing vulnerabilities. " + "You may specify a distro using: --distro :") } } diff --git a/grype/pkg/purl_provider.go b/grype/pkg/purl_provider.go index 56f830196f0..8fa83e21a04 100644 --- a/grype/pkg/purl_provider.go +++ b/grype/pkg/purl_provider.go @@ -10,6 +10,8 @@ import ( "github.com/scylladb/go-set/strset" "github.com/anchore/go-homedir" + "github.com/anchore/grype/grype/distro" + "github.com/anchore/grype/internal/log" "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/linux" @@ -80,6 +82,24 @@ func openPurlFile(path string) (*os.File, error) { return f, nil } +func createLinuxRelease(name string, version string) *linux.Release { + var codename string + + // if there are no digits in the version, it is likely a codename + if !strings.ContainsAny(version, "0123456789") { + codename = version + version = "" + } + + return &linux.Release{ + Name: name, + ID: name, + IDLike: []string{name}, + Version: version, + VersionCodename: codename, + } +} + func decodePurlsFromReader(reader io.Reader, ctx Context) ([]Package, Context, *sbom.SBOM, error) { scanner := bufio.NewScanner(reader) var packages []Package @@ -124,26 +144,8 @@ func decodePurlsFromReader(reader io.Reader, ctx Context) ([]Package, Context, * for name, versions := range distros { if versions.Size() == 1 { version := versions.List()[0] - var codename string - // if there are no digits in the version, it is likely a codename - if !strings.ContainsAny(version, "0123456789") { - codename = version - version = "" - } - ctx.Distro = &linux.Release{ - Name: name, - ID: name, - IDLike: []string{name}, - Version: version, - VersionCodename: codename, - } - s.Artifacts.LinuxDistribution = &linux.Release{ - Name: name, - ID: name, - IDLike: []string{name}, - Version: version, - VersionCodename: codename, - } + ctx.Distro = createLinuxRelease(name, version) + s.Artifacts.LinuxDistribution = createLinuxRelease(name, version) } } } @@ -151,6 +153,7 @@ func decodePurlsFromReader(reader io.Reader, ctx Context) ([]Package, Context, * return packages, ctx, s, nil } +//nolint:funlen func purlToPackage(rawLine string) (*Package, *pkg.Package, string, string, error) { purl, err := packageurl.FromString(rawLine) if err != nil { @@ -208,6 +211,13 @@ func purlToPackage(rawLine string) (*Package, *pkg.Package, string, string, erro } syftPkg.SetID() + + distribution, err := distro.NewFromRelease(*createLinuxRelease(distroName, distroVersion)) + if err != nil { + log.Trace("Unable to create Distro from a release: %s", err) + distribution = nil + } + return &Package{ ID: ID(purl.String()), CPEs: cpes, @@ -217,6 +227,7 @@ func purlToPackage(rawLine string) (*Package, *pkg.Package, string, string, erro Language: pkg.LanguageByName(purl.Type), PURL: purl.String(), Upstreams: upstreams, + Distro: distribution, }, &syftPkg, distroName, distroVersion, nil } diff --git a/grype/pkg/purl_provider_test.go b/grype/pkg/purl_provider_test.go index 10b605e2e17..a2dd50583dc 100644 --- a/grype/pkg/purl_provider_test.go +++ b/grype/pkg/purl_provider_test.go @@ -7,6 +7,7 @@ import ( "github.com/google/go-cmp/cmp/cmpopts" "github.com/stretchr/testify/require" + "github.com/anchore/grype/grype/distro" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" @@ -74,6 +75,7 @@ func Test_PurlProvider(t *testing.T) { Version: "2.88dsf-59", Type: pkg.DebPkg, PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit", + Distro: &distro.Distro{Type: distro.Debian, Version: "", Codename: "jessie", IDLike: []string{"debian"}}, Upstreams: []UpstreamPackage{ { Name: "sysvinit", @@ -189,6 +191,7 @@ func Test_PurlProvider(t *testing.T) { Version: "0:239-82.el8_10.2", Type: pkg.RpmPkg, PURL: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm", + Distro: &distro.Distro{Type: distro.RedHat, Version: "8.10", Codename: "", IDLike: []string{"rhel"}}, Upstreams: []UpstreamPackage{ { Name: "systemd", @@ -236,6 +239,7 @@ func Test_PurlProvider(t *testing.T) { Version: "1:1.12.8-26.el8", Type: pkg.RpmPkg, PURL: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm", + Distro: &distro.Distro{Type: distro.RedHat, Version: "8.10", Codename: "", IDLike: []string{"rhel"}}, Upstreams: []UpstreamPackage{ { Name: "dbus", @@ -283,6 +287,7 @@ func Test_PurlProvider(t *testing.T) { Version: "2.88dsf-59", Type: pkg.DebPkg, PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-8&upstream=sysvinit", + Distro: &distro.Distro{Type: distro.Debian, Version: "8", Codename: "", IDLike: []string{"debian"}}, Upstreams: []UpstreamPackage{ { Name: "sysvinit", @@ -356,6 +361,7 @@ func Test_PurlProvider(t *testing.T) { Version: "7.61.1", Type: pkg.ApkPkg, PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3", + Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, }, }, sbom: &sbom.SBOM{ @@ -481,12 +487,14 @@ func Test_PurlProvider(t *testing.T) { Version: "3.2.1", Type: pkg.ApkPkg, PURL: "pkg:apk/openssl@3.2.1?arch=aarch64&distro=alpine-3.20.3", + Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, }, { Name: "curl", Version: "7.61.1", Type: pkg.ApkPkg, PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3", + Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, }, }, sbom: &sbom.SBOM{ @@ -529,12 +537,14 @@ func Test_PurlProvider(t *testing.T) { Version: "3.2.1", Type: pkg.ApkPkg, PURL: "pkg:apk/openssl@3.2.1?arch=aarch64&distro=alpine-3.20.3", + Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, }, { Name: "curl", Version: "7.61.1", Type: pkg.ApkPkg, PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.2", + Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.2", Codename: "", IDLike: []string{"alpine"}}, }, }, sbom: &sbom.SBOM{ @@ -595,6 +605,7 @@ func Test_PurlProvider(t *testing.T) { opts := []cmp.Option{ cmpopts.IgnoreFields(Package{}, "ID", "Locations", "Licenses", "Metadata", "Language", "CPEs"), + cmpopts.IgnoreUnexported(distro.Distro{}), } syftPkgOpts := []cmp.Option{ From 0d54de168e665ca7df5afa167de8da00816a7250 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, 5 May 2025 11:37:55 -0400 Subject: [PATCH 080/109] chore(deps): update tools to latest versions (#2640) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.binny.yaml b/.binny.yaml index d153b15f878..d73cbb63c33 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -26,7 +26,7 @@ tools: # used for linting - name: golangci-lint version: - want: v2.1.5 + want: v2.1.6 method: github-release with: repo: golangci/golangci-lint From 9e8f298cfe671e4470e59df80eadc4a9dfbb84ef Mon Sep 17 00:00:00 2001 From: Firas AlShafei Date: Mon, 5 May 2025 11:40:28 -0500 Subject: [PATCH 081/109] feat(template): improve html template (#2635) - Add multi-severity filtering with interactive severity boxes - Implement Excel and PDF export with dynamic filenames and metadata - Add PURL, Related URLs, and package type icons to data display - Improve table responsiveness with dynamic height and inline row details - Add dark mode support and modern search interface - Update core dependencies and refactor JavaScript codebase Signed-off-by: Firas AlShafei --- templates/html.tmpl | 2140 +++++++++++++++++++++++++++++++------------ 1 file changed, 1545 insertions(+), 595 deletions(-) diff --git a/templates/html.tmpl b/templates/html.tmpl index 5fbab17f1f6..a550829375d 100644 --- a/templates/html.tmpl +++ b/templates/html.tmpl @@ -1,595 +1,1545 @@ - - - - - - Vulnerability Report - - - - - - - - - - - - - - - - - - - - - - - - - - - -{{/* Initialize counters */}} -{{- $CountCritical := 0 }} -{{- $CountHigh := 0 }} -{{- $CountMedium := 0 }} -{{- $CountLow := 0}} -{{- $CountUnknown := 0 }} - -{{/* Create a list */}} -{{- $FilteredMatches := list }} - -{{/* Loop through all vulns limit output and set count*/}} -{{- range $vuln := .Matches }} - {{/* Use this filter to exclude severity if needed */}} - {{- if or (eq $vuln.Vulnerability.Severity "Critical") (eq $vuln.Vulnerability.Severity "High") (eq $vuln.Vulnerability.Severity "Medium") (eq $vuln.Vulnerability.Severity "Low") (eq $vuln.Vulnerability.Severity "Unknown") }} - {{- $FilteredMatches = append $FilteredMatches $vuln }} - {{- if eq $vuln.Vulnerability.Severity "Critical" }} - {{- $CountCritical = add $CountCritical 1 }} - {{- else if eq $vuln.Vulnerability.Severity "High" }} - {{- $CountHigh = add $CountHigh 1 }} - {{- else if eq $vuln.Vulnerability.Severity "Medium" }} - {{- $CountMedium = add $CountMedium 1 }} - {{- else if eq $vuln.Vulnerability.Severity "Low" }} - {{- $CountLow = add $CountLow 1 }} - {{- else }} - {{- $CountUnknown = add $CountUnknown 1 }} - {{- end }} - {{- end }} -{{- end }} - - -
-
-
-

Container Vulnerability Report

-

Name: {{- if eq (.Source.Type) "image" -}} {{.Source.Target.UserInput}} - {{- else if eq (.Source.Type) "directory" -}} {{.Source.Target}} - {{- else if eq (.Source.Type) "file" -}} {{.Source.Target}} - {{- else -}} unknown - {{- end -}}

-

Type: {{ .Source.Type }}

-

Date: {{.Descriptor.Timestamp}}

-
-
- Grype Logo -
-
-
-
-
Critical
-
{{ $CountCritical }}
-
-
-
High
-
{{ $CountHigh }}
-
-
-
Medium
-
{{ $CountMedium }}
-
-
-
Low
-
{{ $CountLow }}
-
-
-
Unknown
-
{{ $CountUnknown }}
-
-
-
- - - - - - - - - - - - - - - {{- range $FilteredMatches }} - - - - - - - - - - - {{end}} - -
NameVersionTypeVulnerabilitySeverityDescriptionStateFixed In
{{.Artifact.Name}}{{.Artifact.Version}}{{.Artifact.Type}} - {{.Vulnerability.ID}} - {{.Vulnerability.Severity}}{{html .Vulnerability.Description}}{{.Vulnerability.Fix.State}} - {{- if .Vulnerability.Fix.Versions }} -
    - {{- range .Vulnerability.Fix.Versions }} -
  • {{ . }}
  • - {{- end }} -
- {{- else }} - N/A - {{- end }} -
-
-
- - - - - - - + + + + + + Vulnerability Report + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +{{/* Initialize counters */}} +{{- $CountCritical := 0 }} +{{- $CountHigh := 0 }} +{{- $CountMedium := 0 }} +{{- $CountLow := 0}} +{{- $CountUnknown := 0 }} + +{{/* Create a list */}} +{{- $FilteredMatches := list }} + +{{/* Loop through all vulns limit output and set count*/}} +{{- range $vuln := .Matches }} + {{/* Use this filter to exclude severity if needed */}} + {{- if or (eq $vuln.Vulnerability.Severity "Critical") (eq $vuln.Vulnerability.Severity "High") (eq $vuln.Vulnerability.Severity "Medium") (eq $vuln.Vulnerability.Severity "Low") (eq $vuln.Vulnerability.Severity "Unknown") }} + {{- $FilteredMatches = append $FilteredMatches $vuln }} + {{- if eq $vuln.Vulnerability.Severity "Critical" }} + {{- $CountCritical = add $CountCritical 1 }} + {{- else if eq $vuln.Vulnerability.Severity "High" }} + {{- $CountHigh = add $CountHigh 1 }} + {{- else if eq $vuln.Vulnerability.Severity "Medium" }} + {{- $CountMedium = add $CountMedium 1 }} + {{- else if eq $vuln.Vulnerability.Severity "Low" }} + {{- $CountLow = add $CountLow 1 }} + {{- else }} + {{- $CountUnknown = add $CountUnknown 1 }} + {{- end }} + {{- end }} +{{- end }} + + +
+
+
+

Vulnerability Report

+
+
Name:
+
{{- if eq (.Source.Type) "image" -}} {{.Source.Target.UserInput}} + {{- else if eq (.Source.Type) "directory" -}} {{.Source.Target}} + {{- else if eq (.Source.Type) "file" -}} {{.Source.Target}} + {{- else -}} unknown + {{- end -}}
+ +
Type:
+
{{ .Source.Type }}
+ + {{- /* Conditionally add ImageID (Checksum) for images */ -}} + {{- if eq .Source.Type "image" -}} + {{- with .Source.Target.ID -}} +
Checksum:
+
{{ . }}
+ {{- end -}} + {{- end -}} + +
Date:
+
+ {{.Descriptor.Timestamp}} + +
+
+
+
+ Grype Logo +
+
+
+
+
Critical
+
{{ $CountCritical }}
+
+
+
High
+
{{ $CountHigh }}
+
+
+
Medium
+
{{ $CountMedium }}
+
+
+
Low
+
{{ $CountLow }}
+
+
+
Unknown
+
{{ $CountUnknown }}
+
+
+
+ + + + + + + + + + + + + + + + + {{- range $FilteredMatches }} + + + + + + + + + + + + + {{- end }} + +
NameVersionTypeVulnerabilitySeverityStateFixed InDescriptionRelated URLsPURL
{{.Artifact.Name}}{{.Artifact.Version}}{{.Artifact.Type}} + {{.Vulnerability.ID}} + {{.Vulnerability.Severity}}{{.Vulnerability.Fix.State}} + {{- if .Vulnerability.Fix.Versions }} +
    + {{- range .Vulnerability.Fix.Versions }} +
  • {{ . }}
  • + {{- end }} +
+ {{- else }} + N/A + {{- end }} +
{{html .Vulnerability.Description}}{{ toJson .Vulnerability.URLs }}{{ .Artifact.PURL }}
+
+
+ + + + + \ No newline at end of file From 2aedfb5349c6705f507255d5ada4057f3bcabcb6 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 5 May 2025 13:05:47 -0400 Subject: [PATCH 082/109] Add risk score to results and sort by risk by default (#2587) * add risk score + sorting Signed-off-by: Alex Goodman * add documentation around risk and sorting Signed-off-by: Alex Goodman * address review comments Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- README.md | 369 ++++++++----- cmd/grype/cli/cli.go | 28 + cmd/grype/cli/commands/root.go | 2 +- cmd/grype/cli/options/grype.go | 2 + cmd/grype/cli/options/sort_by.go | 47 ++ go.mod | 4 +- go.sum | 8 +- .../snapshot/TestCycloneDxPresenterDir.golden | 40 +- .../TestCycloneDxPresenterImage.golden | 38 +- grype/presenter/internal/test_helpers.go | 126 ++++- .../snapshot/TestJsonDirsPresenter.golden | 55 +- .../snapshot/TestJsonImgsPresenter.golden | 55 +- grype/presenter/models/sort.go | 165 +++++- grype/presenter/models/sort_test.go | 505 ++++++++++++++---- grype/presenter/models/vulnerability.go | 2 + .../models/vulnerability_metadata.go | 11 +- grype/presenter/sarif/presenter_test.go | 7 +- .../TestSarifPresenter_directory.golden | 16 +- .../snapshot/TestSarifPresenter_image.golden | 16 +- .../table/__snapshots__/presenter_test.snap | 48 +- grype/presenter/table/presenter.go | 226 ++++++-- grype/presenter/table/presenter_test.go | 89 ++- grype/vulnerability/metadata.go | 106 ++++ grype/vulnerability/metadata_test.go | 457 ++++++++++++++++ 24 files changed, 1948 insertions(+), 474 deletions(-) create mode 100644 cmd/grype/cli/options/sort_by.go create mode 100644 grype/vulnerability/metadata_test.go diff --git a/README.md b/README.md index 03c9093835e..4a6453e191b 100644 --- a/README.md +++ b/README.md @@ -153,7 +153,7 @@ docker run --rm \ $(ImageName):$(ImageTag) ``` -### Supported sources +## Supported sources Grype can scan a variety of sources beyond those found in Docker. @@ -210,6 +210,49 @@ use the `--distro :` flag. A full example is: grype --add-cpes-if-none --distro alpine:3.10 sbom:some-alpine-3.10.spdx.json ``` +## Threat & Risk Prioritization + +This section explains the columns and UI cues that help prioritize remediation efforts: + +- **Severity**: String severity based on CVSS scores and indicate the significance of a vulnerability in levels. + This balances concerns such as ease of exploitability, and the potential to affect + confidentiality, integrity, and availability of software and services. + +- **EPSS**: + [Exploit Prediction Scoring System](https://www.first.org/epss/model) is a metric expressing the likelihood + that a vulnerability will be + exploited in the wild over the next 30 days (on a 0–1 scale); higher values signal a greater likelihood of + exploitation. + The table output shows the EPSS percentile, a one-way transform of the EPSS score showing the + proportion of all scored vulnerabilities with an equal or lower probability. + Percentiles linearize a heavily skewed distribution, making threshold choice (e.g. “only CVEs above the + 90th percentile”) straightforward. + +- **KEV Indicator**: Flags entries from CISA’s [Known Exploited Vulnerabilities Catalog](https://www.cisa.gov/known-exploited-vulnerabilities-catalog) + --an authoritative list of flaws observed being exploited in the wild. + +- **Risk Score**: A composite 0–100 metric calculated as: + ```markdown + risk = min(1, threat * average(severity)) * 100 + ``` + Where: + - `severity` is the average of all CVSS scores and string severity for a vulnerability (scaled between 0–1). + - `threat` is the EPSS score (between 0–1). If the vulnerability is on the KEV list then `threat` is + `1.05`, or `1.1` if the vulnerability is associated with a ransomware campaign. + This metric is one way to combine EPSS and CVSS suggested in the [EPSS user guide](https://www.first.org/epss/user-guide). + +- **Suggested Fixes**: All possible fixes for a package are listed, however, when multiple fixes are available, we de-emphasize all + upgrade paths except for the minimal upgrade path (which highlights the smallest, safest version bump). + +Results default to sorting by Risk Score and can be overridden with `--sort-by `: + +- `severity`: sort by severity +- `epss`: sort by EPSS percentile (aka, "threat") +- `risk`: sort by risk score +- `kev`: just like risk, except that KEV entries are always above non-KEV entries +- `package`: sort by package name, version, type +- `vulnerability`: sort by vulnerability ID + ### Supported versions Software updates are always applied to the latest version of Grype; fixes are not backported to any previous versions of Grype. @@ -699,192 +742,240 @@ GRYPE_CONFIG=/path/to/config.yaml grype Configuration options (example values are the default): ```yaml -# enable/disable checking for application updates on startup -# same as GRYPE_CHECK_FOR_APP_UPDATE env var -check-for-app-update: true - -# allows users to specify which image source should be used to generate the sbom -# valid values are: registry, docker, podman -# same as GRYPE_DEFAULT_IMAGE_PULL_SOURCE env var -default-image-pull-source: "" - -# same as --name; set the name of the target being analyzed -name: "" - -# upon scanning, if a severity is found at or above the given severity then the return code will be 2 -# default is unset which will skip this validation (options: negligible, low, medium, high, critical) -# same as --fail-on ; GRYPE_FAIL_ON_SEVERITY env var -fail-on-severity: "" - # the output format of the vulnerability report (options: table, template, json, cyclonedx) -# when using template as the output type, you must also provide a value for 'output-template-file' -# same as -o ; GRYPE_OUTPUT env var -output: "table" +# when using template as the output type, you must also provide a value for 'output-template-file' (env: GRYPE_OUTPUT) +output: 'table' # if using template output, you must provide a path to a Go template file # see https://github.com/anchore/grype#using-templates for more information on template output # the default path to the template file is the current working directory # output-template-file: .grype/html.tmpl +# +# write output report to a file (default is to write to stdout) (env: GRYPE_FILE) +file: '' -# write output report to a file (default is to write to stdout) -# same as --file; GRYPE_FILE env var -file: "" +# pretty-print JSON output (env: GRYPE_PRETTY) +pretty: false -# a list of globs to exclude from scanning, for example: -# exclude: -# - '/etc/**' -# - './out/**/*.json' -# same as --exclude ; GRYPE_EXCLUDE env var -exclude: [] +# distro to match against in the format: : (env: GRYPE_DISTRO) +distro: '' -# include matches on kernel-headers packages that are matched against upstream kernel package -# if 'false' any such matches are marked as ignored -match-upstream-kernel-headers: false +# generate CPEs for packages with no CPE data (env: GRYPE_ADD_CPES_IF_NONE) +add-cpes-if-none: false -# os and/or architecture to use when referencing container images (e.g. "windows/armv6" or "arm64") -# same as --platform; GRYPE_PLATFORM env var -platform: "" +# specify the path to a Go template file (requires 'template' output to be selected) (env: GRYPE_OUTPUT_TEMPLATE_FILE) +output-template-file: '' -# If using SBOM input, automatically generate CPEs when packages have none -add-cpes-if-none: false +# enable/disable checking for application updates on startup (env: GRYPE_CHECK_FOR_APP_UPDATE) +check-for-app-update: true -# Explicitly specify a linux distribution to use as : like alpine:3.10 -distro: +# ignore matches for vulnerabilities that are not fixed (env: GRYPE_ONLY_FIXED) +only-fixed: false -external-sources: - enable: false - maven: - search-upstream-by-sha1: true - base-url: https://search.maven.org/solrsearch/select - rate-limit: 300ms +# ignore matches for vulnerabilities that are fixed (env: GRYPE_ONLY_NOTFIXED) +only-notfixed: false -db: - # check for database updates on execution - # same as GRYPE_DB_AUTO_UPDATE env var - auto-update: true +# ignore matches for vulnerabilities with specified comma separated fix states, options=[fixed not-fixed unknown wont-fix] (env: GRYPE_IGNORE_WONTFIX) +ignore-wontfix: '' - # location to write the vulnerability database cache; defaults to $XDG_CACHE_HOME/grype/db - # same as GRYPE_DB_CACHE_DIR env var - cache-dir: "" +# an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux') (env: GRYPE_PLATFORM) +platform: '' - # URL of the vulnerability database - # same as GRYPE_DB_UPDATE_URL env var - update-url: "https://grype.anchore.io/databases" +# upon scanning, if a severity is found at or above the given severity then the return code will be 1 +# default is unset which will skip this validation (options: negligible, low, medium, high, critical) (env: GRYPE_FAIL_ON_SEVERITY) +fail-on-severity: '' - # it ensures db build is no older than the max-allowed-built-age - # set to false to disable check - validate-age: true +# show suppressed/ignored vulnerabilities in the output (only supported with table output format) (env: GRYPE_SHOW_SUPPRESSED) +show-suppressed: false - # Max allowed age for vulnerability database, - # age being the time since it was built - # Default max age is 120h (or five days) - max-allowed-built-age: "120h" +# orient results by CVE instead of the original vulnerability ID when possible (env: GRYPE_BY_CVE) +by-cve: false - # Timeout for downloading GRYPE_DB_UPDATE_URL to see if the database needs to be downloaded - # This file is ~156KiB as of 2024-04-17 so the download should be quick; adjust as needed - update-available-timeout: "30s" +# sort the match results with the given strategy, options=[package severity epss risk kev vulnerability] (env: GRYPE_SORT_BY) +sort-by: 'risk' - # Timeout for downloading actual vulnerability DB - # The DB is ~156MB as of 2024-04-17 so slower connections may exceed the default timeout; adjust as needed - update-download-timeout: "120s" +# same as --name; set the name of the target being analyzed (env: GRYPE_NAME) +name: '' -search: - # the search space to look for packages (options: all-layers, squashed) - # same as -s ; GRYPE_SEARCH_SCOPE env var - scope: "squashed" +# allows users to specify which image source should be used to generate the sbom +# valid values are: registry, docker, podman (env: GRYPE_DEFAULT_IMAGE_PULL_SOURCE) +default-image-pull-source: '' - # search within archives that do contain a file index to search against (zip) - # note: for now this only applies to the java package cataloger - # same as GRYPE_PACKAGE_SEARCH_INDEXED_ARCHIVES env var - indexed-archives: true +search: + # selection of layers to analyze, options=[squashed all-layers] (env: GRYPE_SEARCH_SCOPE) + scope: 'squashed' # search within archives that do not contain a file index to search against (tar, tar.gz, tar.bz2, etc) # note: enabling this may result in a performance impact since all discovered compressed tars will be decompressed - # note: for now this only applies to the java package cataloger - # same as GRYPE_PACKAGE_SEARCH_UNINDEXED_ARCHIVES env var + # note: for now this only applies to the java package cataloger (env: GRYPE_SEARCH_UNINDEXED_ARCHIVES) unindexed-archives: false -# options when pulling directly from a registry via the "registry:" scheme -registry: - # skip TLS verification when communicating with the registry - # same as GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY env var - insecure-skip-tls-verify: false - - # use http instead of https when connecting to the registry - # same as GRYPE_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 - # GRYPE_REGISTRY_CA_CERT env var - ca-cert: "" + # search within archives that do contain a file index to search against (zip) + # note: for now this only applies to the java package cataloger (env: GRYPE_SEARCH_INDEXED_ARCHIVES) + indexed-archives: true - # credentials for specific registries - auth: - # the URL to the registry (e.g. "docker.io", "localhost:5000", etc.) - # GRYPE_REGISTRY_AUTH_AUTHORITY env var - - authority: "" +# A list of vulnerability ignore rules, one or more property may be specified and all matching vulnerabilities will be ignored. +# This is the full set of supported rule fields: +# - vulnerability: CVE-2008-4318 +# fix-state: unknown +# package: +# name: libcurl +# version: 1.5.1 +# type: npm +# location: "/usr/local/lib/node_modules/**" +# +# VEX fields apply when Grype reads vex data: +# - vex-status: not_affected +# vex-justification: vulnerable_code_not_present +ignore: [] - # GRYPE_REGISTRY_AUTH_USERNAME env var - username: "" +# a list of globs to exclude from scanning, for example: +# - '/etc/**' +# - './out/**/*.json' +# same as --exclude (env: GRYPE_EXCLUDE) +exclude: [] - # GRYPE_REGISTRY_AUTH_PASSWORD env var - password: "" +external-sources: + # enable Grype searching network source for additional information (env: GRYPE_EXTERNAL_SOURCES_ENABLE) + enable: false - # note: token and username/password are mutually exclusive - # GRYPE_REGISTRY_AUTH_TOKEN env var - token: "" + maven: + # search for Maven artifacts by SHA1 (env: GRYPE_EXTERNAL_SOURCES_MAVEN_SEARCH_MAVEN_UPSTREAM) + search-maven-upstream: true - # filepath to the client certificate used for TLS authentication to the registry - # GRYPE_REGISTRY_AUTH_TLS_CERT env var - tls-cert: "" + # base URL of the Maven repository to search (env: GRYPE_EXTERNAL_SOURCES_MAVEN_BASE_URL) + base-url: 'https://search.maven.org/solrsearch/select' - # filepath to the client key used for TLS authentication to the registry - # GRYPE_REGISTRY_AUTH_TLS_KEY env var - tls-key: "" + # (env: GRYPE_EXTERNAL_SOURCES_MAVEN_RATE_LIMIT) + rate-limit: 300ms - # - ... # note, more credentials can be provided via config file only (not env vars) +match: + java: + # use CPE matching to find vulnerabilities (env: GRYPE_MATCH_JAVA_USING_CPES) + using-cpes: false + jvm: + # (env: GRYPE_MATCH_JVM_USING_CPES) + using-cpes: true -log: - # suppress all output (except for the vulnerability list) - # same as -q ; GRYPE_LOG_QUIET env var - quiet: false + dotnet: + # use CPE matching to find vulnerabilities (env: GRYPE_MATCH_DOTNET_USING_CPES) + using-cpes: false - # increase verbosity - # same as GRYPE_LOG_VERBOSITY env var - verbosity: 0 + golang: + # use CPE matching to find vulnerabilities (env: GRYPE_MATCH_GOLANG_USING_CPES) + using-cpes: false - # the log level; note: detailed logging suppress the ETUI - # same as GRYPE_LOG_LEVEL env var - # Uses logrus logging levels: https://github.com/sirupsen/logrus#level-logging - level: "error" + # use CPE matching to find vulnerabilities for the Go standard library (env: GRYPE_MATCH_GOLANG_ALWAYS_USE_CPE_FOR_STDLIB) + always-use-cpe-for-stdlib: true - # location to write the log file (default is not to have a log file) - # same as GRYPE_LOG_FILE env var - file: "" + # allow comparison between main module pseudo-versions (e.g. v0.0.0-20240413-2b432cf643...) (env: GRYPE_MATCH_GOLANG_ALLOW_MAIN_MODULE_PSEUDO_VERSION_COMPARISON) + allow-main-module-pseudo-version-comparison: false -match: - # sets the matchers below to use cpes when trying to find - # vulnerability matches. The stock matcher is the default - # when no primary matcher can be identified. - java: + javascript: + # use CPE matching to find vulnerabilities (env: GRYPE_MATCH_JAVASCRIPT_USING_CPES) using-cpes: false + python: + # use CPE matching to find vulnerabilities (env: GRYPE_MATCH_PYTHON_USING_CPES) using-cpes: false - javascript: - using-cpes: false + ruby: + # use CPE matching to find vulnerabilities (env: GRYPE_MATCH_RUBY_USING_CPES) using-cpes: false - dotnet: - using-cpes: false - golang: + + rust: + # use CPE matching to find vulnerabilities (env: GRYPE_MATCH_RUST_USING_CPES) using-cpes: false - # even if CPE matching is disabled, make an exception when scanning for "stdlib". - always-use-cpe-for-stdlib: true - # allow main module pseudo versions, which may have only been "guessed at" by Syft, to be used in vulnerability matching - allow-main-module-pseudo-version-comparison: false + stock: + # use CPE matching to find vulnerabilities (env: GRYPE_MATCH_STOCK_USING_CPES) using-cpes: true + + +registry: + # skip TLS verification when communicating with the registry (env: GRYPE_REGISTRY_INSECURE_SKIP_TLS_VERIFY) + insecure-skip-tls-verify: false + + # use http instead of https when connecting to the registry (env: GRYPE_REGISTRY_INSECURE_USE_HTTP) + insecure-use-http: false + + # Authentication credentials for specific registries. Each entry describes authentication for a specific authority: + # - authority: the registry authority URL the URL to the registry (e.g. "docker.io", "localhost:5000", etc.) (env: SYFT_REGISTRY_AUTH_AUTHORITY) + # username: a username if using basic credentials (env: SYFT_REGISTRY_AUTH_USERNAME) + # password: a corresponding password (env: SYFT_REGISTRY_AUTH_PASSWORD) + # token: a token if using token-based authentication, mutually exclusive with username/password (env: SYFT_REGISTRY_AUTH_TOKEN) + # tls-cert: filepath to the client certificate used for TLS authentication to the registry (env: SYFT_REGISTRY_AUTH_TLS_CERT) + # tls-key: filepath to the client key used for TLS authentication to the registry (env: SYFT_REGISTRY_AUTH_TLS_KEY) + auth: [] + + # filepath to a CA certificate (or directory containing *.crt, *.cert, *.pem) used to generate the client certificate (env: GRYPE_REGISTRY_CA_CERT) + ca-cert: '' + +# a list of VEX documents to consider when producing scanning results (env: GRYPE_VEX_DOCUMENTS) +vex-documents: [] + +# VEX statuses to consider as ignored rules (env: GRYPE_VEX_ADD) +vex-add: [] + +# match kernel-header packages with upstream kernel as kernel vulnerabilities (env: GRYPE_MATCH_UPSTREAM_KERNEL_HEADERS) +match-upstream-kernel-headers: false + +db: + # location to write the vulnerability database cache (env: GRYPE_DB_CACHE_DIR) + cache-dir: '~/Library/Caches/grype/db' + + # URL of the vulnerability database (env: GRYPE_DB_UPDATE_URL) + update-url: 'https://grype.anchore.io/databases' + + # certificate to trust download the database and listing file (env: GRYPE_DB_CA_CERT) + ca-cert: '' + + # check for database updates on execution (env: GRYPE_DB_AUTO_UPDATE) + auto-update: true + + # validate the database matches the known hash each execution (env: GRYPE_DB_VALIDATE_BY_HASH_ON_START) + validate-by-hash-on-start: true + + # ensure db build is no older than the max-allowed-built-age (env: GRYPE_DB_VALIDATE_AGE) + validate-age: true + + # Max allowed age for vulnerability database, + # age being the time since it was built + # Default max age is 120h (or five days) (env: GRYPE_DB_MAX_ALLOWED_BUILT_AGE) + max-allowed-built-age: 120h0m0s + + # fail the scan if unable to check for database updates (env: GRYPE_DB_REQUIRE_UPDATE_CHECK) + require-update-check: false + + # Timeout for downloading GRYPE_DB_UPDATE_URL to see if the database needs to be downloaded + # This file is ~156KiB as of 2024-04-17 so the download should be quick; adjust as needed (env: GRYPE_DB_UPDATE_AVAILABLE_TIMEOUT) + update-available-timeout: 30s + + # Timeout for downloading actual vulnerability DB + # The DB is ~156MB as of 2024-04-17 so slower connections may exceed the default timeout; adjust as needed (env: GRYPE_DB_UPDATE_DOWNLOAD_TIMEOUT) + update-download-timeout: 5m0s + + # Maximum frequency to check for vulnerability database updates (env: GRYPE_DB_MAX_UPDATE_CHECK_FREQUENCY) + max-update-check-frequency: 2h0m0s + +log: + # suppress all logging output (env: GRYPE_LOG_QUIET) + quiet: false + + # explicitly set the logging level (available: [error warn info debug trace]) (env: GRYPE_LOG_LEVEL) + level: 'warn' + + # file path to write logs to (env: GRYPE_LOG_FILE) + file: '' + +dev: + # capture resource profiling data (available: [cpu, mem]) (env: GRYPE_DEV_PROFILE) + profile: '' + + db: + # show sql queries in trace logging (requires -vv) (env: GRYPE_DEV_DB_DEBUG) + debug: false ``` ## Future plans diff --git a/cmd/grype/cli/cli.go b/cmd/grype/cli/cli.go index 164eb94107b..76ddf5a54e3 100644 --- a/cmd/grype/cli/cli.go +++ b/cmd/grype/cli/cli.go @@ -4,7 +4,10 @@ import ( "errors" "os" "runtime/debug" + "strings" + "github.com/charmbracelet/lipgloss" + "github.com/muesli/termenv" "github.com/spf13/cobra" "github.com/anchore/clio" @@ -39,6 +42,10 @@ func create(id clio.Identification) (clio.Application, *cobra.Command) { WithUIConstructor( // select a UI based on the logging configuration and state of stdin (if stdin is a tty) func(cfg clio.Config) (*clio.UICollection, error) { + // remove CI var from consideration when determining if we should use the UI + lipgloss.SetDefaultRenderer(lipgloss.NewRenderer(os.Stdout, termenv.WithEnvironment(environWithoutCI{}))) + + // setup the UIs noUI := ui.None(cfg.Log.Quiet) if !cfg.Log.AllowUI(os.Stdin) || cfg.Log.Quiet { return clio.NewUICollection(noUI), nil @@ -122,3 +129,24 @@ func syftVersion() (string, any) { func dbVersion() (string, any) { return "Supported DB Schema", v6.ModelVersion } + +type environWithoutCI struct { +} + +func (e environWithoutCI) Environ() []string { + var out []string + for _, s := range os.Environ() { + if strings.HasPrefix(s, "CI=") { + continue + } + out = append(out, s) + } + return out +} + +func (e environWithoutCI) Getenv(s string) string { + if s == "CI" { + return "" + } + return os.Getenv(s) +} diff --git a/cmd/grype/cli/commands/root.go b/cmd/grype/cli/commands/root.go index 2a5efee0603..b4b8dd8f2fb 100644 --- a/cmd/grype/cli/commands/root.go +++ b/cmd/grype/cli/commands/root.go @@ -209,7 +209,7 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs log.WithFields("time", time.Since(startTime)).Info("found vulnerability matches") startTime = time.Now() - model, err := models.NewDocument(app.ID(), packages, pkgContext, *remainingMatches, ignoredMatches, vp, opts, dbInfo(status, vp), models.SortByPackage) + model, err := models.NewDocument(app.ID(), packages, pkgContext, *remainingMatches, ignoredMatches, vp, opts, dbInfo(status, vp), models.SortStrategy(opts.SortBy.Criteria)) if err != nil { return fmt.Errorf("failed to create document: %w", err) } diff --git a/cmd/grype/cli/options/grype.go b/cmd/grype/cli/options/grype.go index 4bd5b3f6e7d..0dab835425b 100644 --- a/cmd/grype/cli/options/grype.go +++ b/cmd/grype/cli/options/grype.go @@ -31,6 +31,7 @@ type Grype struct { Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"` ShowSuppressed bool `yaml:"show-suppressed" json:"show-suppressed" mapstructure:"show-suppressed"` ByCVE bool `yaml:"by-cve" json:"by-cve" mapstructure:"by-cve"` // --by-cve, indicates if the original match vulnerability IDs should be preserved or the CVE should be used instead + SortBy SortBy `yaml:",inline" json:",inline" mapstructure:",squash"` Name string `yaml:"name" json:"name" mapstructure:"name"` DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` VexDocuments []string `yaml:"vex-documents" json:"vex-documents" mapstructure:"vex-documents"` @@ -64,6 +65,7 @@ func DefaultGrype(id clio.Identification) *Grype { CheckForAppUpdate: true, VexAdd: []string{}, MatchUpstreamKernelHeaders: false, + SortBy: defaultSortBy(), } } diff --git a/cmd/grype/cli/options/sort_by.go b/cmd/grype/cli/options/sort_by.go new file mode 100644 index 00000000000..8a7a6121880 --- /dev/null +++ b/cmd/grype/cli/options/sort_by.go @@ -0,0 +1,47 @@ +package options + +import ( + "fmt" + "strings" + + "github.com/scylladb/go-set/strset" + + "github.com/anchore/clio" + "github.com/anchore/fangs" + "github.com/anchore/grype/grype/presenter/models" +) + +var _ interface { + fangs.FlagAdder + fangs.PostLoader +} = (*SortBy)(nil) + +type SortBy struct { + Criteria string `yaml:"sort-by" json:"sort-by" mapstructure:"sort-by"` + AllowableOptions []string `yaml:"-" json:"-" mapstructure:"-"` +} + +func defaultSortBy() SortBy { + var strategies []string + for _, s := range models.SortStrategies() { + strategies = append(strategies, strings.ToLower(s.String())) + } + return SortBy{ + Criteria: models.DefaultSortStrategy.String(), + AllowableOptions: strategies, + } +} + +func (o *SortBy) AddFlags(flags clio.FlagSet) { + flags.StringVarP(&o.Criteria, + "sort-by", "", + fmt.Sprintf("sort the match results with the given strategy, options=%v", o.AllowableOptions), + ) +} + +func (o *SortBy) PostLoad() error { + if !strset.New(o.AllowableOptions...).Has(strings.ToLower(o.Criteria)) { + return fmt.Errorf("invalid sort-by criteria: %q (allowable: %s)", o.Criteria, strings.Join(o.AllowableOptions, ", ")) + } + return nil +} diff --git a/go.mod b/go.mod index 8960afe8b09..726ad9277cb 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,8 @@ require ( github.com/adrg/xdg v0.5.3 github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 - github.com/anchore/clio v0.0.0-20250401141128-4c1d6bd1e872 + github.com/anchore/clio v0.0.0-20250408180537-ec8fa27f0d9f + github.com/anchore/fangs v0.0.0-20250402135612-96e29e45f3fe github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 @@ -97,7 +98,6 @@ require ( github.com/STARRY-S/zip v0.2.1 // indirect github.com/acobaugh/osrelease v0.1.0 // indirect github.com/agext/levenshtein v1.2.1 // indirect - github.com/anchore/fangs v0.0.0-20250326231402-da263204d38e // indirect github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6 // indirect diff --git a/go.sum b/go.sum index f6ec232fb46..ef65d2a33f1 100644 --- a/go.sum +++ b/go.sum @@ -686,10 +686,10 @@ github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 h1:yhk+P8lF3 github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51/go.mod h1:nwuGSd7aZp0rtYt79YggCGafz1RYsclE7pi3fhLwvuw= github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 h1:p0ZIe0htYOX284Y4axJaGBvXHU0VCCzLN5Wf5XbKStU= github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9/go.mod h1:3ZsFB9tzW3vl4gEiUeuSOMDnwroWxIxJelOOHUp8dSw= -github.com/anchore/clio v0.0.0-20250401141128-4c1d6bd1e872 h1:iEF0xhHUuh3J8FrlPsZAQVaMpTa2j4lvLRI5XrXzge4= -github.com/anchore/clio v0.0.0-20250401141128-4c1d6bd1e872/go.mod h1:Utb9i4kwiCWvqAIxZaJeMIXFO9uOgQXlvH2BfbfO/zI= -github.com/anchore/fangs v0.0.0-20250326231402-da263204d38e h1:9hXsNmfBqo2exA4a90Qw33Edb+OROVmeibe9RzgS1wA= -github.com/anchore/fangs v0.0.0-20250326231402-da263204d38e/go.mod h1:vrcYMDps9YXwwx2a9AsvipM6Fi5H9//9bymGb8G8BIQ= +github.com/anchore/clio v0.0.0-20250408180537-ec8fa27f0d9f h1:jTeN+fKTXz1VFo3Zj7Msnx//s5kD6Htd+SS0z9/o7Ss= +github.com/anchore/clio v0.0.0-20250408180537-ec8fa27f0d9f/go.mod h1:jQ+jv7v9RQnc5oA+Z0rAyXsQfaCAZHwY/CJZiLVggQ4= +github.com/anchore/fangs v0.0.0-20250402135612-96e29e45f3fe h1:qv/xxpjF5RdKPqZjx8RM0aBi3HUCAO0DhRBMs2xhY1I= +github.com/anchore/fangs v0.0.0-20250402135612-96e29e45f3fe/go.mod h1:vrcYMDps9YXwwx2a9AsvipM6Fi5H9//9bymGb8G8BIQ= github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 h1:GjNGuwK5jWjJMyVppBjYS54eOiiSNv4Ba869k4wh72Q= github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8= github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d h1:gT69osH9AsdpOfqxbRwtxcNnSZ1zg4aKy2BevO3ZBdc= diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden index 4c8e813faf5..354a1daec75 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.6", - "serialNumber": "urn:uuid:d78e14f4-ade8-4948-991c-73318b661114", + "serialNumber": "urn:uuid:8a92c7a6-641a-4fe8-8cb7-1636323af3d9", "version": 1, "metadata": { - "timestamp": "2025-02-27T13:15:13-05:00", + "timestamp": "2025-04-09T12:16:17-04:00", "tools": { "components": [ { @@ -19,7 +19,7 @@ "component": { "bom-ref": "163686ac6e30c752", "type": "file", - "name": "/var/folders/c0/4y79v5k56bz8v34chcmvq2k80000gp/T/TestCycloneDxPresenterDir3112352663/001" + "name": "/var/folders/c0/4y79v5k56bz8v34chcmvq2k80000gp/T/TestCycloneDxPresenterDir3422104391/001" } }, "components": [ @@ -89,28 +89,23 @@ ], "vulnerabilities": [ { - "bom-ref": "urn:uuid:e47db483-7ab2-4177-a82b-416b5a37bf93", + "bom-ref": "urn:uuid:9179966d-ddc9-445e-83f4-a8fdfd6c4ef3", "id": "CVE-1999-0001", - "source": { - "name": "source-1" - }, + "source": {}, "references": [ { "id": "CVE-1999-0001", - "source": { - "name": "source-1" - } + "source": {} } ], "ratings": [ { - "score": 4, + "score": 8.2, "severity": "low", - "method": "CVSSv3", - "vector": "another vector" + "method": "CVSSv31", + "vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:H" } ], - "description": "1999-01 description", "affects": [ { "ref": "9baa2db122fea516" @@ -118,28 +113,23 @@ ] }, { - "bom-ref": "urn:uuid:3e3fbcf0-8dda-41a7-97ed-d1122923bf79", + "bom-ref": "urn:uuid:fe57d7f2-08c1-4446-8424-0f9d2c1e2b13", "id": "CVE-1999-0002", - "source": { - "name": "source-2" - }, + "source": {}, "references": [ { "id": "CVE-1999-0002", - "source": { - "name": "source-2" - } + "source": {} } ], "ratings": [ { - "score": 1, + "score": 8.5, "severity": "critical", - "method": "CVSSv2", - "vector": "vector" + "method": "CVSSv31", + "vector": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H" } ], - "description": "1999-02 description", "affects": [ { "ref": "pkg:deb/package-2@2.2.2?package-id=7bb53d560434bc7f" diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden index f5676aedbb4..461383cb3a7 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.6", - "serialNumber": "urn:uuid:24d4fb73-8881-462c-940a-61b6d37b579d", + "serialNumber": "urn:uuid:04f27880-9830-4cfa-b9a5-9e8b9b4fee04", "version": 1, "metadata": { - "timestamp": "2025-02-27T13:15:13-05:00", + "timestamp": "2025-04-09T12:16:17-04:00", "tools": { "components": [ { @@ -90,28 +90,23 @@ ], "vulnerabilities": [ { - "bom-ref": "urn:uuid:638bc45b-a838-4017-b690-dc5d8d3cfe8d", + "bom-ref": "urn:uuid:0f1e7161-0591-4f58-928e-184e8c8184c0", "id": "CVE-1999-0001", - "source": { - "name": "source-1" - }, + "source": {}, "references": [ { "id": "CVE-1999-0001", - "source": { - "name": "source-1" - } + "source": {} } ], "ratings": [ { - "score": 4, + "score": 8.2, "severity": "low", - "method": "CVSSv3", - "vector": "another vector" + "method": "CVSSv31", + "vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:H" } ], - "description": "1999-01 description", "affects": [ { "ref": "9baa2db122fea516" @@ -119,28 +114,23 @@ ] }, { - "bom-ref": "urn:uuid:6592d365-1128-49a0-8a68-570d8188965a", + "bom-ref": "urn:uuid:757bf3c1-b03d-482b-b1a1-8b7a3fd5346f", "id": "CVE-1999-0002", - "source": { - "name": "source-2" - }, + "source": {}, "references": [ { "id": "CVE-1999-0002", - "source": { - "name": "source-2" - } + "source": {} } ], "ratings": [ { - "score": 1, + "score": 8.5, "severity": "critical", - "method": "CVSSv2", - "vector": "vector" + "method": "CVSSv31", + "vector": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H" } ], - "description": "1999-02 description", "affects": [ { "ref": "pkg:deb/package-2@2.2.2?package-id=7bb53d560434bc7f" diff --git a/grype/presenter/internal/test_helpers.go b/grype/presenter/internal/test_helpers.go index 8c3e8fb12cc..aab61adc688 100644 --- a/grype/presenter/internal/test_helpers.go +++ b/grype/presenter/internal/test_helpers.go @@ -99,7 +99,7 @@ func Redact(s []byte) []byte { return s } -func generateMatches(t *testing.T, p1, p2 pkg.Package) match.Matches { +func generateMatches(t *testing.T, p1, p2 pkg.Package) match.Matches { // nolint:funlen t.Helper() matches := []match.Match{ @@ -114,6 +114,29 @@ func generateMatches(t *testing.T, p1, p2 pkg.Package) match.Matches { Versions: []string{"1.2.1", "2.1.3", "3.4.0"}, State: vulnerability.FixStateFixed, }, + Metadata: &vulnerability.Metadata{ + ID: "CVE-1999-0001", + Severity: "Low", + Cvss: []vulnerability.Cvss{ + { + Source: "nvd", + Type: "CVSS", + Version: "3.1", + Vector: "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:H", + Metrics: vulnerability.CvssMetrics{ + BaseScore: 8.2, + }, + }, + }, + KnownExploited: nil, + EPSS: []vulnerability.EPSS{ + { + CVE: "CVE-1999-0001", + EPSS: 0.03, + Percentile: 0.42, + }, + }, + }, }, Package: p1, Details: []match.Detail{ @@ -139,6 +162,34 @@ func generateMatches(t *testing.T, p1, p2 pkg.Package) match.Matches { ID: "CVE-1999-0002", Namespace: "source-2", }, + Metadata: &vulnerability.Metadata{ + ID: "CVE-1999-0002", + Severity: "Critical", + Cvss: []vulnerability.Cvss{ + { + Source: "nvd", + Type: "CVSS", + Version: "3.1", + Vector: "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H", + Metrics: vulnerability.CvssMetrics{ + BaseScore: 8.5, + }, + }, + }, + KnownExploited: []vulnerability.KnownExploited{ + { + CVE: "CVE-1999-0002", + KnownRansomwareCampaignUse: "Known", + }, + }, + EPSS: []vulnerability.EPSS{ + { + CVE: "CVE-1999-0002", + EPSS: 0.08, + Percentile: 0.53, + }, + }, + }, }, Package: p2, Details: []match.Detail{ @@ -173,6 +224,29 @@ func generateIgnoredMatches(t *testing.T, p pkg.Package) []match.IgnoredMatch { ID: "CVE-1999-0001", Namespace: "source-1", }, + Metadata: &vulnerability.Metadata{ + ID: "CVE-1999-0001", + Severity: "Low", + Cvss: []vulnerability.Cvss{ + { + Source: "nvd", + Type: "CVSS", + Version: "3.1", + Vector: "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:H", + Metrics: vulnerability.CvssMetrics{ + BaseScore: 8.2, + }, + }, + }, + KnownExploited: nil, + EPSS: []vulnerability.EPSS{ + { + CVE: "CVE-1999-0001", + EPSS: 0.03, + Percentile: 0.42, + }, + }, + }, }, Package: p, Details: []match.Detail{ @@ -200,6 +274,34 @@ func generateIgnoredMatches(t *testing.T, p pkg.Package) []match.IgnoredMatch { ID: "CVE-1999-0002", Namespace: "source-2", }, + Metadata: &vulnerability.Metadata{ + ID: "CVE-1999-0002", + Severity: "Critical", + Cvss: []vulnerability.Cvss{ + { + Source: "nvd", + Type: "CVSS", + Version: "3.1", + Vector: "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H", + Metrics: vulnerability.CvssMetrics{ + BaseScore: 8.5, + }, + }, + }, + KnownExploited: []vulnerability.KnownExploited{ + { + CVE: "CVE-1999-0002", + KnownRansomwareCampaignUse: "Known", + }, + }, + EPSS: []vulnerability.EPSS{ + { + CVE: "CVE-1999-0002", + EPSS: 0.08, + Percentile: 0.53, + }, + }, + }, }, Package: p, Details: []match.Detail{ @@ -224,6 +326,28 @@ func generateIgnoredMatches(t *testing.T, p pkg.Package) []match.IgnoredMatch { ID: "CVE-1999-0004", Namespace: "source-2", }, + Metadata: &vulnerability.Metadata{ + ID: "CVE-1999-0004", + Severity: "High", + Cvss: []vulnerability.Cvss{ + { + Source: "nvd", + Type: "CVSS", + Version: "3.1", + Vector: "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:L", + Metrics: vulnerability.CvssMetrics{ + BaseScore: 7.2, + }, + }, + }, + EPSS: []vulnerability.EPSS{ + { + CVE: "CVE-1999-0004", + EPSS: 0.03, + Percentile: 0.75, + }, + }, + }, }, Package: p, Details: []match.Detail{ diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden index 05f412100c6..f2eff6987df 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden @@ -4,20 +4,28 @@ "vulnerability": { "id": "CVE-1999-0001", "dataSource": "", - "namespace": "source-1", "severity": "Low", "urls": [], - "description": "1999-01 description", "cvss": [ { - "version": "3.0", - "vector": "another vector", + "source": "nvd", + "type": "CVSS", + "version": "3.1", + "vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:H", "metrics": { - "baseScore": 4 + "baseScore": 8.2 }, "vendorMetadata": {} } ], + "epss": [ + { + "cve": "CVE-1999-0001", + "epss": 0.03, + "percentile": 0.42, + "date": "0001-01-01" + } + ], "fix": { "versions": [ "1.2.1", @@ -26,7 +34,8 @@ ], "state": "fixed" }, - "advisories": [] + "advisories": [], + "risk": 1.68 }, "relatedVulnerabilities": [], "matchDetails": [ @@ -76,30 +85,40 @@ "vulnerability": { "id": "CVE-1999-0002", "dataSource": "", - "namespace": "source-2", "severity": "Critical", "urls": [], - "description": "1999-02 description", "cvss": [ { - "version": "2.0", - "vector": "vector", + "source": "nvd", + "type": "CVSS", + "version": "3.1", + "vector": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H", "metrics": { - "baseScore": 1, - "exploitabilityScore": 2, - "impactScore": 3 + "baseScore": 8.5 }, - "vendorMetadata": { - "BaseSeverity": "Low", - "Status": "verified" - } + "vendorMetadata": {} + } + ], + "knownExploited": [ + { + "cve": "CVE-1999-0002", + "knownRansomwareCampaignUse": "Known" + } + ], + "epss": [ + { + "cve": "CVE-1999-0002", + "epss": 0.08, + "percentile": 0.53, + "date": "0001-01-01" } ], "fix": { "versions": [], "state": "" }, - "advisories": [] + "advisories": [], + "risk": 96.25000000000001 }, "relatedVulnerabilities": [], "matchDetails": [ diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden index adb4ab0de50..1c2842a1f59 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden @@ -4,20 +4,28 @@ "vulnerability": { "id": "CVE-1999-0001", "dataSource": "", - "namespace": "source-1", "severity": "Low", "urls": [], - "description": "1999-01 description", "cvss": [ { - "version": "3.0", - "vector": "another vector", + "source": "nvd", + "type": "CVSS", + "version": "3.1", + "vector": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:H", "metrics": { - "baseScore": 4 + "baseScore": 8.2 }, "vendorMetadata": {} } ], + "epss": [ + { + "cve": "CVE-1999-0001", + "epss": 0.03, + "percentile": 0.42, + "date": "0001-01-01" + } + ], "fix": { "versions": [ "1.2.1", @@ -26,7 +34,8 @@ ], "state": "fixed" }, - "advisories": [] + "advisories": [], + "risk": 1.68 }, "relatedVulnerabilities": [], "matchDetails": [ @@ -76,30 +85,40 @@ "vulnerability": { "id": "CVE-1999-0002", "dataSource": "", - "namespace": "source-2", "severity": "Critical", "urls": [], - "description": "1999-02 description", "cvss": [ { - "version": "2.0", - "vector": "vector", + "source": "nvd", + "type": "CVSS", + "version": "3.1", + "vector": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:H/A:H", "metrics": { - "baseScore": 1, - "exploitabilityScore": 2, - "impactScore": 3 + "baseScore": 8.5 }, - "vendorMetadata": { - "BaseSeverity": "Low", - "Status": "verified" - } + "vendorMetadata": {} + } + ], + "knownExploited": [ + { + "cve": "CVE-1999-0002", + "knownRansomwareCampaignUse": "Known" + } + ], + "epss": [ + { + "cve": "CVE-1999-0002", + "epss": 0.08, + "percentile": 0.53, + "date": "0001-01-01" } ], "fix": { "versions": [], "state": "" }, - "advisories": [] + "advisories": [], + "risk": 96.25000000000001 }, "relatedVulnerabilities": [], "matchDetails": [ diff --git a/grype/presenter/models/sort.go b/grype/presenter/models/sort.go index 8bb8ee10ab9..f1c5895e44f 100644 --- a/grype/presenter/models/sort.go +++ b/grype/presenter/models/sort.go @@ -10,13 +10,18 @@ import ( type SortStrategy string const ( - SortByPackage SortStrategy = "package" + SortByPackage SortStrategy = "package" + SortBySeverity SortStrategy = "severity" + SortByThreat SortStrategy = "epss" + SortByRisk SortStrategy = "risk" + SortByKEV SortStrategy = "kev" + SortByVulnerability SortStrategy = "vulnerability" - defaultSortStrategy = SortByPackage + DefaultSortStrategy = SortByRisk ) func SortStrategies() []SortStrategy { - return []SortStrategy{SortByPackage} + return []SortStrategy{SortByPackage, SortBySeverity, SortByThreat, SortByRisk, SortByKEV, SortByVulnerability} } func (s SortStrategy) String() string { @@ -37,14 +42,85 @@ type sortStrategyImpl []compareFunc // matchSortStrategy provides predefined sort strategies for Match var matchSortStrategy = map[SortStrategy]sortStrategyImpl{ SortByPackage: { - compareByPackageName, - compareByPackageVersion, - compareByPackageType, + comparePackageAttributes, + compareVulnerabilityAttributes, + }, + SortByVulnerability: { + compareVulnerabilityAttributes, + comparePackageAttributes, + }, + SortBySeverity: { + // severity and tangential attributes... + compareBySeverity, + compareByRisk, + compareByEPSSPercentile, + // followed by package attributes... + comparePackageAttributes, + // followed by the remaining vulnerability attributes... + compareByVulnerabilityID, + }, + SortByThreat: { + // epss and tangential attributes... + compareByEPSSPercentile, + compareByRisk, + compareBySeverity, + // followed by package attributes... + comparePackageAttributes, + // followed by the remaining vulnerability attributes... + compareByVulnerabilityID, + }, + SortByRisk: { + // risk and tangential attributes... + compareByRisk, + compareBySeverity, + compareByEPSSPercentile, + // followed by package attributes... + comparePackageAttributes, + // followed by the remaining vulnerability attributes... + compareByVulnerabilityID, + }, + SortByKEV: { + compareByKEV, + // risk and tangential attributes... + compareByRisk, compareBySeverity, + compareByEPSSPercentile, + // followed by package attributes... + comparePackageAttributes, + // followed by the remaining vulnerability attributes... compareByVulnerabilityID, }, } +func compareVulnerabilityAttributes(a, b Match) int { + return combine( + compareByVulnerabilityID, + compareByRisk, + compareBySeverity, + compareByEPSSPercentile, + )(a, b) +} + +func comparePackageAttributes(a, b Match) int { + return combine( + compareByPackageName, + compareByPackageVersion, + compareByPackageType, + )(a, b) +} + +func combine(impls ...compareFunc) compareFunc { + return func(a, b Match) int { + for _, impl := range impls { + result := impl(a, b) + if result != 0 { + return result + } + } + return 0 + } +} + // SortMatches sorts matches based on a strategy name func SortMatches(matches []Match, strategyName SortStrategy) { sortWithStrategy(matches, getSortStrategy(strategyName)) @@ -53,8 +129,8 @@ func SortMatches(matches []Match, strategyName SortStrategy) { func getSortStrategy(strategyName SortStrategy) sortStrategyImpl { strategy, exists := matchSortStrategy[strategyName] if !exists { - log.WithFields("strategy", strategyName).Debugf("unknown sort strategy, falling back to default of %q", defaultSortStrategy) - strategy = matchSortStrategy[defaultSortStrategy] + log.WithFields("strategy", strategyName).Debugf("unknown sort strategy, falling back to default of %q", DefaultSortStrategy) + strategy = matchSortStrategy[DefaultSortStrategy] } return strategy } @@ -87,8 +163,22 @@ func compareByVulnerabilityID(a, b Match) int { } func compareBySeverity(a, b Match) int { - aScore := severityScore(a.Vulnerability.Severity) - bScore := severityScore(b.Vulnerability.Severity) + aScore := severityPriority(a.Vulnerability.Severity) + bScore := severityPriority(b.Vulnerability.Severity) + + switch { + case aScore < bScore: // higher severity first + return -1 + case aScore > bScore: + return 1 + default: + return 0 + } +} + +func compareByEPSSPercentile(a, b Match) int { + aScore := epssPercentile(a.Vulnerability.EPSS) + bScore := epssPercentile(b.Vulnerability.EPSS) switch { case aScore > bScore: // higher severity first @@ -142,20 +232,61 @@ func compareByPackageType(a, b Match) int { } } -// severityScore maps severity strings to numeric scores for comparison -func severityScore(severity string) int { +func compareByRisk(a, b Match) int { + aRisk := a.Vulnerability.Risk + bRisk := b.Vulnerability.Risk + + switch { + case aRisk > bRisk: + return -1 + case aRisk < bRisk: + return 1 + default: + return 0 + } +} + +func compareByKEV(a, b Match) int { + aKEV := len(a.Vulnerability.KnownExploited) + bKEV := len(b.Vulnerability.KnownExploited) + + switch { + case aKEV > bKEV: + return -1 + case aKEV < bKEV: + return 1 + default: + return 0 + } +} + +func epssPercentile(es []EPSS) float64 { + switch len(es) { + case 0: + return 0.0 + case 1: + return es[0].Percentile + } + sort.Slice(es, func(i, j int) bool { + return es[i].Percentile > es[j].Percentile + }) + return es[0].Percentile +} + +// severityPriority maps severity strings to numeric priority for comparison (the lowest value is most severe) +func severityPriority(severity string) int { switch strings.ToLower(severity) { case "critical": - return 5 + return 1 case "high": - return 4 + return 2 case "medium": return 3 case "low": - return 2 + return 4 case "negligible": - return 1 + return 5 default: - return 0 + return 100 // least severe } } diff --git a/grype/presenter/models/sort_test.go b/grype/presenter/models/sort_test.go index 478550fcab4..e86ae0c3a83 100644 --- a/grype/presenter/models/sort_test.go +++ b/grype/presenter/models/sort_test.go @@ -4,16 +4,154 @@ import ( "testing" "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) -func TestSortMatches(t *testing.T) { - matches := []Match{ +func TestSortStrategies(t *testing.T) { + strategies := SortStrategies() + expected := []SortStrategy{ + SortByPackage, + SortBySeverity, + SortByThreat, + SortByRisk, + SortByKEV, + SortByVulnerability, + } + assert.Equal(t, expected, strategies) +} + +func TestSortStrategyString(t *testing.T) { + assert.Equal(t, "package", SortByPackage.String()) + assert.Equal(t, "severity", SortBySeverity.String()) + assert.Equal(t, "epss", SortByThreat.String()) + assert.Equal(t, "risk", SortByRisk.String()) + assert.Equal(t, "kev", SortByKEV.String()) + assert.Equal(t, "vulnerability", SortByVulnerability.String()) +} + +func TestGetSortStrategy(t *testing.T) { + tests := []struct { + name string + strategyName SortStrategy + expected bool + }{ + { + name: "Valid strategy", + strategyName: SortByPackage, + expected: true, + }, + { + name: "Invalid strategy", + strategyName: "invalid", + expected: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + strategy := getSortStrategy(tt.strategyName) + validStrategy, _ := matchSortStrategy[tt.strategyName] + + if tt.expected { + require.NotNil(t, strategy) + assert.Equal(t, validStrategy, strategy) + } else { + // Should fallback to default strategy + assert.NotNil(t, strategy) + assert.Equal(t, matchSortStrategy[DefaultSortStrategy], strategy) + } + }) + } +} + +func TestEPSSPercentile(t *testing.T) { + tests := []struct { + name string + epss []EPSS + expected float64 + }{ + { + name: "Empty slice", + epss: []EPSS{}, + expected: 0.0, + }, + { + name: "Single item", + epss: []EPSS{ + {Percentile: 0.75}, + }, + expected: 0.75, + }, + { + name: "Multiple items, already sorted", + epss: []EPSS{ + {Percentile: 0.95}, + {Percentile: 0.75}, + {Percentile: 0.50}, + }, + expected: 0.95, + }, + { + name: "Multiple items, unsorted", + epss: []EPSS{ + {Percentile: 0.50}, + {Percentile: 0.95}, + {Percentile: 0.75}, + }, + expected: 0.95, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := epssPercentile(tt.epss) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestSeverityPriority(t *testing.T) { + tests := []struct { + severity string + expected int + }{ + {"critical", 1}, + {"CRITICAL", 1}, + {"high", 2}, + {"HIGH", 2}, + {"medium", 3}, + {"MEDIUM", 3}, + {"low", 4}, + {"LOW", 4}, + {"negligible", 5}, + {"NEGLIGIBLE", 5}, + {"unknown", 100}, + {"", 100}, + } + + for _, tt := range tests { + t.Run(tt.severity, func(t *testing.T) { + result := severityPriority(tt.severity) + assert.Equal(t, tt.expected, result) + }) + } +} + +func createTestMatches() []Match { + return []Match{ { + // match 0: medium severity, high risk, high EPSS, no KEV Vulnerability: Vulnerability{ VulnerabilityMetadata: VulnerabilityMetadata{ ID: "CVE-2023-1111", Severity: "medium", + EPSS: []EPSS{ + {Percentile: 0.90}, + }, + KnownExploited: []KnownExploited{}, // empty KEV }, + Risk: 75.0, }, Artifact: Package{ Name: "package-b", @@ -22,11 +160,17 @@ func TestSortMatches(t *testing.T) { }, }, { + // match 1: critical severity, medium risk, medium EPSS, no KEV Vulnerability: Vulnerability{ VulnerabilityMetadata: VulnerabilityMetadata{ ID: "CVE-2023-2222", Severity: "critical", + EPSS: []EPSS{ + {Percentile: 0.70}, + }, + KnownExploited: []KnownExploited{}, // empty KEV }, + Risk: 50.0, }, Artifact: Package{ Name: "package-a", @@ -35,11 +179,19 @@ func TestSortMatches(t *testing.T) { }, }, { + // match 2: high severity, low risk, low EPSS, has KEV Vulnerability: Vulnerability{ VulnerabilityMetadata: VulnerabilityMetadata{ ID: "CVE-2023-3333", Severity: "high", + EPSS: []EPSS{ + {Percentile: 0.30}, + }, + KnownExploited: []KnownExploited{ + {CVE: "CVE-2023-3333", KnownRansomwareCampaignUse: "No"}, + }, // has KEV }, + Risk: 25.0, }, Artifact: Package{ Name: "package-a", @@ -48,11 +200,17 @@ func TestSortMatches(t *testing.T) { }, }, { + // match 3: low severity, very low risk, very low EPSS, no KEV Vulnerability: Vulnerability{ VulnerabilityMetadata: VulnerabilityMetadata{ ID: "CVE-2023-4444", Severity: "low", + EPSS: []EPSS{ + {Percentile: 0.10}, + }, + KnownExploited: []KnownExploited{}, // empty KEV }, + Risk: 10.0, }, Artifact: Package{ Name: "package-c", @@ -61,11 +219,20 @@ func TestSortMatches(t *testing.T) { }, }, { + // match 4: critical severity, very low risk, medium EPSS, has KEV with ransomware Vulnerability: Vulnerability{ VulnerabilityMetadata: VulnerabilityMetadata{ ID: "CVE-2023-5555", Severity: "critical", + EPSS: []EPSS{ + {Percentile: 0.50}, + }, + KnownExploited: []KnownExploited{ + {CVE: "CVE-2023-5555", KnownRansomwareCampaignUse: "Known"}, + {CVE: "CVE-2023-5555", KnownRansomwareCampaignUse: "Known", Product: "Different Product"}, + }, // has multiple KEV entries with ransomware }, + Risk: 5.0, }, Artifact: Package{ Name: "package-a", @@ -74,122 +241,262 @@ func TestSortMatches(t *testing.T) { }, }, } +} - t.Run("SortByPackage", func(t *testing.T) { - testMatches := deepCopyMatches(matches) - SortMatches(testMatches, SortByPackage) - - expected := []Match{ - // package-a with 1.0.0 version, docker type first (alphabetical) - matches[4], // package-a, 1.0.0, docker, critical - matches[2], // package-a, 1.0.0, npm, high - matches[1], // package-a, 2.0.0, docker, critical - matches[0], // package-b, 1.2.0, npm, medium - matches[3], // package-c, 3.1.0, gem, low - } +func TestAllSortStrategies(t *testing.T) { + matches := createTestMatches() - if diff := cmp.Diff(expected, testMatches); diff != "" { - t.Errorf("SortByPackage mismatch (-want +got):\n%s", diff) - } - }) + tests := []struct { + strategy SortStrategy + expected []int // indexes into the original matches slice + }{ + { + strategy: SortByPackage, + expected: []int{4, 2, 1, 0, 3}, // sorted by package name, version, type + }, + { + strategy: SortByVulnerability, + expected: []int{0, 1, 2, 3, 4}, // sorted by vulnerability ID + }, + { + strategy: SortBySeverity, + expected: []int{1, 4, 2, 0, 3}, // sorted by severity: critical, critical, high, medium, low + }, + { + strategy: SortByThreat, + expected: []int{0, 1, 4, 2, 3}, // sorted by EPSS percentile: 0.90, 0.70, 0.50, 0.30, 0.10 + }, + { + strategy: SortByRisk, + expected: []int{0, 1, 2, 3, 4}, // sorted by risk: 75.0, 50.0, 25.0, 10.0, 5.0 + }, + { + strategy: SortByKEV, + expected: []int{4, 2, 0, 1, 3}, // sorted by KEV count: 2, 1, 0, 0, 0 (with ties broken by risk) + }, + } - t.Run("UnknownStrategy", func(t *testing.T) { - testMatches := deepCopyMatches(matches) - // should use default (package) strategy for unknown strategy names - SortMatches(testMatches, "unknown") - - expected := []Match{ - matches[4], // package-a, 1.0.0, docker - matches[2], // package-a, 1.0.0, npm - matches[1], // package-a, 2.0.0, docker - matches[0], // package-b, 1.2.0, npm - matches[3], // package-c, 3.1.0, gem - } + for _, tt := range tests { + t.Run(string(tt.strategy), func(t *testing.T) { + testMatches := deepCopyMatches(matches) + SortMatches(testMatches, tt.strategy) - if diff := cmp.Diff(expected, testMatches); diff != "" { - t.Errorf("Unknown strategy mismatch (-want +got):\n%s", diff) - } - }) + expected := make([]Match, len(tt.expected)) + for i, idx := range tt.expected { + expected[i] = matches[idx] + } + + if diff := cmp.Diff(expected, testMatches); diff != "" { + t.Errorf("%s mismatch (-want +got):\n%s", tt.strategy, diff) + } + }) + } } -func TestEdgeCases(t *testing.T) { - t.Run("EmptySlice", func(t *testing.T) { - matches := []Match{} - // should not panic on empty slice - SortMatches(matches, SortByPackage) +func TestIndividualCompareFunctions(t *testing.T) { + ms := createTestMatches() + m0 := ms[0] // medium severity, high risk, high EPSS, no KEV + m1 := ms[1] // critical severity, medium risk, medium EPSS, no KEV + m2 := ms[2] // high severity, low risk, low EPSS, has KEV + m3 := ms[3] // low severity, very low risk, very low EPSS, no KEV + m4 := ms[4] // critical severity, very low risk, medium EPSS, has KEV with ransomware - expected := []Match{} - if diff := cmp.Diff(expected, matches); diff != "" { - t.Errorf("Empty slice mismatch (-want +got):\n%s", diff) + tests := []struct { + name string + compareFunc compareFunc + pairs []struct { + a, b Match + expected int } + }{ + { + name: "compareByVulnerabilityID", + compareFunc: compareByVulnerabilityID, + pairs: []struct { + a, b Match + expected int + }{ + {m0, m1, -1}, // CVE-2023-1111 < CVE-2023-2222 + {m1, m0, 1}, // CVE-2023-2222 > CVE-2023-1111 + {m0, m0, 0}, // Same ID + }, + }, + { + name: "compareBySeverity", + compareFunc: compareBySeverity, + pairs: []struct { + a, b Match + expected int + }{ + {m0, m1, 1}, // medium > critical + {m1, m0, -1}, // critical < medium + {m1, m4, 0}, // both critical + {m2, m3, -1}, // high < low + }, + }, + { + name: "compareByEPSSPercentile", + compareFunc: compareByEPSSPercentile, + pairs: []struct { + a, b Match + expected int + }{ + {m0, m1, -1}, // 0.90 > 0.70 + {m1, m0, 1}, // 0.70 < 0.90 + {m1, m4, -1}, // 0.70 > 0.50 + {m4, m1, 1}, // 0.50 < 0.70 + }, + }, + { + name: "compareByPackageName", + compareFunc: compareByPackageName, + pairs: []struct { + a, b Match + expected int + }{ + {m0, m1, 1}, // package-b > package-a + {m1, m0, -1}, // package-a < package-b + {m1, m2, 0}, // both package-a + }, + }, + { + name: "compareByPackageVersion", + compareFunc: compareByPackageVersion, + pairs: []struct { + a, b Match + expected int + }{ + {m1, m2, 1}, // 2.0.0 > 1.0.0 + {m2, m1, -1}, // 1.0.0 < 2.0.0 + {m2, m4, 0}, // both 1.0.0 + }, + }, + { + name: "compareByPackageType", + compareFunc: compareByPackageType, + pairs: []struct { + a, b Match + expected int + }{ + {m0, m1, 1}, // npm > docker + {m1, m0, -1}, // docker < npm + {m0, m2, 0}, // both npm + }, + }, + { + name: "compareByRisk", + compareFunc: compareByRisk, + pairs: []struct { + a, b Match + expected int + }{ + {m0, m1, -1}, // 75.0 > 50.0 + {m1, m0, 1}, // 50.0 < 75.0 + {m3, m4, -1}, // 10.0 > 5.0 + }, + }, + { + name: "compareByKEV", + compareFunc: compareByKEV, + pairs: []struct { + a, b Match + expected int + }{ + {m0, m2, 1}, // 0 < 1 KEV entry + {m2, m0, -1}, // 1 > 0 KEV entry + {m2, m4, 1}, // 1 < 2 KEV entries + {m4, m2, -1}, // 2 > 1 KEV entry + {m0, m1, 0}, // both 0 KEV entries + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + for _, pair := range tt.pairs { + result := tt.compareFunc(pair.a, pair.b) + assert.Equal(t, pair.expected, result, "comparing %v and %v", pair.a.Vulnerability.ID, pair.b.Vulnerability.ID) + } + }) + } +} + +func TestCombinedCompareFunctions(t *testing.T) { + ms := createTestMatches() + m0 := ms[0] // medium severity, high risk, high EPSS, no KEV, package-b + m1 := ms[1] // critical severity, medium risk, medium EPSS, no KEV, package-a + m2 := ms[2] // high severity, low risk, low EPSS, has KEV, package-a + + t.Run("compareVulnerabilityAttributes", func(t *testing.T) { + result := compareVulnerabilityAttributes(m0, m1) + assert.Equal(t, -1, result, "CVE-2023-1111 should come before CVE-2023-2222") + + result = compareVulnerabilityAttributes(m1, m0) + assert.Equal(t, 1, result, "CVE-2023-2222 should come after CVE-2023-1111") }) - t.Run("SingleItem", func(t *testing.T) { - matches := []Match{ - { - Vulnerability: Vulnerability{ - VulnerabilityMetadata: VulnerabilityMetadata{ - ID: "CVE-2023-1111", - Severity: "medium", - }, - }, - Artifact: Package{ - Name: "package-a", - Version: "1.0.0", - }, - }, - } - expected := deepCopyMatches(matches) - // should not change anything with a single item - SortMatches(matches, SortByPackage) + t.Run("comparePackageAttributes", func(t *testing.T) { + result := comparePackageAttributes(m0, m1) + assert.Equal(t, 1, result, "package-b should come after package-a") - if diff := cmp.Diff(expected, matches); diff != "" { - t.Errorf("Single item mismatch (-want +got):\n%s", diff) - } + result = comparePackageAttributes(m1, m2) + assert.Equal(t, 1, result, "package-a 2.0.0 should come after package-a 1.0.0") + + result = comparePackageAttributes(m1, m1) + assert.Equal(t, 0, result, "same package should be equal") }) - t.Run("NilValues", func(t *testing.T) { - matches := []Match{ - { - Vulnerability: Vulnerability{ - VulnerabilityMetadata: VulnerabilityMetadata{ - ID: "CVE-2023-1111", - Severity: "", - }, - }, - Artifact: Package{ - Name: "", - Version: "", - }, - }, - { - Vulnerability: Vulnerability{ - VulnerabilityMetadata: VulnerabilityMetadata{ - ID: "CVE-2023-2222", - Severity: "low", - }, - }, - Artifact: Package{ - Name: "package-a", - Version: "1.0.0", - }, - }, - } + t.Run("combine function", func(t *testing.T) { + // create a combined function that first compares by severity, then by risk if severity is equal + combined := combine(compareBySeverity, compareByRisk) - expected := []Match{ - matches[0], // empty name comes first alphabetically - matches[1], // "package-a" - } + result := combined(m0, m1) + assert.Equal(t, 1, result, "medium should come after critical regardless of risk") - // should handle empty strings properly - SortMatches(matches, SortByPackage) + // create two matches with the same severity but different risk + m5 := m1 // critical severity, risk 50.0 + m6 := m1 + m6.Vulnerability.Risk = 60.0 // critical severity, risk 60.0 - if diff := cmp.Diff(expected, matches); diff != "" { - t.Errorf("Nil values mismatch (-want +got):\n%s", diff) - } + result = combined(m5, m6) + assert.Equal(t, 1, result, "with equal severity, lower risk (50.0) should come after higher risk (60.0)") + + result = combined(m6, m5) + assert.Equal(t, -1, result, "with equal severity, higher risk (60.0) should come before lower risk (50.0)") }) } +func TestSortWithStrategy(t *testing.T) { + matches := createTestMatches() + + // create a custom strategy that sorts only by vulnerability ID + customStrategy := sortStrategyImpl{compareByVulnerabilityID} + + expected := []Match{ + matches[0], // CVE-2023-1111 + matches[1], // CVE-2023-2222 + matches[2], // CVE-2023-3333 + matches[3], // CVE-2023-4444 + matches[4], // CVE-2023-5555 + } + + testMatches := deepCopyMatches(matches) + sortWithStrategy(testMatches, customStrategy) + + if diff := cmp.Diff(expected, testMatches); diff != "" { + t.Errorf("sortWithStrategy mismatch (-want +got):\n%s", diff) + } + + // create an empty strategy (should not change the order) + emptyStrategy := sortStrategyImpl{} + originalMatches := deepCopyMatches(matches) + sortWithStrategy(originalMatches, emptyStrategy) + + if diff := cmp.Diff(matches, originalMatches); diff != "" { + t.Errorf("Empty strategy should not change order (-original +after):\n%s", diff) + } +} + func deepCopyMatches(matches []Match) []Match { result := make([]Match, len(matches)) copy(result, matches) diff --git a/grype/presenter/models/vulnerability.go b/grype/presenter/models/vulnerability.go index 391a9f636de..38216ce37cb 100644 --- a/grype/presenter/models/vulnerability.go +++ b/grype/presenter/models/vulnerability.go @@ -12,6 +12,7 @@ type Vulnerability struct { VulnerabilityMetadata Fix Fix `json:"fix"` Advisories []Advisory `json:"advisories"` + Risk float64 `json:"risk"` } type Fix struct { @@ -52,6 +53,7 @@ func NewVulnerability(vuln vulnerability.Vulnerability, metadata *vulnerability. State: string(vuln.Fix.State), }, Advisories: advisories, + Risk: metadata.RiskScore(), } } func sortVersions(fixedVersions []string, format version.Format) []string { diff --git a/grype/presenter/models/vulnerability_metadata.go b/grype/presenter/models/vulnerability_metadata.go index 98910f7955d..d02e6243227 100644 --- a/grype/presenter/models/vulnerability_metadata.go +++ b/grype/presenter/models/vulnerability_metadata.go @@ -71,9 +71,9 @@ func toKnownExploited(knownExploited []vulnerability.KnownExploited) []KnownExpl CVE: ke.CVE, VendorProject: ke.VendorProject, Product: ke.Product, - DateAdded: ke.DateAdded.Format(time.DateOnly), + DateAdded: formatDate(ke.DateAdded), RequiredAction: ke.RequiredAction, - DueDate: ke.DueDate.Format(time.DateOnly), + DueDate: formatDate(ke.DueDate), KnownRansomwareCampaignUse: ke.KnownRansomwareCampaignUse, Notes: ke.Notes, URLs: ke.URLs, @@ -83,6 +83,13 @@ func toKnownExploited(knownExploited []vulnerability.KnownExploited) []KnownExpl return result } +func formatDate(t *time.Time) string { + if t == nil { + return "" + } + return t.Format(time.DateOnly) +} + func toEPSS(epss []vulnerability.EPSS) []EPSS { result := make([]EPSS, len(epss)) for idx, e := range epss { diff --git a/grype/presenter/sarif/presenter_test.go b/grype/presenter/sarif/presenter_test.go index cec3081863f..2b30cfec612 100644 --- a/grype/presenter/sarif/presenter_test.go +++ b/grype/presenter/sarif/presenter_test.go @@ -6,6 +6,7 @@ import ( "os/exec" "testing" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -17,7 +18,7 @@ import ( "github.com/anchore/syft/syft/source/directorysource" ) -var updateSnapshot = flag.Bool("update-sarif", false, "update .golden files for sarif presenters") +var updateSnapshot = flag.Bool("update", false, "update .golden files for sarif presenters") var validatorImage = "ghcr.io/anchore/sarif-validator:0.1.0@sha256:a0729d695e023740f5df6bcb50d134e88149bea59c63a896a204e88f62b564c6" func TestSarifPresenter(t *testing.T) { @@ -57,8 +58,8 @@ func TestSarifPresenter(t *testing.T) { actual = internal.Redact(actual) expected = internal.Redact(expected) - if !bytes.Equal(expected, actual) { - assert.JSONEq(t, string(expected), string(actual)) + if d := cmp.Diff(string(expected), string(actual)); d != "" { + t.Fatalf("(-want +got):\n%s", d) } }) } diff --git a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden index fa07c7fd78a..91900ee484b 100644 --- a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden +++ b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_directory.golden @@ -16,15 +16,15 @@ "text": "CVE-1999-0001 low vulnerability for package-1 package" }, "fullDescription": { - "text": "1999-01 description" + "text": "Version 1.1.1 is affected with an available fix in versions 1.2.1,2.1.3,3.4.0" }, "helpUri": "https://github.com/anchore/grype", "help": { - "text": "Vulnerability CVE-1999-0001\nSeverity: low\nPackage: package-1\nVersion: 1.1.1\nFix Version: 1.2.1,2.1.3,3.4.0\nType: rpm\nLocation: /some/path/somefile-1.txt\nData Namespace: source-1\nLink: CVE-1999-0001", - "markdown": "**Vulnerability CVE-1999-0001**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| low | package-1 | 1.1.1 | 1.2.1,2.1.3,3.4.0 | rpm | /some/path/somefile-1.txt | source-1 | CVE-1999-0001 |\n" + "text": "Vulnerability CVE-1999-0001\nSeverity: low\nPackage: package-1\nVersion: 1.1.1\nFix Version: 1.2.1,2.1.3,3.4.0\nType: rpm\nLocation: /some/path/somefile-1.txt\nData Namespace: \nLink: CVE-1999-0001", + "markdown": "**Vulnerability CVE-1999-0001**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| low | package-1 | 1.1.1 | 1.2.1,2.1.3,3.4.0 | rpm | /some/path/somefile-1.txt | | CVE-1999-0001 |\n" }, "properties": { - "security-severity": "4.0" + "security-severity": "8.2" } }, { @@ -34,18 +34,18 @@ "text": "CVE-1999-0002 critical vulnerability for package-2 package" }, "fullDescription": { - "text": "1999-02 description" + "text": "Version 2.2.2 is affected with no fixes reported yet." }, "helpUri": "https://github.com/anchore/grype", "help": { - "text": "Vulnerability CVE-1999-0002\nSeverity: critical\nPackage: package-2\nVersion: 2.2.2\nFix Version: \nType: deb\nLocation: /some/path/somefile-2.txt\nData Namespace: source-2\nLink: CVE-1999-0002", - "markdown": "**Vulnerability CVE-1999-0002**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| critical | package-2 | 2.2.2 | | deb | /some/path/somefile-2.txt | source-2 | CVE-1999-0002 |\n" + "text": "Vulnerability CVE-1999-0002\nSeverity: critical\nPackage: package-2\nVersion: 2.2.2\nFix Version: \nType: deb\nLocation: /some/path/somefile-2.txt\nData Namespace: \nLink: CVE-1999-0002", + "markdown": "**Vulnerability CVE-1999-0002**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| critical | package-2 | 2.2.2 | | deb | /some/path/somefile-2.txt | | CVE-1999-0002 |\n" }, "properties": { "purls": [ "pkg:deb/package-2@2.2.2" ], - "security-severity": "1.0" + "security-severity": "8.5" } } ] diff --git a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden index 2de58e3ef4d..b9d33518d2b 100644 --- a/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden +++ b/grype/presenter/sarif/test-fixtures/snapshot/TestSarifPresenter_image.golden @@ -16,15 +16,15 @@ "text": "CVE-1999-0001 low vulnerability for package-1 package" }, "fullDescription": { - "text": "1999-01 description" + "text": "Version 1.1.1 is affected with an available fix in versions 1.2.1,2.1.3,3.4.0" }, "helpUri": "https://github.com/anchore/grype", "help": { - "text": "Vulnerability CVE-1999-0001\nSeverity: low\nPackage: package-1\nVersion: 1.1.1\nFix Version: 1.2.1,2.1.3,3.4.0\nType: rpm\nLocation: somefile-1.txt\nData Namespace: source-1\nLink: CVE-1999-0001", - "markdown": "**Vulnerability CVE-1999-0001**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| low | package-1 | 1.1.1 | 1.2.1,2.1.3,3.4.0 | rpm | somefile-1.txt | source-1 | CVE-1999-0001 |\n" + "text": "Vulnerability CVE-1999-0001\nSeverity: low\nPackage: package-1\nVersion: 1.1.1\nFix Version: 1.2.1,2.1.3,3.4.0\nType: rpm\nLocation: somefile-1.txt\nData Namespace: \nLink: CVE-1999-0001", + "markdown": "**Vulnerability CVE-1999-0001**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| low | package-1 | 1.1.1 | 1.2.1,2.1.3,3.4.0 | rpm | somefile-1.txt | | CVE-1999-0001 |\n" }, "properties": { - "security-severity": "4.0" + "security-severity": "8.2" } }, { @@ -34,18 +34,18 @@ "text": "CVE-1999-0002 critical vulnerability for package-2 package" }, "fullDescription": { - "text": "1999-02 description" + "text": "Version 2.2.2 is affected with no fixes reported yet." }, "helpUri": "https://github.com/anchore/grype", "help": { - "text": "Vulnerability CVE-1999-0002\nSeverity: critical\nPackage: package-2\nVersion: 2.2.2\nFix Version: \nType: deb\nLocation: somefile-2.txt\nData Namespace: source-2\nLink: CVE-1999-0002", - "markdown": "**Vulnerability CVE-1999-0002**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| critical | package-2 | 2.2.2 | | deb | somefile-2.txt | source-2 | CVE-1999-0002 |\n" + "text": "Vulnerability CVE-1999-0002\nSeverity: critical\nPackage: package-2\nVersion: 2.2.2\nFix Version: \nType: deb\nLocation: somefile-2.txt\nData Namespace: \nLink: CVE-1999-0002", + "markdown": "**Vulnerability CVE-1999-0002**\n| Severity | Package | Version | Fix Version | Type | Location | Data Namespace | Link |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n| critical | package-2 | 2.2.2 | | deb | somefile-2.txt | | CVE-1999-0002 |\n" }, "properties": { "purls": [ "pkg:deb/package-2@2.2.2" ], - "security-severity": "1.0" + "security-severity": "8.5" } } ] diff --git a/grype/presenter/table/__snapshots__/presenter_test.snap b/grype/presenter/table/__snapshots__/presenter_test.snap index 7ea321677bd..b3aa7fcbd1c 100755 --- a/grype/presenter/table/__snapshots__/presenter_test.snap +++ b/grype/presenter/table/__snapshots__/presenter_test.snap @@ -1,15 +1,15 @@ [TestTablePresenter/no_color - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low -package-2 2.2.2 deb CVE-1999-0002 Critical +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY EPSS% RISK +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low 42.00 1.7 +package-2 2.2.2 deb CVE-1999-0002 Critical 53.00 96.3 (kev) --- [TestTablePresenter/with_color - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low -package-2 2.2.2 deb CVE-1999-0002 Critical +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY EPSS% RISK +package-1 1.1.1 1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low 42.00 1.7 +package-2 2.2.2 deb CVE-1999-0002 Critical 53.00 96.3 KEV --- @@ -19,35 +19,35 @@ No vulnerabilities found --- [TestHidesIgnoredMatches - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low -package-2 2.2.2 deb CVE-1999-0002 Critical +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY EPSS% RISK +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low 42.00 1.7 +package-2 2.2.2 deb CVE-1999-0002 Critical 53.00 96.3 (kev) --- [TestDisplaysIgnoredMatches - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low -package-2 2.2.2 deb CVE-1999-0002 Critical -package-2 2.2.2 deb CVE-1999-0001 Low (suppressed) -package-2 2.2.2 deb CVE-1999-0002 Critical (suppressed) -package-2 2.2.2 deb CVE-1999-0004 Critical (suppressed by VEX) +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY EPSS% RISK +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low 42.00 1.7 +package-2 2.2.2 deb CVE-1999-0002 Critical 53.00 96.3 (kev) +package-2 2.2.2 deb CVE-1999-0001 Low 42.00 1.7 (suppressed) +package-2 2.2.2 deb CVE-1999-0002 Critical 53.00 96.3 (kev, suppressed) +package-2 2.2.2 deb CVE-1999-0004 High 75.00 2.2 (suppressed by VEX) --- [TestDisplaysDistro - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low (ubuntu:2.5) -package-2 2.2.2 deb CVE-1999-0002 Critical (ubuntu:3.5) +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY EPSS% RISK +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low 42.00 1.7 (ubuntu:2.5) +package-2 2.2.2 deb CVE-1999-0002 Critical 53.00 96.3 (kev, ubuntu:3.5) --- [TestDisplaysIgnoredMatchesAndDistro - 1] -NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY -package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low (ubuntu:2.5) -package-2 2.2.2 deb CVE-1999-0002 Critical (ubuntu:3.5) -package-2 2.2.2 deb CVE-1999-0001 Low (ubuntu:2.5, suppressed) -package-2 2.2.2 deb CVE-1999-0002 Critical (ubuntu:3.5, suppressed) -package-2 2.2.2 deb CVE-1999-0004 Critical (suppressed by VEX) +NAME INSTALLED FIXED-IN TYPE VULNERABILITY SEVERITY EPSS% RISK +package-1 1.1.1 *1.2.1, 2.1.3, 3.4.0 rpm CVE-1999-0001 Low 42.00 1.7 (ubuntu:2.5) +package-2 2.2.2 deb CVE-1999-0002 Critical 53.00 96.3 (kev, ubuntu:3.5) +package-2 2.2.2 deb CVE-1999-0001 Low 42.00 1.7 (ubuntu:2.5, suppressed) +package-2 2.2.2 deb CVE-1999-0002 Critical 53.00 96.3 (kev, ubuntu:3.5, suppressed) +package-2 2.2.2 deb CVE-1999-0004 High 75.00 2.2 (suppressed by VEX) --- diff --git a/grype/presenter/table/presenter.go b/grype/presenter/table/presenter.go index f936861d893..874a51a72c6 100644 --- a/grype/presenter/table/presenter.go +++ b/grype/presenter/table/presenter.go @@ -26,6 +26,14 @@ type Presenter struct { withColor bool recommendedFixStyle lipgloss.Style + kevStyle lipgloss.Style + criticalStyle lipgloss.Style + highStyle lipgloss.Style + mediumStyle lipgloss.Style + lowStyle lipgloss.Style + negligibleStyle lipgloss.Style + auxiliaryStyle lipgloss.Style + unknownStyle lipgloss.Style } type rows []row @@ -37,21 +45,48 @@ type row struct { PackageType string VulnerabilityID string Severity string + EPSS epss + Risk string Annotation string } +type epss struct { + Score float64 + Percentile float64 +} + +func (e epss) String() string { + percentile := e.Percentile * 100 + switch { + case percentile == 0: + return " N/A" + case percentile < 0.1: + return "< 0.1%" + } + return fmt.Sprintf("%5.2f", percentile) +} + // NewPresenter is a *Presenter constructor func NewPresenter(pb models.PresenterConfig, showSuppressed bool) *Presenter { withColor := supportsColor() fixStyle := lipgloss.NewStyle().Border(lipgloss.Border{Left: "*"}, false, false, false, true) if withColor { - fixStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("6")).Bold(true).Underline(true) + fixStyle = lipgloss.NewStyle() } return &Presenter{ document: pb.Document, showSuppressed: showSuppressed, withColor: withColor, recommendedFixStyle: fixStyle, + negligibleStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("240")), // dark gray + lowStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("36")), // cyan/teal + mediumStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("178")), // gold/amber + highStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("203")), // salmon/light red + criticalStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("198")).Bold(true), // bright pink + kevStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("198")).Reverse(true).Bold(true), // white on bright pink + //kevStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("198")), // bright pink + auxiliaryStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("240")), // dark gray + unknownStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("12")), // light blue } } @@ -65,7 +100,7 @@ func (p *Presenter) Present(output io.Writer) error { } table := tablewriter.NewWriter(output) - table.SetHeader([]string{"Name", "Installed", "Fixed-In", "Type", "Vulnerability", "Severity"}) + table.SetHeader([]string{"Name", "Installed", "Fixed-In", "Type", "Vulnerability", "Severity", "EPSS%", "Risk"}) table.SetAutoWrapText(false) table.SetHeaderAlignment(tablewriter.ALIGN_LEFT) table.SetAlignment(tablewriter.ALIGN_LEFT) @@ -79,22 +114,7 @@ func (p *Presenter) Present(output io.Writer) error { table.SetTablePadding(" ") table.SetNoWhiteSpace(true) - if p.withColor { - for _, row := range rs.Deduplicate() { - severityColor := getSeverityColor(row.Severity) - table.Rich(row.Columns(), []tablewriter.Colors{ - {}, // name - {}, // version - {}, // fix - {}, // package type - {}, // vulnerability ID - severityColor, // severity - annotationColor, // annotations - }) - } - } else { - table.AppendBulk(rs.Render()) - } + table.AppendBulk(rs.Render()) table.Render() @@ -143,22 +163,34 @@ func supportsColor() bool { return lipgloss.NewStyle().Foreground(lipgloss.Color("5")).Render("") != "" } -func (p *Presenter) newRow(m models.Match, severitySuffix string, showDistro bool) row { +func (p *Presenter) newRow(m models.Match, extraAnnotation string, showDistro bool) row { var annotations []string if showDistro { if d, err := distro.FromString(m.Vulnerability.Namespace); err == nil { - annotations = append(annotations, fmt.Sprintf("%s:%s", d.DistroType(), d.Version())) + annotations = append(annotations, p.auxiliaryStyle.Render(fmt.Sprintf("%s:%s", d.DistroType(), d.Version()))) } } - if severitySuffix != "" { - annotations = append(annotations, severitySuffix) + if extraAnnotation != "" { + annotations = append(annotations, p.auxiliaryStyle.Render(extraAnnotation)) + } + + var kev, annotation string + if len(m.Vulnerability.KnownExploited) > 0 { + if p.withColor { + kev = p.kevStyle.Reverse(false).Render("") + p.kevStyle.Render("KEV") + p.kevStyle.Reverse(false).Render("") // ⚡❋◆◉፨⿻⨳✖• + } else { + annotations = append([]string{"kev"}, annotations...) + } } - annotation := "" if len(annotations) > 0 { - annotation = fmt.Sprintf("(%s)", strings.Join(annotations, ", ")) + annotation = p.auxiliaryStyle.Render("(") + strings.Join(annotations, p.auxiliaryStyle.Render(", ")) + p.auxiliaryStyle.Render(")") + } + + if kev != "" { + annotation = kev + " " + annotation } return row{ @@ -167,12 +199,58 @@ func (p *Presenter) newRow(m models.Match, severitySuffix string, showDistro boo Fix: p.formatFix(m), PackageType: string(m.Artifact.Type), VulnerabilityID: m.Vulnerability.ID, - Severity: m.Vulnerability.Severity, + Severity: p.formatSeverity(m.Vulnerability.Severity), + EPSS: newEPSS(m.Vulnerability.EPSS), + Risk: p.formatRisk(m.Vulnerability.Risk), Annotation: annotation, } } +func newEPSS(es []models.EPSS) epss { + if len(es) == 0 { + return epss{} + } + return epss{ + Score: es[0].EPSS, + Percentile: es[0].Percentile, + } +} + +func (p *Presenter) formatSeverity(severity string) string { + var severityStyle *lipgloss.Style + switch strings.ToLower(severity) { + case "critical": + severityStyle = &p.criticalStyle + case "high": + severityStyle = &p.highStyle + case "medium": + severityStyle = &p.mediumStyle + case "low": + severityStyle = &p.lowStyle + case "negligible": + severityStyle = &p.negligibleStyle + } + + if severityStyle == nil { + severityStyle = &p.unknownStyle + } + + return severityStyle.Render(severity) +} + +func (p *Presenter) formatRisk(risk float64) string { + // TODO: add color to risk? + switch { + case risk == 0: + return " N/A" + case risk < 0.1: + return "< 0.1" + } + return fmt.Sprintf("%5.1f", risk) +} + func (p *Presenter) formatFix(m models.Match) string { + // adjust the model fix state values for better presentation switch m.Vulnerability.Fix.State { case vulnerability.FixStateWontFix.String(): return "(won't fix)" @@ -180,6 +258,19 @@ func (p *Presenter) formatFix(m models.Match) string { return "" } + // do our best to summarize the fixed versions, de-epmhasize non-recommended versions + // also, since there is not a lot of screen real estate, we will truncate the list of fixed versions + // to ~30 characters (or so) to avoid wrapping. + return p.applyTruncation( + p.formatVersionsToDisplay( + m, + getRecommendedVersions(m), + ), + m.Vulnerability.Fix.Versions, + ) +} + +func getRecommendedVersions(m models.Match) *strset.Set { recommended := strset.New() for _, d := range m.MatchDetails { if d.Fix == nil { @@ -189,25 +280,72 @@ func (p *Presenter) formatFix(m models.Match) string { recommended.Add(d.Fix.SuggestedVersion) } } + return recommended +} - var vers []string +const maxVersionFieldLength = 30 + +func (p *Presenter) formatVersionsToDisplay(m models.Match, recommendedVersions *strset.Set) []string { hasMultipleVersions := len(m.Vulnerability.Fix.Versions) > 1 + shouldHighlightRecommended := hasMultipleVersions && recommendedVersions.Size() > 0 + + var currentCharacterCount int + added := strset.New() + var vers []string + for _, v := range m.Vulnerability.Fix.Versions { - if hasMultipleVersions && recommended.Has(v) { - vers = append(vers, p.recommendedFixStyle.Render(v)) - continue + if added.Has(v) { + continue // skip duplicates + } + + if shouldHighlightRecommended { + if recommendedVersions.Has(v) { + // recommended versions always get added + added.Add(v) + currentCharacterCount += len(v) + vers = append(vers, p.recommendedFixStyle.Render(v)) + continue + } + + // skip not-necessarily-recommended versions if we're running out of space + if currentCharacterCount+len(v) > maxVersionFieldLength { + continue + } + + // add not-necessarily-recommended versions with auxiliary styling + currentCharacterCount += len(v) + added.Add(v) + vers = append(vers, p.auxiliaryStyle.Render(v)) + } else { + // when not prioritizing, add all versions + added.Add(v) + vers = append(vers, v) } - vers = append(vers, v) } - return strings.Join(vers, ", ") + return vers +} + +func (p *Presenter) applyTruncation(formattedVersions []string, allVersions []string) string { + finalVersions := strings.Join(formattedVersions, p.auxiliaryStyle.Render(", ")) + + var characterCount int + for _, v := range allVersions { + characterCount += len(v) + } + + if characterCount > maxVersionFieldLength && len(allVersions) > 1 { + finalVersions += p.auxiliaryStyle.Render(", ...") + } + + return finalVersions } func (r row) Columns() []string { if r.Annotation != "" { - return []string{r.Name, r.Version, r.Fix, r.PackageType, r.VulnerabilityID, r.Severity, r.Annotation} + return []string{r.Name, r.Version, r.Fix, r.PackageType, r.VulnerabilityID, r.Severity, r.EPSS.String(), r.Risk, r.Annotation} } - return []string{r.Name, r.Version, r.Fix, r.PackageType, r.VulnerabilityID, r.Severity} + return []string{r.Name, r.Version, r.Fix, r.PackageType, r.VulnerabilityID, r.Severity, r.EPSS.String(), r.Risk} } func (r row) String() string { @@ -242,25 +380,3 @@ func (rs rows) Deduplicate() []row { // render final columns return deduped } - -func getSeverityColor(severity string) tablewriter.Colors { - severityFontType, severityColor := tablewriter.Normal, tablewriter.Normal - - switch strings.ToLower(severity) { - case "critical": - severityFontType = tablewriter.Bold - severityColor = tablewriter.FgRedColor - case "high": - severityColor = tablewriter.FgRedColor - case "medium": - severityColor = tablewriter.FgYellowColor - case "low": - severityColor = tablewriter.FgGreenColor - case "negligible": - severityColor = tablewriter.FgBlueColor - } - - return tablewriter.Colors{severityFontType, severityColor} -} - -var annotationColor = tablewriter.Colors{tablewriter.FgWhiteColor} diff --git a/grype/presenter/table/presenter_test.go b/grype/presenter/table/presenter_test.go index 2f7394b18f8..24bd168611a 100644 --- a/grype/presenter/table/presenter_test.go +++ b/grype/presenter/table/presenter_test.go @@ -33,18 +33,26 @@ func TestCreateRow(t *testing.T) { Versions: []string{"1.0.2", "2.0.1", "3.0.4"}, State: vulnerability.FixStateFixed.String(), }, + Risk: 87.2, VulnerabilityMetadata: models.VulnerabilityMetadata{ ID: "CVE-1999-0001", Namespace: "source-1", Description: "1999-01 description", - Severity: "Low", + Severity: "Medium", Cvss: []models.Cvss{ { Metrics: models.CvssMetrics{ - BaseScore: 4, + BaseScore: 7, }, - Vector: "another vector", - Version: "3.0", + Vector: "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:L/I:L/A:H", + Version: "3.1", + }, + }, + EPSS: []models.EPSS{ + { + CVE: "CVE-1999-0001", + EPSS: 0.3, + Percentile: 0.5, }, }, }, @@ -60,30 +68,43 @@ func TestCreateRow(t *testing.T) { }, }, } + + matchWithKev := match1 + matchWithKev.Vulnerability.KnownExploited = append(matchWithKev.Vulnerability.KnownExploited, models.KnownExploited{ + CVE: "CVE-1999-0001", + KnownRansomwareCampaignUse: "Known", + }) + cases := []struct { - name string - match models.Match - severitySuffix string - expectedRow []string + name string + match models.Match + extraAnnotation string + expectedRow []string }{ { - name: "create row for vulnerability", - match: match1, - severitySuffix: "", - expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "1.0.2, *2.0.1, 3.0.4", string(match1.Artifact.Type), match1.Vulnerability.ID, "Low"}, + name: "create row for vulnerability", + match: match1, + extraAnnotation: "", + expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "1.0.2, *2.0.1, 3.0.4", string(match1.Artifact.Type), match1.Vulnerability.ID, "Medium", "50.00", " 87.2"}, + }, + { + name: "create row for suppressed vulnerability", + match: match1, + extraAnnotation: appendSuppressed, + expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "1.0.2, *2.0.1, 3.0.4", string(match1.Artifact.Type), match1.Vulnerability.ID, "Medium", "50.00", " 87.2", "(suppressed)"}, }, { - name: "create row for suppressed vulnerability", - match: match1, - severitySuffix: appendSuppressed, - expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "1.0.2, *2.0.1, 3.0.4", string(match1.Artifact.Type), match1.Vulnerability.ID, "Low", "(suppressed)"}, + name: "create row for suppressed vulnerability + Kev", + match: matchWithKev, + extraAnnotation: appendSuppressed, + expectedRow: []string{match1.Artifact.Name, match1.Artifact.Version, "1.0.2, *2.0.1, 3.0.4", string(match1.Artifact.Type), match1.Vulnerability.ID, "Medium", "50.00", " 87.2", "(kev, suppressed)"}, }, } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) { p := NewPresenter(models.PresenterConfig{}, false) - row := p.newRow(testCase.match, testCase.severitySuffix, false) + row := p.newRow(testCase.match, testCase.extraAnnotation, false) cols := rows{row}.Render()[0] assert.Equal(t, testCase.expectedRow, cols) @@ -230,7 +251,7 @@ func TestRowsRender(t *testing.T) { result := rs.Render() expected := [][]string{ - {"pkg1", "1.0.0", "1.1.0", "os", "CVE-2023-1234", "critical"}, + {"pkg1", "1.0.0", "1.1.0", "os", "CVE-2023-1234", "critical", "75.00", " N/A"}, } if diff := cmp.Diff(expected, result); diff != "" { @@ -248,9 +269,9 @@ func TestRowsRender(t *testing.T) { result := rs.Render() expected := [][]string{ - {"pkgA", "1.0.0", "", "os", "CVE-2023-1234", "critical"}, - {"pkgB", "2.0.0", "(won't fix)", "os", "CVE-2023-5678", "high"}, - {"pkgC", "3.0.0", "3.1.0", "os", "CVE-2023-9012", "medium"}, + {"pkgA", "1.0.0", "", "os", "CVE-2023-1234", "critical", "75.00", " N/A"}, + {"pkgB", "2.0.0", "(won't fix)", "os", "CVE-2023-5678", "high", "75.00", " N/A"}, + {"pkgC", "3.0.0", "3.1.0", "os", "CVE-2023-9012", "medium", "75.00", " N/A"}, } if diff := cmp.Diff(expected, result); diff != "" { @@ -265,17 +286,15 @@ func TestRowsRender(t *testing.T) { result := rs.Render() expected := [][]string{ - {"pkg1", "1.0.0", "1.1.0", "os", "CVE-2023-1234", "critical"}, + {"pkg1", "1.0.0", "1.1.0", "os", "CVE-2023-1234", "critical", "75.00", " N/A"}, } if diff := cmp.Diff(expected, result); diff != "" { t.Errorf("Render() mismatch (-want +got):\n%s", diff) } - // should have 7 columns: name, version, fix, packageType, vulnID, severity - if len(result[0]) != 6 { - t.Errorf("Expected 7 columns, got %d", len(result[0])) - } + // expected columns: name, version, fix, packageType, vulnID, severity, epss, risk + assert.Len(t, result[0], 8) }) } @@ -290,6 +309,24 @@ func createTestRow(name, version, fix, pkgType, vulnID, severity string, fixStat VulnerabilityMetadata: models.VulnerabilityMetadata{ ID: vulnID, Severity: severity, + Cvss: []models.Cvss{ + { + Source: "nvd", + Type: "CVSS", + Version: "3.1", + Vector: "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:C/C:H/I:L/A:L", + Metrics: models.CvssMetrics{ + BaseScore: 7.2, + }, + }, + }, + EPSS: []models.EPSS{ + { + CVE: vulnID, + EPSS: 0.03, + Percentile: 0.75, + }, + }, }, }, Artifact: models.Package{ diff --git a/grype/vulnerability/metadata.go b/grype/vulnerability/metadata.go index 2032c7d9857..5351f2baa01 100644 --- a/grype/vulnerability/metadata.go +++ b/grype/vulnerability/metadata.go @@ -1,6 +1,7 @@ package vulnerability import ( + "strings" "time" ) @@ -14,6 +15,111 @@ type Metadata struct { Cvss []Cvss KnownExploited []KnownExploited EPSS []EPSS + + // calculated as-needed + risk float64 +} + +// RiskScore computes a basic quantitative risk by combining threat and severity. +// Threat is represented by epss (likelihood of exploitation), and severity by the cvss base score + string severity. +// Impact is currently fixed at 1 and may be integrated into the calculation in future versions. +// Raw risk is epss * (cvss / 10) * impact, then scaled to 0–100 for readability. +// If a vulnerability appears in the KEV list, apply an additional boost to reflect known exploitation. +// Known ransomware campaigns receive a further, distinct boost. +func (m *Metadata) RiskScore() float64 { + if m == nil { + return 0 + } + if m.risk != 0 { + return m.risk + } + m.risk = riskScore(*m) + return m.risk +} + +func riskScore(m Metadata) float64 { + return min(threat(m)*severity(m)*kevModifier(m), 1.0) * 100.0 +} + +func kevModifier(m Metadata) float64 { + if len(m.KnownExploited) > 0 { + for _, kev := range m.KnownExploited { + if strings.ToLower(kev.KnownRansomwareCampaignUse) == "known" { + // consider ransomware campaigns to be a greater kevModifier than other KEV threats + return 1.1 + } + } + return 1.05 // boost the final result, as if there is a greater kevModifier inherently from KEV threats + } + return 1.0 +} + +func threat(m Metadata) float64 { + if len(m.KnownExploited) > 0 { + // per the EPSS guidance, any evidence of exploitation in the wild (not just PoC) should be considered over EPSS data + return 1.0 + } + if len(m.EPSS) == 0 { + return 0.0 + } + return m.EPSS[0].EPSS +} + +// severity returns a 0-1 value, which is a combination of the string severity and the average of the cvss base scores. +// If there are no cvss scores, the string severity is used. Some vendors only update the string severity and not the +// cvss scores, so it's important to consider all sources. We are also not biasing towards any one source (multiple +// cvss scores won't over-weigh the string severity). +func severity(m Metadata) float64 { + // TODO: summarization should take a policy: prefer NVD over CNA or vice versa... + + stringSeverityScore := severityToScore(m.Severity) / 10.0 + avgBaseScore := average(validBaseScores(m.Cvss...)...) / 10.0 + if avgBaseScore == 0 { + return stringSeverityScore + } + return average(stringSeverityScore, avgBaseScore) +} + +func severityToScore(severity string) float64 { + // use the middle of the range for each severity + switch strings.ToLower(severity) { + case "negligible": + return 0.5 + case "low": + return 3.0 + case "medium": + return 5.0 + case "high": + return 7.5 + case "critical": + return 9.0 + } + // the severity value might be "unknown" or an unexpected value. These should not be lost + // in the noise and placed at the bottom of the list... instead we compromise to the middle of the list. + return 5.0 +} + +func validBaseScores(as ...Cvss) []float64 { + var out []float64 + for _, a := range as { + if a.Metrics.BaseScore == 0 { + // this is a mistake... base scores cannot be 0. Don't include this value and bring down the average + continue + } + out = append(out, a.Metrics.BaseScore) + } + return out +} + +func average(as ...float64) float64 { + if len(as) == 0 { + return 0 + } + sum := 0.0 + for _, a := range as { + sum += a + } + return sum / float64(len(as)) } type Cvss struct { diff --git a/grype/vulnerability/metadata_test.go b/grype/vulnerability/metadata_test.go new file mode 100644 index 00000000000..befcaa45a80 --- /dev/null +++ b/grype/vulnerability/metadata_test.go @@ -0,0 +1,457 @@ +package vulnerability + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestRiskScore(t *testing.T) { + tests := []struct { + name string + metadata Metadata + expected float64 + }{ + { + name: "nil metadata", + metadata: Metadata{}, + expected: 0, + }, + { + name: "already calculated risk", + metadata: Metadata{ + risk: 42.5, + }, + expected: 42.5, + }, + { + name: "no EPSS data, no KEV", + metadata: Metadata{ + Severity: "high", + Cvss: []Cvss{ + { + Metrics: CvssMetrics{ + BaseScore: 7.5, + }, + }, + }, + }, + expected: 0, // threat is 0 without EPSS or KEV + }, + { + name: "with EPSS data, no KEV", + metadata: Metadata{ + Severity: "high", + EPSS: []EPSS{ + { + EPSS: 0.5, + Percentile: 0.95, + }, + }, + Cvss: []Cvss{ + { + Metrics: CvssMetrics{ + BaseScore: 7.5, + }, + }, + }, + }, + expected: 37.5, // 0.5 * (7.5/10) * 1 * 100 + }, + { + name: "with KEV, no EPSS", + metadata: Metadata{ + Severity: "high", + KnownExploited: []KnownExploited{ + { + CVE: "CVE-2023-1234", + KnownRansomwareCampaignUse: "No", + }, + }, + Cvss: []Cvss{ + { + Metrics: CvssMetrics{ + BaseScore: 7.5, + }, + }, + }, + }, + expected: 78.75, // 1.0 * (7.5/10) * 1.05* 100 + }, + { + name: "with KEV ransomware", + metadata: Metadata{ + Severity: "high", + KnownExploited: []KnownExploited{ + { + CVE: "CVE-2023-1234", + KnownRansomwareCampaignUse: "Known", + }, + }, + Cvss: []Cvss{ + { + Metrics: CvssMetrics{ + BaseScore: 7.5, + }, + }, + }, + }, + expected: 82.5, // 1.0 * (7.5/10) * 1.1 * 100 + }, + { + name: "with severity string only", + metadata: Metadata{ + Severity: "critical", + EPSS: []EPSS{ + { + EPSS: 0.8, + Percentile: 0.99, + }, + }, + }, + expected: 72, // 0.8 * (9.0/10) * 1.0 * 100 + }, + { + name: "with multiple CVSS scores + string severity", + metadata: Metadata{ + Severity: "medium", + EPSS: []EPSS{ + { + EPSS: 0.6, + Percentile: 0.90, + }, + }, + Cvss: []Cvss{ + { + Source: "NVD", + Metrics: CvssMetrics{ + BaseScore: 6.5, + }, + }, + { + Source: "Vendor", + Metrics: CvssMetrics{ + BaseScore: 5.5, + }, + }, + }, + }, + expected: 33, // 0.6 * ( (((6.5+5.5)/2)+5)/2 /10) * 1.0 * 100 + }, + { + name: "with some invalid CVSS scores + string severity", + metadata: Metadata{ + Severity: "medium", + EPSS: []EPSS{ + { + EPSS: 0.4, + Percentile: 0.85, + }, + }, + Cvss: []Cvss{ + { + Source: "NVD", + Metrics: CvssMetrics{ + BaseScore: 0, // invalid, should be ignored + }, + }, + { + Source: "Vendor", + Metrics: CvssMetrics{ + BaseScore: 6.0, + }, + }, + }, + }, + expected: 22, // 0.4 * ((6.0+5)/2 /10) * 1.0 * 100 + }, + { + name: "unknown severity", + metadata: Metadata{ + Severity: "unknown", + EPSS: []EPSS{ + { + EPSS: 0.3, + Percentile: 0.80, + }, + }, + }, + expected: 15, // 0.3 * (5.0/10) * 1.0 * 100 + }, + { + name: "maximum risk clamp", + metadata: Metadata{ + Severity: "critical", + KnownExploited: []KnownExploited{ + { + CVE: "CVE-2023-1234", + KnownRansomwareCampaignUse: "Known", + }, + }, + Cvss: []Cvss{ + { + Metrics: CvssMetrics{ + BaseScore: 10.0, + }, + }, + }, + }, + expected: 100, // clamped to 100 as it would be 1.0 * 1.0 * 1.1 * 100 = 120 + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := tt.metadata.RiskScore() + assert.InDelta(t, tt.expected, result, 0.01, "RiskScore method returned incorrect value") + + // test the calculated value is cached + if tt.name != "already calculated risk" && tt.name != "nil metadata" { + require.InDelta(t, tt.expected, tt.metadata.risk, 0.01, "risk was not cached") + } + + // test the standalone function + if tt.name != "nil metadata" && tt.name != "already calculated risk" { + funcResult := riskScore(tt.metadata) + assert.InDelta(t, tt.expected, funcResult, 0.0001, "riskScore function returned incorrect value") + } + }) + } +} + +func TestSeverityToScore(t *testing.T) { + tests := []struct { + severity string + expected float64 + }{ + {"negligible", 0.5}, + {"NEGLIGIBLE", 0.5}, + {"low", 3.0}, + {"LOW", 3.0}, + {"medium", 5.0}, + {"MEDIUM", 5.0}, + {"high", 7.5}, + {"HIGH", 7.5}, + {"critical", 9.0}, + {"CRITICAL", 9.0}, + {"unknown", 5.0}, + {"", 5.0}, + {"something-else", 5.0}, + } + + for _, tt := range tests { + t.Run(tt.severity, func(t *testing.T) { + result := severityToScore(tt.severity) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestAverageCVSS(t *testing.T) { + tests := []struct { + name string + cvss []Cvss + expected float64 + }{ + { + name: "empty slice", + cvss: []Cvss{}, + expected: 0, + }, + { + name: "single valid score", + cvss: []Cvss{ + {Metrics: CvssMetrics{BaseScore: 7.5}}, + }, + expected: 7.5, + }, + { + name: "multiple valid scores", + cvss: []Cvss{ + {Metrics: CvssMetrics{BaseScore: 7.5}}, + {Metrics: CvssMetrics{BaseScore: 8.5}}, + {Metrics: CvssMetrics{BaseScore: 9.0}}, + }, + expected: 8.33333, + }, + { + name: "with invalid scores", + cvss: []Cvss{ + {Metrics: CvssMetrics{BaseScore: 0}}, // invalid + {Metrics: CvssMetrics{BaseScore: 7.5}}, + {Metrics: CvssMetrics{BaseScore: 0}}, // invalid + {Metrics: CvssMetrics{BaseScore: 8.5}}, + }, + expected: 8.0, + }, + { + name: "all invalid scores", + cvss: []Cvss{ + {Metrics: CvssMetrics{BaseScore: 0}}, + {Metrics: CvssMetrics{BaseScore: 0}}, + }, + expected: 0, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := average(validBaseScores(tt.cvss...)...) + assert.InDelta(t, tt.expected, result, 0.00001) + }) + } +} + +func TestThreat(t *testing.T) { + tests := []struct { + name string + metadata Metadata + expected float64 + }{ + { + name: "no EPSS, no KEV", + metadata: Metadata{}, + expected: 0, + }, + { + name: "with EPSS, no KEV", + metadata: Metadata{ + EPSS: []EPSS{ + {EPSS: 0.75}, + }, + }, + expected: 0.75, + }, + { + name: "with KEV, no EPSS", + metadata: Metadata{ + KnownExploited: []KnownExploited{ + {CVE: "CVE-2023-1234"}, + }, + }, + expected: 1.0, + }, + { + name: "with KEV and EPSS", + metadata: Metadata{ + EPSS: []EPSS{ + {EPSS: 0.5}, + }, + KnownExploited: []KnownExploited{ + {CVE: "CVE-2023-1234"}, + }, + }, + expected: 1.0, // KEV takes precedence + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := threat(tt.metadata) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestImpact(t *testing.T) { + tests := []struct { + name string + metadata Metadata + expected float64 + }{ + { + name: "no KEV", + metadata: Metadata{}, + expected: 1.0, + }, + { + name: "KEV without ransomware", + metadata: Metadata{ + KnownExploited: []KnownExploited{ + {KnownRansomwareCampaignUse: "No"}, + }, + }, + expected: 1.05, + }, + { + name: "KEV with ransomware", + metadata: Metadata{ + KnownExploited: []KnownExploited{ + {KnownRansomwareCampaignUse: "Known"}, + }, + }, + expected: 1.1, + }, + { + name: "KEV with case insensitive ransomware", + metadata: Metadata{ + KnownExploited: []KnownExploited{ + {KnownRansomwareCampaignUse: "KNOWN"}, + }, + }, + expected: 1.1, + }, + { + name: "multiple KEV entries, one with ransomware", + metadata: Metadata{ + KnownExploited: []KnownExploited{ + {KnownRansomwareCampaignUse: "No"}, + {KnownRansomwareCampaignUse: "Known"}, + }, + }, + expected: 1.1, // highest wins + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := kevModifier(tt.metadata) + assert.Equal(t, tt.expected, result) + }) + } +} + +func TestSeverity(t *testing.T) { + tests := []struct { + name string + metadata Metadata + expected float64 + }{ + { + name: "no CVSS, medium severity", + metadata: Metadata{ + Severity: "medium", + }, + expected: 0.5, + }, + { + name: "with CVSS + severity string", + metadata: Metadata{ + Severity: "medium", + Cvss: []Cvss{ + {Metrics: CvssMetrics{BaseScore: 8.0}}, + }, + }, + expected: 0.65, + }, + { + name: "multiple CVSS scores + severity string", + metadata: Metadata{ + Severity: "medium", + Cvss: []Cvss{ + {Metrics: CvssMetrics{BaseScore: 6.0}}, + {Metrics: CvssMetrics{BaseScore: 8.0}}, + }, + }, + expected: 0.6, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := severity(tt.metadata) + assert.InDelta(t, tt.expected, result, 0.00001) + }) + } +} From 130dc15b287499d781c6570f34647d41b7a6b35c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 11:53:02 -0400 Subject: [PATCH 083/109] chore(deps): bump gorm.io/gorm from 1.26.0 to 1.26.1 (#2644) Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.26.0 to 1.26.1. - [Release notes](https://github.com/go-gorm/gorm/releases) - [Commits](https://github.com/go-gorm/gorm/compare/v1.26.0...v1.26.1) --- updated-dependencies: - dependency-name: gorm.io/gorm dependency-version: 1.26.1 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 726ad9277cb..75430b7bf41 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( golang.org/x/time v0.11.0 golang.org/x/tools v0.32.0 gopkg.in/yaml.v3 v3.0.1 - gorm.io/gorm v1.26.0 + gorm.io/gorm v1.26.1 ) require ( diff --git a/go.sum b/go.sum index ef65d2a33f1..6bc66ec44e0 100644 --- a/go.sum +++ b/go.sum @@ -2400,8 +2400,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/gorm v1.26.0 h1:9lqQVPG5aNNS6AyHdRiwScAVnXHg/L/Srzx55G5fOgs= -gorm.io/gorm v1.26.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= +gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw= +gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= 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= From ea6a4b8bfe337357b3597852812d7bf68a28d953 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 11:53:09 -0400 Subject: [PATCH 084/109] chore(deps): bump golang.org/x/tools from 0.32.0 to 0.33.0 (#2643) Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.32.0 to 0.33.0. - [Release notes](https://github.com/golang/tools/releases) - [Commits](https://github.com/golang/tools/compare/v0.32.0...v0.33.0) --- updated-dependencies: - dependency-name: golang.org/x/tools dependency-version: 0.33.0 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 | 14 +++++++------- go.sum | 28 ++++++++++++++-------------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 75430b7bf41..76b4d8aebf2 100644 --- a/go.mod +++ b/go.mod @@ -67,7 +67,7 @@ require ( github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 golang.org/x/time v0.11.0 - golang.org/x/tools v0.32.0 + golang.org/x/tools v0.33.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/gorm v1.26.1 ) @@ -282,14 +282,14 @@ require ( go.opentelemetry.io/otel/trace v1.33.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect - golang.org/x/crypto v0.37.0 // indirect + golang.org/x/crypto v0.38.0 // indirect golang.org/x/mod v0.24.0 // indirect - golang.org/x/net v0.39.0 // indirect + golang.org/x/net v0.40.0 // indirect golang.org/x/oauth2 v0.25.0 // indirect - golang.org/x/sync v0.13.0 // indirect - golang.org/x/sys v0.32.0 // indirect - golang.org/x/term v0.31.0 // indirect - golang.org/x/text v0.24.0 // indirect + golang.org/x/sync v0.14.0 // indirect + golang.org/x/sys v0.33.0 // indirect + golang.org/x/term v0.32.0 // indirect + golang.org/x/text v0.25.0 // indirect golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect google.golang.org/api v0.215.0 // indirect google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect diff --git a/go.sum b/go.sum index 6bc66ec44e0..70d602c01b9 100644 --- a/go.sum +++ b/go.sum @@ -1673,8 +1673,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= -golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= +golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1808,8 +1808,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= -golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= -golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= +golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY= +golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds= 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= @@ -1862,8 +1862,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= -golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ= +golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= 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= @@ -1967,8 +1967,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= -golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= 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= @@ -1984,8 +1984,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= -golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= +golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg= +golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= 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= @@ -2007,8 +2007,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= -golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4= +golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA= 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= @@ -2083,8 +2083,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= -golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= -golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= +golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= +golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= 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 fef6504e9de2f4797ba4e0c95b9761639bbbf313 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 15:55:10 +0000 Subject: [PATCH 085/109] chore(deps): bump actions/setup-go from 5.4.0 to 5.5.0 (#2648) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.4.0 to 5.5.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/0aaccfd150d50ccaeb58ebd88d36e91967a5f35b...d35c59abb061a4a6fb18e82ac0862c26744d6ab5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: 5.5.0 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 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 4d91cd336b7..aad2ff2623b 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -56,7 +56,7 @@ jobs: ${{ runner.os }}-go- - name: Set correct version of Golang to use during CodeQL run - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 + uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 with: go-version: '1.21' check-latest: true From 88e921cfeba8e8b93a911433a63b580d5551a4aa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 8 May 2025 12:34:56 -0400 Subject: [PATCH 086/109] chore(deps): bump actions/setup-go in /.github/actions/bootstrap (#2649) Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.4.0 to 5.5.0. - [Release notes](https://github.com/actions/setup-go/releases) - [Commits](https://github.com/actions/setup-go/compare/0aaccfd150d50ccaeb58ebd88d36e91967a5f35b...d35c59abb061a4a6fb18e82ac0862c26744d6ab5) --- updated-dependencies: - dependency-name: actions/setup-go dependency-version: 5.5.0 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/actions/bootstrap/action.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/bootstrap/action.yaml b/.github/actions/bootstrap/action.yaml index 06b3fb4fd16..3a6a906469e 100644 --- a/.github/actions/bootstrap/action.yaml +++ b/.github/actions/bootstrap/action.yaml @@ -32,7 +32,7 @@ runs: using: "composite" steps: # note: go mod and build is automatically cached on default with v4+ - - uses: actions/setup-go@0aaccfd150d50ccaeb58ebd88d36e91967a5f35b # v5.4.0 + - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 if: inputs.go-version != '' with: go-version: ${{ inputs.go-version }} From 2a964c490eebc13c7c7d041892ce76538e0b93bb Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Fri, 9 May 2025 11:57:21 -0400 Subject: [PATCH 087/109] fix: fall back to fuzzy constraint units (#2651) If a given constraint unit cannot be parsed as its labeled type, try parsing it as a fuzzy version. In this way matching can be attempted even in the face of surprising upstream data. Signed-off-by: Will Murphy --- grype/version/constraint_expression.go | 17 +++- grype/version/constraint_expression_test.go | 90 +++++++++++++++++++++ grype/version/pep440_constraint.go | 2 +- 3 files changed, 105 insertions(+), 4 deletions(-) diff --git a/grype/version/constraint_expression.go b/grype/version/constraint_expression.go index 9870863b0f6..6fc97cc7865 100644 --- a/grype/version/constraint_expression.go +++ b/grype/version/constraint_expression.go @@ -7,6 +7,8 @@ import ( "text/scanner" ) +var ErrFallbackToFuzzy = fmt.Errorf("falling back to fuzzy version matching") + type constraintExpression struct { units [][]constraintUnit // only supports or'ing a group of and'ed groups comparators [][]Comparator // only supports or'ing a group of and'ed groups @@ -20,7 +22,7 @@ func newConstraintExpression(phrase string, genFn comparatorGenerator) (constrai orUnits := make([][]constraintUnit, len(orParts)) orComparators := make([][]Comparator, len(orParts)) - + var fuzzyErr error for orIdx, andParts := range orParts { andUnits := make([]constraintUnit, len(andParts)) andComparators := make([]Comparator, len(andParts)) @@ -36,7 +38,16 @@ func newConstraintExpression(phrase string, genFn comparatorGenerator) (constrai comparator, err := genFn(*unit) if err != nil { - return constraintExpression{}, fmt.Errorf("failed to create comparator for '%s': %w", unit, err) + // this is a version constraint that could not be parsed as its + // specified type. Try falling back to fuzzy matching so that + // a match can still be attempted. + comparator, err = newFuzzyComparator(*unit) + if err != nil { + return constraintExpression{}, fmt.Errorf("failed to create comparator for '%s': %w", unit, err) + } + // Tell the caller we had to fallback from the specified + // version constraint format + fuzzyErr = ErrFallbackToFuzzy } andComparators[andIdx] = comparator } @@ -48,7 +59,7 @@ func newConstraintExpression(phrase string, genFn comparatorGenerator) (constrai return constraintExpression{ units: orUnits, comparators: orComparators, - }, nil + }, fuzzyErr } func (c *constraintExpression) satisfied(other *Version) (bool, error) { diff --git a/grype/version/constraint_expression_test.go b/grype/version/constraint_expression_test.go index 664fa465443..5b0420ed343 100644 --- a/grype/version/constraint_expression_test.go +++ b/grype/version/constraint_expression_test.go @@ -1,6 +1,8 @@ package version import ( + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" "testing" "github.com/go-test/deep" @@ -86,3 +88,91 @@ func TestScanExpression(t *testing.T) { }) } } + +func TestNewConstraintExpression(t *testing.T) { + tests := []struct { + name string + phrase string + genFn comparatorGenerator + expected constraintExpression + wantErr error + }{ + { + name: "single valid constraint", + phrase: "<1.1.1", + genFn: newGolangComparator, + expected: constraintExpression{ + units: [][]constraintUnit{ + {constraintUnit{ + rangeOperator: LT, + version: "1.1.1", + }}, + }, + comparators: [][]Comparator{ + {mustGolangComparator(t, constraintUnit{ + rangeOperator: LT, + version: "1.1.1", + })}, + }, + }, + wantErr: nil, + }, + { + name: "fall back to fuzzy on invalid semver", + phrase: ">9.6.0b1", + genFn: newGolangComparator, + expected: constraintExpression{ + units: [][]constraintUnit{ + {constraintUnit{ + rangeOperator: GT, + version: "9.6.0b1", + }}, + }, + comparators: [][]Comparator{ + {mustFuzzyComparator(t, constraintUnit{ + rangeOperator: GT, + version: "9.6.0b1", + })}, + }, + }, + wantErr: ErrFallbackToFuzzy, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual, err := newConstraintExpression(test.phrase, test.genFn) + if test.wantErr != nil { + require.ErrorIs(t, err, test.wantErr) + } else { + require.NoError(t, err) + } + + opts := []cmp.Option{ + cmp.AllowUnexported(constraintExpression{}, + constraintUnit{}, golangVersion{}, fuzzyVersion{}, semanticVersion{}), + } + if diff := cmp.Diff(test.expected, actual, opts...); diff != "" { + t.Errorf("actual does not match expected, diff: %s", diff) + } + }) + } +} + +func mustGolangComparator(t *testing.T, unit constraintUnit) Comparator { + t.Helper() + c, err := newGolangComparator(unit) + if err != nil { + t.Fatal(err) + } + return c +} + +func mustFuzzyComparator(t *testing.T, unit constraintUnit) Comparator { + t.Helper() + c, err := newFuzzyComparator(unit) + if err != nil { + t.Fatal(err) + } + return c +} diff --git a/grype/version/pep440_constraint.go b/grype/version/pep440_constraint.go index cc0c0b319ea..4ea4c479355 100644 --- a/grype/version/pep440_constraint.go +++ b/grype/version/pep440_constraint.go @@ -44,7 +44,7 @@ func newPep440Constraint(raw string) (pep440Constraint, error) { constraints, err := newConstraintExpression(raw, newPep440Comparator) if err != nil { - return pep440Constraint{}, fmt.Errorf("unable to parse pep440 constrain phrase %w", err) + return pep440Constraint{}, fmt.Errorf("unable to parse pep440 constraint phrase %w", err) } return pep440Constraint{ From 487af67928065c0f221dd94dee32f7600ff02750 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, 9 May 2025 18:21:19 +0000 Subject: [PATCH 088/109] chore(deps): update anchore dependencies (#2650) * chore(deps): update anchore dependencies Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * account for metadata changes Signed-off-by: Alex Goodman --------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Alex Goodman Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com> Co-authored-by: Alex Goodman --- go.mod | 8 ++++---- go.sum | 18 ++++++++++-------- grype/db/v6/data.go | 3 +++ grype/pkg/package_test.go | 11 ++++++++++- .../snapshot/TestCycloneDxPresenterDir.golden | 14 +++++++------- .../TestCycloneDxPresenterImage.golden | 12 ++++++------ .../snapshot/TestJsonDirsPresenter.golden | 2 +- .../snapshot/TestJsonImgsPresenter.golden | 2 +- .../compare_sbom_input_vs_lib_test.go | 1 + 9 files changed, 43 insertions(+), 28 deletions(-) diff --git a/go.mod b/go.mod index 76b4d8aebf2..a69bb4c9efb 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 github.com/anchore/stereoscope v0.1.4 - github.com/anchore/syft v1.23.1 + github.com/anchore/syft v1.23.2-0.20250509082920-0b78186a97a0 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 @@ -90,8 +90,7 @@ require ( github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver v1.5.0 // indirect - github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.11.7 // indirect github.com/ProtonMail/go-crypto v1.1.6 // indirect @@ -156,7 +155,7 @@ require ( github.com/felixge/fgprof v0.9.5 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect - github.com/github/go-spdx/v2 v2.3.2 // indirect + github.com/github/go-spdx/v2 v2.3.3 // indirect github.com/gkampitakis/ciinfo v0.3.1 // indirect github.com/gkampitakis/go-diff v1.3.2 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect @@ -220,6 +219,7 @@ require ( github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 // indirect github.com/nwaples/rardecode v1.1.3 // indirect github.com/nwaples/rardecode/v2 v2.1.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect diff --git a/go.sum b/go.sum index 70d602c01b9..03fb3333d84 100644 --- a/go.sum +++ b/go.sum @@ -648,10 +648,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapp github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY= @@ -710,8 +708,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiE github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek= github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw= -github.com/anchore/syft v1.23.1 h1:QFWFMlwyJprl60RMrceSB6FJpZCsAQeWVLd9Ltzgwa0= -github.com/anchore/syft v1.23.1/go.mod h1:vDV0VBC601wHZ2nGuxqoDjfYsiu87WmE0w8HG3RDI6k= +github.com/anchore/syft v1.23.2-0.20250509082920-0b78186a97a0 h1:Jnsz2S3H6PtGM2A11vqyD6Iyl479lY2be9lNWGLjkuA= +github.com/anchore/syft v1.23.2-0.20250509082920-0b78186a97a0/go.mod h1:rD7NI0LzopzDuVe5SW+NOUAcbaRe8TYwwourGwBYZZk= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= @@ -946,8 +944,8 @@ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8 github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/github/go-spdx/v2 v2.3.2 h1:IfdyNHTqzs4zAJjXdVQfRnxt1XMfycXoHBE2Vsm1bjs= -github.com/github/go-spdx/v2 v2.3.2/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ= +github.com/github/go-spdx/v2 v2.3.3 h1:QI7evnHWEfWkT54eJwkoV/f3a0xD3gLlnVmT5wQG6LE= +github.com/github/go-spdx/v2 v2.3.3/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ= github.com/gkampitakis/ciinfo v0.3.1 h1:lzjbemlGI4Q+XimPg64ss89x8Mf3xihJqy/0Mgagapo= github.com/gkampitakis/ciinfo v0.3.1/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo= github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M= @@ -1364,6 +1362,10 @@ github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= +github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 h1:kpt9ZfKcm+EDG4s40hMwE//d5SBgDjUOrITReV2u4aA= +github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1/go.mod h1:qgCw4bBKZX8qMgGeEZzGFVT3notl42dBjNqO2jut0M0= +github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE= +github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8= github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc= github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0= github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U= diff --git a/grype/db/v6/data.go b/grype/db/v6/data.go index bbd1ac0979b..df4fbe34e2b 100644 --- a/grype/db/v6/data.go +++ b/grype/db/v6/data.go @@ -72,6 +72,9 @@ func KnownPackageSpecifierOverrides() []PackageSpecifierOverride { // jenkins plugins are a special case since they are always considered to be within the java ecosystem {Ecosystem: string(pkg.JenkinsPluginPkg), ReplacementEcosystem: ptr(string(pkg.JavaPkg))}, + + // legacy cases + {Ecosystem: "pecl", ReplacementEcosystem: ptr(string(pkg.PhpPeclPkg))}, } // remap package URL types to syft package types diff --git a/grype/pkg/package_test.go b/grype/pkg/package_test.go index f2950fac2f3..f6ae2421f00 100644 --- a/grype/pkg/package_test.go +++ b/grype/pkg/package_test.go @@ -694,7 +694,7 @@ func TestNew(t *testing.T) { }, }, { - name: "Php-pecl-entry", + name: "php-pecl-entry", syftPkg: syftPkg.Package{ Metadata: syftPkg.PhpPeclEntry{ Name: "a", @@ -703,6 +703,15 @@ func TestNew(t *testing.T) { }, }, }, + { + name: "php-pear-entry", + syftPkg: syftPkg.Package{ + Metadata: syftPkg.PhpPearEntry{ + Name: "a", + Version: "a", + }, + }, + }, { name: "lua-rocks-entry", syftPkg: syftPkg.Package{ diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden index 354a1daec75..45d6a15345b 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.6", - "serialNumber": "urn:uuid:8a92c7a6-641a-4fe8-8cb7-1636323af3d9", + "serialNumber": "urn:uuid:3ebe8aaa-da14-4729-b919-7e57658361e8", "version": 1, "metadata": { - "timestamp": "2025-04-09T12:16:17-04:00", + "timestamp": "2025-05-09T13:51:45-04:00", "tools": { "components": [ { @@ -19,7 +19,7 @@ "component": { "bom-ref": "163686ac6e30c752", "type": "file", - "name": "/var/folders/c0/4y79v5k56bz8v34chcmvq2k80000gp/T/TestCycloneDxPresenterDir3422104391/001" + "name": "/var/folders/c0/4y79v5k56bz8v34chcmvq2k80000gp/T/TestCycloneDxPresenterDir3160715530/001" } }, "components": [ @@ -57,7 +57,7 @@ ] }, { - "bom-ref": "pkg:deb/package-2@2.2.2?package-id=7bb53d560434bc7f", + "bom-ref": "pkg:deb/package-2@2.2.2?package-id=d4131374f6ed43cf", "type": "library", "name": "package-2", "version": "2.2.2", @@ -89,7 +89,7 @@ ], "vulnerabilities": [ { - "bom-ref": "urn:uuid:9179966d-ddc9-445e-83f4-a8fdfd6c4ef3", + "bom-ref": "urn:uuid:244fe426-e979-4e33-8dc4-e0d8883de4f6", "id": "CVE-1999-0001", "source": {}, "references": [ @@ -113,7 +113,7 @@ ] }, { - "bom-ref": "urn:uuid:fe57d7f2-08c1-4446-8424-0f9d2c1e2b13", + "bom-ref": "urn:uuid:8a90ec6f-64c4-468d-a8a8-4ee72d24c83d", "id": "CVE-1999-0002", "source": {}, "references": [ @@ -132,7 +132,7 @@ ], "affects": [ { - "ref": "pkg:deb/package-2@2.2.2?package-id=7bb53d560434bc7f" + "ref": "pkg:deb/package-2@2.2.2?package-id=d4131374f6ed43cf" } ] } diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden index 461383cb3a7..760e35f44ab 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.6", - "serialNumber": "urn:uuid:04f27880-9830-4cfa-b9a5-9e8b9b4fee04", + "serialNumber": "urn:uuid:792e3ebc-6cc7-4b3a-8fbb-bfb5532ba5c1", "version": 1, "metadata": { - "timestamp": "2025-04-09T12:16:17-04:00", + "timestamp": "2025-05-09T13:51:45-04:00", "tools": { "components": [ { @@ -58,7 +58,7 @@ ] }, { - "bom-ref": "pkg:deb/package-2@2.2.2?package-id=7bb53d560434bc7f", + "bom-ref": "pkg:deb/package-2@2.2.2?package-id=d4131374f6ed43cf", "type": "library", "name": "package-2", "version": "2.2.2", @@ -90,7 +90,7 @@ ], "vulnerabilities": [ { - "bom-ref": "urn:uuid:0f1e7161-0591-4f58-928e-184e8c8184c0", + "bom-ref": "urn:uuid:1565a551-e4bb-4012-8863-41407f2dedd3", "id": "CVE-1999-0001", "source": {}, "references": [ @@ -114,7 +114,7 @@ ] }, { - "bom-ref": "urn:uuid:757bf3c1-b03d-482b-b1a1-8b7a3fd5346f", + "bom-ref": "urn:uuid:702719fe-6a8c-4890-b65f-065fc25e15cf", "id": "CVE-1999-0002", "source": {}, "references": [ @@ -133,7 +133,7 @@ ], "affects": [ { - "ref": "pkg:deb/package-2@2.2.2?package-id=7bb53d560434bc7f" + "ref": "pkg:deb/package-2@2.2.2?package-id=d4131374f6ed43cf" } ] } diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden index f2eff6987df..23b6442b112 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden @@ -134,7 +134,7 @@ } ], "artifact": { - "id": "7bb53d560434bc7f", + "id": "d4131374f6ed43cf", "name": "package-2", "version": "2.2.2", "type": "deb", diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden index 1c2842a1f59..7ae3d3de88a 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden @@ -134,7 +134,7 @@ } ], "artifact": { - "id": "7bb53d560434bc7f", + "id": "d4131374f6ed43cf", "name": "package-2", "version": "2.2.2", "type": "deb", diff --git a/test/integration/compare_sbom_input_vs_lib_test.go b/test/integration/compare_sbom_input_vs_lib_test.go index 80e0db48ed6..25e5fe8a0e3 100644 --- a/test/integration/compare_sbom_input_vs_lib_test.go +++ b/test/integration/compare_sbom_input_vs_lib_test.go @@ -54,6 +54,7 @@ func TestCompareSBOMInputToLibResults(t *testing.T) { string(syftPkg.BinaryPkg), // these are removed due to overlap-by-file-ownership string(syftPkg.BitnamiPkg), string(syftPkg.PhpPeclPkg), + string(syftPkg.PhpPearPkg), string(syftPkg.RustPkg), string(syftPkg.KbPkg), string(syftPkg.DartPubPkg), From fafe4c3160958376790e8bc6b8f6880264934552 Mon Sep 17 00:00:00 2001 From: Weston Steimel Date: Mon, 12 May 2025 17:10:49 +0000 Subject: [PATCH 089/109] fix: adjust version prefix check when excluding overlapping packages (#2653) Adjust the prefix check to allow removing an owned package if either version is a prefix of the other. Signed-off-by: Weston Steimel --- grype/pkg/package.go | 4 ++-- grype/pkg/package_test.go | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/grype/pkg/package.go b/grype/pkg/package.go index 58ea7c8dd2d..6a58eef2a96 100644 --- a/grype/pkg/package.go +++ b/grype/pkg/package.go @@ -127,8 +127,8 @@ func excludePackage(comprehensiveDistroFeed bool, p syftPkg.Package, parent syft // python 3.9.2 binary // python3.9 3.9.2-1 deb - // If the version is not effectively the same, keep both - if !strings.HasPrefix(parent.Version, p.Version) { + // If the version is not approximately the same, keep both + if !strings.HasPrefix(parent.Version, p.Version) && !strings.HasPrefix(p.Version, parent.Version) { return false } diff --git a/grype/pkg/package_test.go b/grype/pkg/package_test.go index f6ae2421f00..1267b3eaa0f 100644 --- a/grype/pkg/package_test.go +++ b/grype/pkg/package_test.go @@ -995,6 +995,27 @@ func Test_RemovePackagesByOverlap(t *testing.T) { []string{"rpm:python3-rpm@4.14.3-26.el8 -> python:rpm@4.14.3"}), "amzn"), expectedPackages: []string{"rpm:python3-rpm@4.14.3-26.el8", "python:rpm@4.14.3"}, }, + { + name: "remove overlapping package when parent version is prefix of child version", + sbom: withDistro(catalogWithOverlaps( + []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5", "linux-kernel:linux-kernel@5.14.0-503.40.1.el9_5.x86_64+rt"}, + []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5 -> linux-kernel:linux-kernel@5.14.0-503.40.1.el9_5.x86_64+rt"}), "rhel"), + expectedPackages: []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5"}, + }, + { + name: "remove overlapping package when child version is prefix of parent version", + sbom: withDistro(catalogWithOverlaps( + []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5+rt", "linux-kernel:linux-kernel@5.14.0-503.40.1.el9_5"}, + []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5+rt -> linux-kernel:linux-kernel@5.14.0-503.40.1.el9_5"}), "rhel"), + expectedPackages: []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5+rt"}, + }, + { + name: "do not remove overlapping package when versions are not similar", + sbom: withDistro(catalogWithOverlaps( + []string{"rpm:kernel@5.14.0-503.40.1.el9_5", "linux-kernel:linux-kernel@6.17"}, + []string{"rpm:kernel@5.14.0-503.40.1.el9_5 -> linux-kernel:linux-kernel@6.17"}), "rhel"), + expectedPackages: []string{"rpm:kernel@5.14.0-503.40.1.el9_5", "linux-kernel:linux-kernel@6.17"}, + }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { From a7616a67c3b1636707563b0f83f12567c1c28fc4 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Mon, 12 May 2025 19:30:06 -0400 Subject: [PATCH 090/109] fix: include group/namespace across formats (#2645) Signed-off-by: Keith Zantow --- cmd/grype/cli/commands/root.go | 15 +- cmd/grype/cli/commands/root_test.go | 12 +- go.mod | 2 +- go.sum | 4 +- grype/deprecated.go | 4 +- grype/distro/distro.go | 39 ++ grype/distro/distro_test.go | 2 + .../packagemetadata/discover_type_names.go | 2 +- grype/pkg/context.go | 4 +- grype/pkg/package.go | 148 ++++++- grype/pkg/package_test.go | 16 +- grype/pkg/provider.go | 61 ++- grype/pkg/purl_provider.go | 248 +---------- grype/pkg/purl_provider_test.go | 409 ++---------------- grype/pkg/syft_provider.go | 18 +- grype/pkg/syft_sbom_provider.go | 122 +++--- grype/pkg/syft_sbom_provider_test.go | 189 +++++++- grype/presenter/internal/test_helpers.go | 6 +- grype/presenter/json/presenter_test.go | 6 +- grype/presenter/models/distribution.go | 19 +- grype/presenter/models/document_test.go | 10 +- grype/presenter/models/source.go | 4 +- grype/presenter/models/source_test.go | 4 +- grype/presenter/sarif/presenter.go | 4 +- grype/version/constraint_expression_test.go | 4 +- grype/vulnerability_matcher.go | 16 +- grype/vulnerability_matcher_test.go | 31 +- test/integration/match_by_image_test.go | 5 +- 28 files changed, 565 insertions(+), 839 deletions(-) diff --git a/cmd/grype/cli/commands/root.go b/cmd/grype/cli/commands/root.go index b4b8dd8f2fb..b2e64ed6f0d 100644 --- a/cmd/grype/cli/commands/root.go +++ b/cmd/grype/cli/commands/root.go @@ -12,6 +12,7 @@ import ( "github.com/anchore/clio" "github.com/anchore/grype/cmd/grype/cli/options" "github.com/anchore/grype/grype" + "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/grype/event" "github.com/anchore/grype/grype/event/parsers" "github.com/anchore/grype/grype/grypeerr" @@ -35,7 +36,6 @@ import ( "github.com/anchore/grype/internal/stringutil" "github.com/anchore/syft/syft" "github.com/anchore/syft/syft/cataloging" - "github.com/anchore/syft/syft/linux" syftPkg "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" ) @@ -261,15 +261,10 @@ func applyDistroHint(pkgs []pkg.Package, context *pkg.Context, opts *options.Gry if len(split) > 1 { v = split[1] } - context.Distro = &linux.Release{ - PrettyName: d, - Name: d, - ID: d, - IDLike: []string{ - d, - }, - Version: v, - VersionID: v, + var err error + context.Distro, err = distro.NewFromNameVersion(d, v) + if err != nil { + log.WithFields("distro", opts.Distro, "error", err).Warn("unable to parse distro") } } diff --git a/cmd/grype/cli/commands/root_test.go b/cmd/grype/cli/commands/root_test.go index bd1d27b80dc..e50d3408908 100644 --- a/cmd/grype/cli/commands/root_test.go +++ b/cmd/grype/cli/commands/root_test.go @@ -28,24 +28,24 @@ func Test_applyDistroHint(t *testing.T) { applyDistroHint([]pkg.Package{}, &ctx, &cfg) assert.NotNil(t, ctx.Distro) - assert.Equal(t, "alpine", ctx.Distro.Name) + assert.Equal(t, "alpine", ctx.Distro.Name()) assert.Equal(t, "3.10", ctx.Distro.Version) // does override an existing distro - cfg.Distro = "ubuntu:latest" + cfg.Distro = "ubuntu:24.04" applyDistroHint([]pkg.Package{}, &ctx, &cfg) assert.NotNil(t, ctx.Distro) - assert.Equal(t, "ubuntu", ctx.Distro.Name) - assert.Equal(t, "latest", ctx.Distro.Version) + assert.Equal(t, "ubuntu", ctx.Distro.Name()) + assert.Equal(t, "24.04", ctx.Distro.Version) // doesn't remove an existing distro when empty cfg.Distro = "" applyDistroHint([]pkg.Package{}, &ctx, &cfg) assert.NotNil(t, ctx.Distro) - assert.Equal(t, "ubuntu", ctx.Distro.Name) - assert.Equal(t, "latest", ctx.Distro.Version) + assert.Equal(t, "ubuntu", ctx.Distro.Name()) + assert.Equal(t, "24.04", ctx.Distro.Version) } func Test_getProviderConfig(t *testing.T) { diff --git a/go.mod b/go.mod index a69bb4c9efb..1ae7937ba62 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 github.com/anchore/stereoscope v0.1.4 - github.com/anchore/syft v1.23.2-0.20250509082920-0b78186a97a0 + github.com/anchore/syft v1.23.2-0.20250512173324-621d21eb04ca github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 diff --git a/go.sum b/go.sum index 03fb3333d84..c050d466770 100644 --- a/go.sum +++ b/go.sum @@ -708,8 +708,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiE github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek= github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw= -github.com/anchore/syft v1.23.2-0.20250509082920-0b78186a97a0 h1:Jnsz2S3H6PtGM2A11vqyD6Iyl479lY2be9lNWGLjkuA= -github.com/anchore/syft v1.23.2-0.20250509082920-0b78186a97a0/go.mod h1:rD7NI0LzopzDuVe5SW+NOUAcbaRe8TYwwourGwBYZZk= +github.com/anchore/syft v1.23.2-0.20250512173324-621d21eb04ca h1:GY1N+iXgj9rdibkf46vm+VQkASGGtW4NtDSNDAZSUuk= +github.com/anchore/syft v1.23.2-0.20250512173324-621d21eb04ca/go.mod h1:rD7NI0LzopzDuVe5SW+NOUAcbaRe8TYwwourGwBYZZk= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= diff --git a/grype/deprecated.go b/grype/deprecated.go index fc8ec72b29f..050974fef36 100644 --- a/grype/deprecated.go +++ b/grype/deprecated.go @@ -1,6 +1,7 @@ package grype import ( + "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/matcher" "github.com/anchore/grype/grype/pkg" @@ -8,7 +9,6 @@ import ( "github.com/anchore/grype/internal/log" "github.com/anchore/stereoscope/pkg/image" "github.com/anchore/syft/syft" - "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/source" ) @@ -33,7 +33,7 @@ func FindVulnerabilities(store vulnerability.Provider, userImageStr string, scop } // TODO: deprecated, will remove before v1.0.0 -func FindVulnerabilitiesForPackage(store vulnerability.Provider, d *linux.Release, matchers []match.Matcher, packages []pkg.Package) match.Matches { +func FindVulnerabilitiesForPackage(store vulnerability.Provider, d *distro.Distro, matchers []match.Matcher, packages []pkg.Package) match.Matches { exclusionProvider, _ := store.(match.ExclusionProvider) // TODO v5 is an exclusion provider, but v6 is not runner := VulnerabilityMatcher{ VulnerabilityProvider: store, diff --git a/grype/distro/distro.go b/grype/distro/distro.go index 13f95224366..50cf68855c8 100644 --- a/grype/distro/distro.go +++ b/grype/distro/distro.go @@ -6,6 +6,7 @@ import ( hashiVer "github.com/hashicorp/go-version" + "github.com/anchore/grype/internal/log" "github.com/anchore/syft/syft/linux" ) @@ -43,6 +44,13 @@ func New(t Type, version, label string, idLikes ...string) (*Distro, error) { } } + for i := range idLikes { + typ, ok := IDMapping[strings.TrimSpace(idLikes[i])] + if ok { + idLikes[i] = typ.String() + } + } + return &Distro{ Type: t, major: major, @@ -54,6 +62,35 @@ func New(t Type, version, label string, idLikes ...string) (*Distro, error) { }, nil } +// NewFromNameVersion creates a new Distro object derived from the provided name and version +func NewFromNameVersion(name, version string) (*Distro, error) { + var codename string + + // if there are no digits in the version, it is likely a codename + if !strings.ContainsAny(version, "0123456789") { + codename = version + version = "" + } + + typ := IDMapping[name] + if typ == "" { + typ = Type(name) + } + return New(typ, version, codename, string(typ)) +} + +// FromRelease attempts to get a distro from the linux release, only logging any errors +func FromRelease(linuxRelease *linux.Release) *Distro { + if linuxRelease == nil { + return nil + } + d, err := NewFromRelease(*linuxRelease) + if err != nil { + log.WithFields("error", err).Warn("unable to create distro from linux distribution") + } + return d +} + // NewFromRelease creates a new Distro object derived from a syft linux.Release object. func NewFromRelease(release linux.Release) (*Distro, error) { t := TypeFromRelease(release) @@ -105,6 +142,8 @@ func (d Distro) String() string { versionStr := "(version unknown)" if d.Version != "" { versionStr = d.Version + } else if d.Codename != "" { + versionStr = d.Codename } return fmt.Sprintf("%s %s", d.Type, versionStr) } diff --git a/grype/distro/distro_test.go b/grype/distro/distro_test.go index e399b7d2732..c9ca73e6972 100644 --- a/grype/distro/distro_test.go +++ b/grype/distro/distro_test.go @@ -29,10 +29,12 @@ func Test_NewDistroFromRelease(t *testing.T) { ID: "centos", VersionID: "8", Version: "7", + IDLike: []string{"rhel"}, }, expected: &Distro{ Type: CentOS, Version: "8", + IDLike: []string{"redhat"}, }, major: "8", minor: "", diff --git a/grype/internal/packagemetadata/discover_type_names.go b/grype/internal/packagemetadata/discover_type_names.go index 617e18d8585..a59748758c3 100644 --- a/grype/internal/packagemetadata/discover_type_names.go +++ b/grype/internal/packagemetadata/discover_type_names.go @@ -16,7 +16,7 @@ import ( var metadataExceptions = strset.New( "FileMetadata", - "PURLFileMetadata", + "SBOMFileMetadata", "PURLLiteralMetadata", "CPELiteralMetadata", ) diff --git a/grype/pkg/context.go b/grype/pkg/context.go index 5f46a6f9f9c..0279e24a401 100644 --- a/grype/pkg/context.go +++ b/grype/pkg/context.go @@ -1,11 +1,11 @@ package pkg import ( - "github.com/anchore/syft/syft/linux" + "github.com/anchore/grype/grype/distro" "github.com/anchore/syft/syft/source" ) type Context struct { Source *source.Description - Distro *linux.Release + Distro *distro.Distro } diff --git a/grype/pkg/package.go b/grype/pkg/package.go index 6a58eef2a96..abf6b244d30 100644 --- a/grype/pkg/package.go +++ b/grype/pkg/package.go @@ -3,15 +3,16 @@ package pkg import ( "fmt" "regexp" + "slices" "strings" "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/internal/log" "github.com/anchore/grype/internal/stringutil" + "github.com/anchore/packageurl-go" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/linux" syftPkg "github.com/anchore/syft/syft/pkg" cpes "github.com/anchore/syft/syft/pkg/cataloger/common/cpe" ) @@ -44,7 +45,7 @@ type Package struct { Metadata interface{} // This is NOT 1-for-1 the syft metadata! Only the select data needed for vulnerability matching } -func New(p syftPkg.Package) Package { +func New(p syftPkg.Package, enhancers ...Enhancer) Package { metadata, upstreams := dataFromPkg(p) licenseObjs := p.Licenses.ToSlice() @@ -57,7 +58,7 @@ func New(p syftPkg.Package) Package { licenses = []string{} } - return Package{ + out := Package{ ID: ID(p.ID()), Name: p.Name, Version: p.Version, @@ -70,13 +71,25 @@ func New(p syftPkg.Package) Package { Upstreams: upstreams, Metadata: metadata, } + + if len(enhancers) > 0 { + purl, err := packageurl.FromString(p.PURL) + if err != nil { + log.WithFields("purl", purl, "error", err).Debug("unable to parse PURL") + } + for _, e := range enhancers { + e(&out, purl, p) + } + } + + return out } -func FromCollection(catalog *syftPkg.Collection, config SynthesisConfig) []Package { - return FromPackages(catalog.Sorted(), config) +func FromCollection(catalog *syftPkg.Collection, config SynthesisConfig, enhancers ...Enhancer) []Package { + return FromPackages(catalog.Sorted(), config, enhancers...) } -func FromPackages(syftpkgs []syftPkg.Package, config SynthesisConfig) []Package { +func FromPackages(syftpkgs []syftPkg.Package, config SynthesisConfig, enhancers ...Enhancer) []Package { var pkgs []Package for _, p := range syftpkgs { if len(p.CPEs) == 0 { @@ -87,7 +100,7 @@ func FromPackages(syftpkgs []syftPkg.Package, config SynthesisConfig) []Package log.Debugf("no CPEs for package: %s", p) } } - pkgs = append(pkgs, New(p)) + pkgs = append(pkgs, New(p, enhancers...)) } return pkgs @@ -98,7 +111,7 @@ func (p Package) String() string { return fmt.Sprintf("Pkg(type=%s, name=%s, version=%s, upstreams=%d)", p.Type, p.Name, p.Version, len(p.Upstreams)) } -func removePackagesByOverlap(catalog *syftPkg.Collection, relationships []artifact.Relationship, distro *linux.Release) *syftPkg.Collection { +func removePackagesByOverlap(catalog *syftPkg.Collection, relationships []artifact.Relationship, distro *distro.Distro) *syftPkg.Collection { byOverlap := map[artifact.ID]artifact.Relationship{} for _, r := range relationships { if r.Type == artifact.OwnershipByFileOverlapRelationship { @@ -151,23 +164,23 @@ func excludePackage(comprehensiveDistroFeed bool, p syftPkg.Package, parent syft // distroFeedIsComprehensive returns true if the distro feed // is comprehensive enough that we can drop packages owned by distro packages // before matching. -func distroFeedIsComprehensive(distro *linux.Release) bool { +func distroFeedIsComprehensive(dst *distro.Distro) bool { // TODO: this mechanism should be re-examined once https://github.com/anchore/grype/issues/1426 // is addressed - if distro == nil { + if dst == nil { return false } - if distro.ID == "amzn" { + if dst.Type == distro.AmazonLinux { // AmazonLinux shows "like rhel" but is not an rhel clone // and does not have an exhaustive vulnerability feed. return false } for _, d := range comprehensiveDistros { - if strings.EqualFold(d, distro.ID) { + if strings.EqualFold(string(d), dst.Name()) { return true } - for _, n := range distro.IDLike { - if strings.EqualFold(d, n) { + for _, n := range dst.IDLike { + if strings.EqualFold(string(d), n) { return true } } @@ -177,13 +190,13 @@ func distroFeedIsComprehensive(distro *linux.Release) bool { // computed by: // sqlite3 vulnerability.db 'select distinct namespace from vulnerability where fix_state in ("wont-fix", "not-fixed") order by namespace;' | cut -d ':' -f 1 | sort | uniq -// then removing 'github' and replacing 'redhat' with 'rhel' -var comprehensiveDistros = []string{ - "azurelinux", - "debian", - "mariner", - "rhel", - "ubuntu", +// then removing 'github' +var comprehensiveDistros = []distro.Type{ + distro.Azure, + distro.Debian, + distro.Mariner, + distro.RedHat, + distro.Ubuntu, } func isOSPackage(p syftPkg.Package) bool { @@ -195,7 +208,7 @@ func isOSPackage(p syftPkg.Package) bool { } } -func dataFromPkg(p syftPkg.Package) (interface{}, []UpstreamPackage) { +func dataFromPkg(p syftPkg.Package) (any, []UpstreamPackage) { var metadata interface{} var upstreams []UpstreamPackage @@ -222,6 +235,7 @@ func dataFromPkg(p syftPkg.Package) (interface{}, []UpstreamPackage) { case syftPkg.JavaVMInstallation: metadata = javaVMDataFromPkg(p) } + return metadata, upstreams } @@ -409,3 +423,93 @@ func ByID(id ID, pkgs []Package) *Package { } return nil } + +func parseUpstream(pkgName string, value string, pkgType syftPkg.Type) []UpstreamPackage { + if pkgType == syftPkg.RpmPkg { + return handleSourceRPM(pkgName, value) + } + return handleDefaultUpstream(pkgName, value) +} + +func handleDefaultUpstream(pkgName string, value string) []UpstreamPackage { + fields := strings.Split(value, "@") + switch len(fields) { + case 2: + if fields[0] == pkgName { + return nil + } + return []UpstreamPackage{ + { + Name: fields[0], + Version: fields[1], + }, + } + case 1: + if fields[0] == pkgName { + return nil + } + return []UpstreamPackage{ + { + Name: fields[0], + }, + } + } + return nil +} + +func setUpstreamsFromPURL(out *Package, purl packageurl.PackageURL, syftPkg syftPkg.Package) { + if len(out.Upstreams) == 0 { + out.Upstreams = upstreamsFromPURL(purl, syftPkg.Type) + } +} + +// upstreamsFromPURL reads any additional data Grype can use, which is ignored by Syft's PURL conversion +func upstreamsFromPURL(purl packageurl.PackageURL, pkgType syftPkg.Type) (upstreams []UpstreamPackage) { + for _, qualifier := range purl.Qualifiers { + if qualifier.Key == syftPkg.PURLQualifierUpstream { + for _, newUpstream := range parseUpstream(purl.Name, qualifier.Value, pkgType) { + if slices.Contains(upstreams, newUpstream) { + continue + } + upstreams = append(upstreams, newUpstream) + } + } + } + return upstreams +} + +func setDistroFromPURL(out *Package, purl packageurl.PackageURL, _ syftPkg.Package) { + if out.Distro == nil { + out.Distro = distroFromPURL(purl) + } +} + +// distroFromPURL reads distro data for Grype can use, which is ignored by Syft's PURL conversion +func distroFromPURL(purl packageurl.PackageURL) (d *distro.Distro) { + var distroName, distroVersion string + + for _, qualifier := range purl.Qualifiers { + if qualifier.Key == syftPkg.PURLQualifierDistro { + fields := strings.SplitN(qualifier.Value, "-", 2) + distroName = fields[0] + if len(fields) > 1 { + distroVersion = fields[1] + } + } + } + + if distroName != "" { + var err error + d, err = distro.NewFromNameVersion(distroName, distroVersion) + if err != nil { + log.WithFields("purl", purl, "error", err).Debug("unable to create distro from a release") + d = nil + } + } + + return d +} + +type Enhancer func(out *Package, purl packageurl.PackageURL, pkg syftPkg.Package) + +var purlEnhancers = []Enhancer{setUpstreamsFromPURL, setDistroFromPURL} diff --git a/grype/pkg/package_test.go b/grype/pkg/package_test.go index 1267b3eaa0f..bb64df534f9 100644 --- a/grype/pkg/package_test.go +++ b/grype/pkg/package_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" + "github.com/anchore/grype/grype/distro" "github.com/anchore/syft/syft/artifact" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" @@ -983,35 +984,35 @@ func Test_RemovePackagesByOverlap(t *testing.T) { }, { name: "python bindings for system RPM install", - sbom: withDistro(catalogWithOverlaps( + sbom: withLinuxRelease(catalogWithOverlaps( []string{"rpm:python3-rpm@4.14.3-26.el8", "python:rpm@4.14.3"}, []string{"rpm:python3-rpm@4.14.3-26.el8 -> python:rpm@4.14.3"}), "rhel"), expectedPackages: []string{"rpm:python3-rpm@4.14.3-26.el8"}, }, { name: "amzn linux doesn't remove packages in this way", - sbom: withDistro(catalogWithOverlaps( + sbom: withLinuxRelease(catalogWithOverlaps( []string{"rpm:python3-rpm@4.14.3-26.el8", "python:rpm@4.14.3"}, []string{"rpm:python3-rpm@4.14.3-26.el8 -> python:rpm@4.14.3"}), "amzn"), expectedPackages: []string{"rpm:python3-rpm@4.14.3-26.el8", "python:rpm@4.14.3"}, }, { name: "remove overlapping package when parent version is prefix of child version", - sbom: withDistro(catalogWithOverlaps( + sbom: withLinuxRelease(catalogWithOverlaps( []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5", "linux-kernel:linux-kernel@5.14.0-503.40.1.el9_5.x86_64+rt"}, []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5 -> linux-kernel:linux-kernel@5.14.0-503.40.1.el9_5.x86_64+rt"}), "rhel"), expectedPackages: []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5"}, }, { name: "remove overlapping package when child version is prefix of parent version", - sbom: withDistro(catalogWithOverlaps( + sbom: withLinuxRelease(catalogWithOverlaps( []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5+rt", "linux-kernel:linux-kernel@5.14.0-503.40.1.el9_5"}, []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5+rt -> linux-kernel:linux-kernel@5.14.0-503.40.1.el9_5"}), "rhel"), expectedPackages: []string{"rpm:kernel-rt-core@5.14.0-503.40.1.el9_5+rt"}, }, { name: "do not remove overlapping package when versions are not similar", - sbom: withDistro(catalogWithOverlaps( + sbom: withLinuxRelease(catalogWithOverlaps( []string{"rpm:kernel@5.14.0-503.40.1.el9_5", "linux-kernel:linux-kernel@6.17"}, []string{"rpm:kernel@5.14.0-503.40.1.el9_5 -> linux-kernel:linux-kernel@6.17"}), "rhel"), expectedPackages: []string{"rpm:kernel@5.14.0-503.40.1.el9_5", "linux-kernel:linux-kernel@6.17"}, @@ -1019,7 +1020,8 @@ func Test_RemovePackagesByOverlap(t *testing.T) { } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - catalog := removePackagesByOverlap(test.sbom.Artifacts.Packages, test.sbom.Relationships, test.sbom.Artifacts.LinuxDistribution) + d := distro.FromRelease(test.sbom.Artifacts.LinuxDistribution) + catalog := removePackagesByOverlap(test.sbom.Artifacts.Packages, test.sbom.Relationships, d) pkgs := FromCollection(catalog, SynthesisConfig{}) var pkgNames []string for _, p := range pkgs { @@ -1100,7 +1102,7 @@ func catalogWithOverlaps(packages []string, overlaps []string) *sbom.SBOM { } } -func withDistro(s *sbom.SBOM, id string) *sbom.SBOM { +func withLinuxRelease(s *sbom.SBOM, id string) *sbom.SBOM { s.Artifacts.LinuxDistribution = &linux.Release{ ID: id, } diff --git a/grype/pkg/provider.go b/grype/pkg/provider.go index 165d490ad00..e06af83b23e 100644 --- a/grype/pkg/provider.go +++ b/grype/pkg/provider.go @@ -6,6 +6,7 @@ import ( "github.com/bmatcuk/doublestar/v2" + "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/internal/log" "github.com/anchore/syft/syft/file" "github.com/anchore/syft/syft/sbom" @@ -15,20 +16,17 @@ var errDoesNotProvide = fmt.Errorf("cannot provide packages from the given sourc // Provide a set of packages and context metadata describing where they were sourced from. func Provide(userInput string, config ProviderConfig) ([]Package, Context, *sbom.SBOM, error) { - packages, ctx, s, err := syftSBOMProvider(userInput, config) - if !errors.Is(err, errDoesNotProvide) { - if len(config.Exclusions) > 0 { - var exclusionsErr error - packages, exclusionsErr = filterPackageExclusions(packages, config.Exclusions) - if exclusionsErr != nil { - return nil, ctx, s, exclusionsErr - } - } - log.WithFields("input", userInput).Trace("interpreting input as an SBOM document") - return packages, ctx, s, err + packages, ctx, s, err := provide(userInput, config) + if err != nil { + return nil, Context{}, nil, err } + setContextDistro(packages, &ctx) + return packages, ctx, s, nil +} - packages, ctx, s, err = purlProvider(userInput) +// Provide a set of packages and context metadata describing where they were sourced from. +func provide(userInput string, config ProviderConfig) ([]Package, Context, *sbom.SBOM, error) { + packages, ctx, s, err := purlProvider(userInput, config) if !errors.Is(err, errDoesNotProvide) { log.WithFields("input", userInput).Trace("interpreting input as one or more PURLs") return packages, ctx, s, err @@ -40,6 +38,19 @@ func Provide(userInput string, config ProviderConfig) ([]Package, Context, *sbom return packages, ctx, s, err } + packages, ctx, s, err = syftSBOMProvider(userInput, config) + if !errors.Is(err, errDoesNotProvide) { + if len(config.Exclusions) > 0 { + var exclusionsErr error + packages, exclusionsErr = filterPackageExclusions(packages, config.Exclusions) + if exclusionsErr != nil { + return nil, ctx, s, exclusionsErr + } + } + log.WithFields("input", userInput).Trace("interpreting input as an SBOM document") + return packages, ctx, s, err + } + log.WithFields("input", userInput).Trace("passing input to syft for interpretation") return syftProvider(userInput, config) } @@ -92,3 +103,29 @@ func locationMatches(location file.Location, exclusion string) (bool, error) { } return matchesRealPath || matchesVirtualPath, nil } + +func setContextDistro(packages []Package, ctx *Context) { + if ctx.Distro != nil { + return + } + var singleDistro *distro.Distro + for _, p := range packages { + if p.Distro == nil { + continue + } + if singleDistro == nil { + singleDistro = p.Distro + continue + } + if singleDistro.Type != p.Distro.Type || + singleDistro.Version != p.Distro.Version || + singleDistro.Codename != p.Distro.Codename { + return + } + } + + // if there is one distro (with one version) represented, use that + if singleDistro != nil { + ctx.Distro = singleDistro + } +} diff --git a/grype/pkg/purl_provider.go b/grype/pkg/purl_provider.go index 8fa83e21a04..740f34960fe 100644 --- a/grype/pkg/purl_provider.go +++ b/grype/pkg/purl_provider.go @@ -1,21 +1,11 @@ package pkg import ( - "bufio" "fmt" "io" - "os" "strings" - "github.com/scylladb/go-set/strset" - - "github.com/anchore/go-homedir" - "github.com/anchore/grype/grype/distro" - "github.com/anchore/grype/internal/log" - "github.com/anchore/packageurl-go" - "github.com/anchore/syft/syft/cpe" - "github.com/anchore/syft/syft/linux" - "github.com/anchore/syft/syft/pkg" + "github.com/anchore/syft/syft/format" "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" ) @@ -23,41 +13,28 @@ import ( const ( purlInputPrefix = "purl:" singlePurlInputPrefix = "pkg:" - cpesQualifierKey = "cpes" ) type PURLLiteralMetadata struct { PURL string } -type PURLFileMetadata struct { - Path string -} - -func purlProvider(userInput string) ([]Package, Context, *sbom.SBOM, error) { +func purlProvider(userInput string, config ProviderConfig) ([]Package, Context, *sbom.SBOM, error) { reader, ctx, err := getPurlReader(userInput) if err != nil { return nil, Context{}, nil, err } - return decodePurlsFromReader(reader, ctx) + s, _, _, err := format.Decode(reader) + if s == nil { + return nil, Context{}, nil, fmt.Errorf("unable to decode purl: %w", err) + } + + return FromCollection(s.Artifacts.Packages, config.SynthesisConfig, purlEnhancers...), ctx, s, nil } func getPurlReader(userInput string) (r io.Reader, ctx Context, err error) { - switch { - case strings.HasPrefix(userInput, purlInputPrefix): - path := strings.TrimPrefix(userInput, purlInputPrefix) - ctx.Source = &source.Description{ - Metadata: PURLFileMetadata{ - Path: path, - }, - } - file, err := openPurlFile(path) - if err != nil { - return nil, ctx, err - } - return file, ctx, nil - case strings.HasPrefix(userInput, singlePurlInputPrefix): + if strings.HasPrefix(userInput, singlePurlInputPrefix) { ctx.Source = &source.Description{ Metadata: PURLLiteralMetadata{ PURL: userInput, @@ -67,210 +44,3 @@ func getPurlReader(userInput string) (r io.Reader, ctx Context, err error) { } return nil, ctx, errDoesNotProvide } - -func openPurlFile(path string) (*os.File, error) { - expandedPath, err := homedir.Expand(path) - if err != nil { - return nil, fmt.Errorf("unable to open purls: %w", err) - } - - f, err := os.Open(expandedPath) - if err != nil { - return nil, fmt.Errorf("unable to open file %s: %w", expandedPath, err) - } - - return f, nil -} - -func createLinuxRelease(name string, version string) *linux.Release { - var codename string - - // if there are no digits in the version, it is likely a codename - if !strings.ContainsAny(version, "0123456789") { - codename = version - version = "" - } - - return &linux.Release{ - Name: name, - ID: name, - IDLike: []string{name}, - Version: version, - VersionCodename: codename, - } -} - -func decodePurlsFromReader(reader io.Reader, ctx Context) ([]Package, Context, *sbom.SBOM, error) { - scanner := bufio.NewScanner(reader) - var packages []Package - var syftPkgs []pkg.Package - - distros := make(map[string]*strset.Set) - for scanner.Scan() { - rawLine := scanner.Text() - p, syftPkg, distroName, distroVersion, err := purlToPackage(rawLine) - if err != nil { - return nil, Context{}, nil, err - } - if distroName != "" { - if _, ok := distros[distroName]; !ok { - distros[distroName] = strset.New() - } - distros[distroName].Add(distroVersion) - } - if p != nil { - packages = append(packages, *p) - } - if syftPkg != nil { - syftPkgs = append(syftPkgs, *syftPkg) - } - } - - if err := scanner.Err(); err != nil { - return nil, Context{}, nil, err - } - - s := &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(syftPkgs...), - }, - } - // Do we have multiple purls - // purl litteral <-- - // purl file <-- FileMetadata - - // if there is one distro (with one version) represented, use that - if len(distros) == 1 { - for name, versions := range distros { - if versions.Size() == 1 { - version := versions.List()[0] - ctx.Distro = createLinuxRelease(name, version) - s.Artifacts.LinuxDistribution = createLinuxRelease(name, version) - } - } - } - - return packages, ctx, s, nil -} - -//nolint:funlen -func purlToPackage(rawLine string) (*Package, *pkg.Package, string, string, error) { - purl, err := packageurl.FromString(rawLine) - if err != nil { - return nil, nil, "", "", fmt.Errorf("unable to decode purl %s: %w", rawLine, err) - } - - var cpes []cpe.CPE - var upstreams []UpstreamPackage - var distroName, distroVersion string - epoch := "0" - - pkgType := pkg.TypeByName(purl.Type) - - for _, qualifier := range purl.Qualifiers { - switch qualifier.Key { - case cpesQualifierKey: - rawCpes := strings.Split(qualifier.Value, ",") - for _, rawCpe := range rawCpes { - c, err := cpe.New(rawCpe, "") - if err != nil { - return nil, nil, "", "", fmt.Errorf("unable to decode cpe %s in purl %s: %w", rawCpe, rawLine, err) - } - cpes = append(cpes, c) - } - case pkg.PURLQualifierEpoch: - epoch = qualifier.Value - case pkg.PURLQualifierUpstream: - upstreams = append(upstreams, parseUpstream(purl.Name, qualifier.Value, pkgType)...) - case pkg.PURLQualifierDistro: - name, version := parseDistroQualifier(qualifier.Value) - if name != "" && version != "" { - distroName = name - distroVersion = version - } - } - } - - version := purl.Version - if purl.Type == packageurl.TypeRPM && !strings.HasPrefix(purl.Version, fmt.Sprintf("%s:", epoch)) { - version = fmt.Sprintf("%s:%s", epoch, purl.Version) - } - - name := purl.Name - if pkgType == pkg.GoModulePkg && purl.Namespace != "" { - name = purl.Namespace + "/" + name - } - - syftPkg := pkg.Package{ - Name: name, - Version: version, - Type: pkgType, - CPEs: cpes, - PURL: purl.String(), - Language: pkg.LanguageByName(purl.Type), - } - - syftPkg.SetID() - - distribution, err := distro.NewFromRelease(*createLinuxRelease(distroName, distroVersion)) - if err != nil { - log.Trace("Unable to create Distro from a release: %s", err) - distribution = nil - } - - return &Package{ - ID: ID(purl.String()), - CPEs: cpes, - Name: name, - Version: version, - Type: pkgType, - Language: pkg.LanguageByName(purl.Type), - PURL: purl.String(), - Upstreams: upstreams, - Distro: distribution, - }, &syftPkg, distroName, distroVersion, nil -} - -func parseDistroQualifier(value string) (string, string) { - fields := strings.SplitN(value, "-", 2) - switch len(fields) { - case 2: - return fields[0], fields[1] - case 1: - return fields[0], "" - } - return "", "" -} - -func parseUpstream(pkgName string, value string, pkgType pkg.Type) []UpstreamPackage { - if pkgType == pkg.RpmPkg { - return handleSourceRPM(pkgName, value) - } - return handleDefaultUpstream(pkgName, value) -} - -func handleDefaultUpstream(pkgName string, value string) []UpstreamPackage { - fields := strings.Split(value, "@") - switch len(fields) { - case 2: - if fields[0] == pkgName { - return nil - } - return []UpstreamPackage{ - { - Name: fields[0], - Version: fields[1], - }, - } - case 1: - if fields[0] == pkgName { - return nil - } - return []UpstreamPackage{ - { - Name: fields[0], - }, - } - } - return nil -} diff --git a/grype/pkg/purl_provider_test.go b/grype/pkg/purl_provider_test.go index a2dd50583dc..a63d55735ba 100644 --- a/grype/pkg/purl_provider_test.go +++ b/grype/pkg/purl_provider_test.go @@ -8,10 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/grype/grype/distro" - "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/pkg" - "github.com/anchore/syft/syft/sbom" "github.com/anchore/syft/syft/source" ) @@ -21,7 +18,6 @@ func Test_PurlProvider(t *testing.T) { userInput string context Context pkgs []Package - sbom *sbom.SBOM wantErr require.ErrorAssertionFunc }{ { @@ -42,26 +38,15 @@ func Test_PurlProvider(t *testing.T) { PURL: "pkg:apk/curl@7.61.1", }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "curl", - Version: "7.61.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/curl@7.61.1", - }), - }, - }, }, { name: "os with codename", userInput: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit", context: Context{ - Distro: &linux.Release{ - Name: "debian", - ID: "debian", - IDLike: []string{"debian"}, - VersionCodename: "jessie", // important! + Distro: &distro.Distro{ + Type: "debian", + IDLike: []string{"debian"}, + Codename: "jessie", // important! }, Source: &source.Description{ Metadata: PURLLiteralMetadata{ @@ -83,22 +68,6 @@ func Test_PurlProvider(t *testing.T) { }, }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "sysv-rc", - Version: "2.88dsf-59", - Type: pkg.DebPkg, - PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit", - }), - LinuxDistribution: &linux.Release{ - Name: "debian", - ID: "debian", - IDLike: []string{"debian"}, - VersionCodename: "jessie", - }, - }, - }, }, { name: "default upstream", @@ -123,16 +92,6 @@ func Test_PurlProvider(t *testing.T) { }, }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "libcrypto3", - Version: "3.3.2", - Type: pkg.ApkPkg, - PURL: "pkg:apk/libcrypto3@3.3.2?upstream=openssl", - }), - }, - }, }, { name: "upstream with version", @@ -158,25 +117,14 @@ func Test_PurlProvider(t *testing.T) { }, }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "libcrypto3", - Version: "3.3.2", - Type: pkg.ApkPkg, - PURL: "pkg:apk/libcrypto3@3.3.2?upstream=openssl%403.2.1", - }), - }, - }, }, { name: "upstream for source RPM", userInput: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm", context: Context{ - Distro: &linux.Release{ - Name: "rhel", - ID: "rhel", - IDLike: []string{"rhel"}, + Distro: &distro.Distro{ + Type: "redhat", + IDLike: []string{"redhat"}, Version: "8.10", }, Source: &source.Description{ @@ -188,10 +136,10 @@ func Test_PurlProvider(t *testing.T) { pkgs: []Package{ { Name: "systemd-x", - Version: "0:239-82.el8_10.2", + Version: "239-82.el8_10.2", Type: pkg.RpmPkg, PURL: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm", - Distro: &distro.Distro{Type: distro.RedHat, Version: "8.10", Codename: "", IDLike: []string{"rhel"}}, + Distro: &distro.Distro{Type: distro.RedHat, Version: "8.10", Codename: "", IDLike: []string{"redhat"}}, Upstreams: []UpstreamPackage{ { Name: "systemd", @@ -200,31 +148,14 @@ func Test_PurlProvider(t *testing.T) { }, }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "systemd-x", - Version: "0:239-82.el8_10.2", - Type: pkg.RpmPkg, - PURL: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm", - }), - LinuxDistribution: &linux.Release{ - Name: "rhel", - ID: "rhel", - IDLike: []string{"rhel"}, - Version: "8.10", - }, - }, - }, }, { name: "RPM with epoch", userInput: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm", context: Context{ - Distro: &linux.Release{ - Name: "rhel", - ID: "rhel", - IDLike: []string{"rhel"}, + Distro: &distro.Distro{ + Type: "redhat", + IDLike: []string{"redhat"}, Version: "8.10", }, Source: &source.Description{ @@ -239,7 +170,7 @@ func Test_PurlProvider(t *testing.T) { Version: "1:1.12.8-26.el8", Type: pkg.RpmPkg, PURL: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm", - Distro: &distro.Distro{Type: distro.RedHat, Version: "8.10", Codename: "", IDLike: []string{"rhel"}}, + Distro: &distro.Distro{Type: distro.RedHat, Version: "8.10", Codename: "", IDLike: []string{"redhat"}}, Upstreams: []UpstreamPackage{ { Name: "dbus", @@ -248,104 +179,13 @@ func Test_PurlProvider(t *testing.T) { }, }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "dbus-common", - Version: "1:1.12.8-26.el8", - Type: pkg.RpmPkg, - PURL: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm", - }), - LinuxDistribution: &linux.Release{ - Name: "rhel", - ID: "rhel", - IDLike: []string{"rhel"}, - Version: "8.10", - }, - }, - }, - }, - { - name: "takes multiple purls", - userInput: "purl:test-fixtures/purl/valid-purl.txt", - context: Context{ - Distro: &linux.Release{ - Name: "debian", - ID: "debian", - IDLike: []string{"debian"}, - Version: "8", - }, - Source: &source.Description{ - Metadata: PURLFileMetadata{ - Path: "test-fixtures/purl/valid-purl.txt", - }, - }, - }, - pkgs: []Package{ - { - Name: "sysv-rc", - Version: "2.88dsf-59", - Type: pkg.DebPkg, - PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-8&upstream=sysvinit", - Distro: &distro.Distro{Type: distro.Debian, Version: "8", Codename: "", IDLike: []string{"debian"}}, - Upstreams: []UpstreamPackage{ - { - Name: "sysvinit", - }, - }, - }, - { - Name: "ant", - Version: "1.10.8", - Type: pkg.JavaPkg, - PURL: "pkg:maven/org.apache.ant/ant@1.10.8", - }, - { - Name: "log4j-core", - Version: "2.14.1", - Type: pkg.JavaPkg, - PURL: "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1", - }, - }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection( - pkg.Package{ - Name: "sysv-rc", - Version: "2.88dsf-59", - Type: pkg.DebPkg, - PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-8&upstream=sysvinit", - }, - pkg.Package{ - Name: "ant", - Version: "1.10.8", - Type: pkg.JavaPkg, - Language: pkg.Java, - PURL: "pkg:maven/org.apache.ant/ant@1.10.8", - }, - pkg.Package{ - Name: "log4j-core", - Version: "2.14.1", - Type: pkg.JavaPkg, - Language: pkg.Java, - PURL: "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1", - }), - LinuxDistribution: &linux.Release{ - Name: "debian", - ID: "debian", - IDLike: []string{"debian"}, - Version: "8", - }, - }, - }, }, { name: "infer context when distro is present for single purl", userInput: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3", context: Context{ - Distro: &linux.Release{ - Name: "alpine", - ID: "alpine", + Distro: &distro.Distro{ + Type: "alpine", IDLike: []string{"alpine"}, Version: "3.20.3", }, @@ -364,22 +204,6 @@ func Test_PurlProvider(t *testing.T) { Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "curl", - Version: "7.61.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3", - }), - LinuxDistribution: &linux.Release{ - Name: "alpine", - ID: "alpine", - IDLike: []string{"alpine"}, - Version: "3.20.3", - }, - }, - }, }, { name: "include namespace in name when purl is type Golang", @@ -397,17 +221,6 @@ func Test_PurlProvider(t *testing.T) { PURL: "pkg:golang/k8s.io/ingress-nginx@v1.11.2", }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "k8s.io/ingress-nginx", - Version: "v1.11.2", - Type: pkg.GoModulePkg, - Language: pkg.Go, - PURL: "pkg:golang/k8s.io/ingress-nginx@v1.11.2", - }), - }, - }, }, { name: "include complex namespace in name when purl is type Golang", @@ -425,17 +238,6 @@ func Test_PurlProvider(t *testing.T) { PURL: "pkg:golang/github.com/wazuh/wazuh@v4.5.0", }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "github.com/wazuh/wazuh", - Version: "v4.5.0", - Type: pkg.GoModulePkg, - PURL: "pkg:golang/github.com/wazuh/wazuh@v4.5.0", - Language: pkg.Go, - }), - }, - }, }, { name: "do not include namespace when given blank input blank", @@ -453,149 +255,12 @@ func Test_PurlProvider(t *testing.T) { PURL: "pkg:golang/wazuh@v4.5.0", }, }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "wazuh", - Version: "v4.5.0", - Type: pkg.GoModulePkg, - PURL: "pkg:golang/wazuh@v4.5.0", - Language: pkg.Go, - }), - }, - }, }, { - name: "infer context when distro is present for multiple similar purls", - userInput: "purl:test-fixtures/purl/homogeneous-os.txt", - context: Context{ - Distro: &linux.Release{ - Name: "alpine", - ID: "alpine", - IDLike: []string{"alpine"}, - Version: "3.20.3", - }, - Source: &source.Description{ - Metadata: PURLFileMetadata{ - Path: "test-fixtures/purl/homogeneous-os.txt", - }, - }, - }, - pkgs: []Package{ - { - Name: "openssl", - Version: "3.2.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/openssl@3.2.1?arch=aarch64&distro=alpine-3.20.3", - Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, - }, - { - Name: "curl", - Version: "7.61.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3", - Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, - }, - }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "openssl", - Version: "3.2.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/openssl@3.2.1?arch=aarch64&distro=alpine-3.20.3", - }, - pkg.Package{ - Name: "curl", - Version: "7.61.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3", - }), - LinuxDistribution: &linux.Release{ - Name: "alpine", - ID: "alpine", - IDLike: []string{"alpine"}, - Version: "3.20.3", - }, - }, - }, - }, - { - name: "different distro info in purls does not infer context", - userInput: "purl:test-fixtures/purl/different-os.txt", - context: Context{ - // important: no distro info inferred - Source: &source.Description{ - Metadata: PURLFileMetadata{ - Path: "test-fixtures/purl/different-os.txt", - }, - }, - }, - pkgs: []Package{ - { - Name: "openssl", - Version: "3.2.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/openssl@3.2.1?arch=aarch64&distro=alpine-3.20.3", - Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, - }, - { - Name: "curl", - Version: "7.61.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.2", - Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.2", Codename: "", IDLike: []string{"alpine"}}, - }, - }, - sbom: &sbom.SBOM{ - Artifacts: sbom.Artifacts{ - Packages: pkg.NewCollection(pkg.Package{ - Name: "openssl", - Version: "3.2.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/openssl@3.2.1?arch=aarch64&distro=alpine-3.20.3", - }, - pkg.Package{ - Name: "curl", - Version: "7.61.1", - Type: pkg.ApkPkg, - PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.2", - }), - }, - }, - }, - { - name: "fails on path with nonexistant file", - userInput: "purl:tttt/empty.txt", - wantErr: require.Error, - }, - { - name: "fails on invalid path", - userInput: "purl:~&&", - wantErr: require.Error, - }, - { - name: "allow empty purl file", - userInput: "purl:test-fixtures/purl/empty.json", - sbom: &sbom.SBOM{}, - context: Context{ - Source: &source.Description{ - Metadata: PURLFileMetadata{ - Path: "test-fixtures/purl/empty.json", - }, - }, - }, - }, - { - name: "fails on invalid purl in file", + name: "fails on purl list input", userInput: "purl:test-fixtures/purl/invalid-purl.txt", wantErr: require.Error, }, - { - name: "fails on invalid cpe in file", - userInput: "purl:test-fixtures/purl/invalid-cpe.txt", - wantErr: require.Error, - }, { name: "invalid prefix", userInput: "dir:test-fixtures/purl", @@ -603,23 +268,14 @@ func Test_PurlProvider(t *testing.T) { }, } - opts := []cmp.Option{ - cmpopts.IgnoreFields(Package{}, "ID", "Locations", "Licenses", "Metadata", "Language", "CPEs"), - cmpopts.IgnoreUnexported(distro.Distro{}), - } - - syftPkgOpts := []cmp.Option{ - cmpopts.IgnoreFields(pkg.Package{}, "id"), - cmpopts.IgnoreUnexported(pkg.Package{}, file.LocationSet{}, pkg.LicenseSet{}), - } - for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { if tc.wantErr == nil { tc.wantErr = require.NoError } - packages, ctx, gotSBOM, err := purlProvider(tc.userInput) + packages, ctx, _, err := purlProvider(tc.userInput, ProviderConfig{}) + setContextDistro(packages, &ctx) tc.wantErr(t, err) if err != nil { @@ -627,35 +283,20 @@ func Test_PurlProvider(t *testing.T) { return } - if d := cmp.Diff(tc.context, ctx, opts...); d != "" { + if d := cmp.Diff(tc.context, ctx, diffOpts...); d != "" { t.Errorf("unexpected context (-want +got):\n%s", d) } require.Len(t, packages, len(tc.pkgs)) for idx, expected := range tc.pkgs { - if d := cmp.Diff(expected, packages[idx], opts...); d != "" { + if d := cmp.Diff(expected, packages[idx], diffOpts...); d != "" { t.Errorf("unexpected context (-want +got):\n%s", d) } } - - gotSyftPkgs := gotSBOM.Artifacts.Packages.Sorted() - wantSyftPkgs := tc.sbom.Artifacts.Packages.Sorted() - require.Equal(t, len(gotSyftPkgs), len(wantSyftPkgs)) - for idx, wantPkg := range wantSyftPkgs { - if d := cmp.Diff(wantPkg, gotSyftPkgs[idx], syftPkgOpts...); d != "" { - t.Errorf("unexpected Syft Pkg (-want +got):\n%s", d) - } - } - - wantSyftDistro := tc.sbom.Artifacts.LinuxDistribution - gotDistro := gotSBOM.Artifacts.LinuxDistribution - if wantSyftDistro == nil { - require.Nil(t, gotDistro) - return - } - - if d := cmp.Diff(wantSyftDistro, gotDistro); d != "" { - t.Errorf("unexpected Syft Distro (-want +got):\n%s", d) - } }) } } + +var diffOpts = []cmp.Option{ + cmpopts.IgnoreFields(Package{}, "ID", "Locations", "Licenses", "Language", "CPEs"), + cmpopts.IgnoreUnexported(distro.Distro{}), +} diff --git a/grype/pkg/syft_provider.go b/grype/pkg/syft_provider.go index 8cfaec2de1f..9bd8a609cd1 100644 --- a/grype/pkg/syft_provider.go +++ b/grype/pkg/syft_provider.go @@ -5,6 +5,7 @@ import ( "errors" "github.com/anchore/go-collections" + "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/internal/log" "github.com/anchore/stereoscope" "github.com/anchore/stereoscope/pkg/image" @@ -19,14 +20,7 @@ func syftProvider(userInput string, config ProviderConfig) ([]Package, Context, if err != nil { return nil, Context{}, nil, err } - - defer func() { - if src != nil { - if err := src.Close(); err != nil { - log.Tracef("unable to close source: %+v", err) - } - } - }() + defer log.CloseAndLogError(src, "syft source") s, err := syft.CreateSBOM(context.Background(), src, config.SBOMOptions) if err != nil { @@ -37,14 +31,16 @@ func syftProvider(userInput string, config ProviderConfig) ([]Package, Context, return nil, Context{}, nil, errors.New("no SBOM provided") } - pkgCatalog := removePackagesByOverlap(s.Artifacts.Packages, s.Relationships, s.Artifacts.LinuxDistribution) - srcDescription := src.Describe() + d := distro.FromRelease(s.Artifacts.LinuxDistribution) + + pkgCatalog := removePackagesByOverlap(s.Artifacts.Packages, s.Relationships, d) + packages := FromCollection(pkgCatalog, config.SynthesisConfig) pkgCtx := Context{ Source: &srcDescription, - Distro: s.Artifacts.LinuxDistribution, + Distro: d, } return packages, pkgCtx, s, nil diff --git a/grype/pkg/syft_sbom_provider.go b/grype/pkg/syft_sbom_provider.go index 4c7b88ba7a1..f2ebf9bbd48 100644 --- a/grype/pkg/syft_sbom_provider.go +++ b/grype/pkg/syft_sbom_provider.go @@ -11,66 +11,70 @@ import ( "github.com/gabriel-vasile/mimetype" "github.com/anchore/go-homedir" + "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/internal" "github.com/anchore/grype/internal/log" "github.com/anchore/syft/syft/format" + "github.com/anchore/syft/syft/format/syftjson" "github.com/anchore/syft/syft/sbom" ) +type SBOMFileMetadata struct { + Path string +} + func syftSBOMProvider(userInput string, config ProviderConfig) ([]Package, Context, *sbom.SBOM, error) { - s, err := getSBOM(userInput) + s, fmtID, path, err := getSBOM(userInput) if err != nil { return nil, Context{}, nil, err } - catalog := removePackagesByOverlap(s.Artifacts.Packages, s.Relationships, s.Artifacts.LinuxDistribution) + src := s.Source + if src.Metadata == nil && path != "" { + src.Metadata = SBOMFileMetadata{ + Path: path, + } + } + + d := distro.FromRelease(s.Artifacts.LinuxDistribution) - return FromCollection(catalog, config.SynthesisConfig), Context{ - Source: &s.Source, - Distro: s.Artifacts.LinuxDistribution, - }, s, nil -} + catalog := removePackagesByOverlap(s.Artifacts.Packages, s.Relationships, d) -func newInputInfo(scheme, contentTye string) *inputInfo { - return &inputInfo{ - Scheme: scheme, - ContentType: contentTye, + var enhancers []Enhancer + if fmtID != syftjson.ID { + enhancers = purlEnhancers } -} -type inputInfo struct { - ContentType string - Scheme string + return FromCollection(catalog, config.SynthesisConfig, enhancers...), Context{ + Source: &src, + Distro: d, + }, s, nil } -func getSBOM(userInput string) (*sbom.SBOM, error) { - reader, err := getSBOMReader(userInput) +func getSBOM(userInput string) (*sbom.SBOM, sbom.FormatID, string, error) { + reader, path, err := getSBOMReader(userInput) if err != nil { - return nil, err + return nil, "", path, err } + s, fmtID, err := readSBOM(reader) + return s, fmtID, path, err +} + +func readSBOM(reader io.ReadSeeker) (*sbom.SBOM, sbom.FormatID, error) { s, fmtID, _, err := format.Decode(reader) if err != nil { - return nil, fmt.Errorf("unable to decode sbom: %w", err) + return nil, "", fmt.Errorf("unable to decode sbom: %w", err) } if fmtID == "" || s == nil { - return nil, errDoesNotProvide + return nil, "", errDoesNotProvide } - return s, nil + return s, fmtID, nil } -func getSBOMReader(userInput string) (r io.ReadSeeker, err error) { - r, _, err = extractReaderAndInfo(userInput) - if err != nil { - return nil, err - } - - return r, nil -} - -func extractReaderAndInfo(userInput string) (io.ReadSeeker, *inputInfo, error) { +func getSBOMReader(userInput string) (io.ReadSeeker, string, error) { switch { // the order of cases matter case userInput == "": @@ -78,44 +82,39 @@ func extractReaderAndInfo(userInput string) (io.ReadSeeker, *inputInfo, error) { // options from the CLI, otherwise we should not assume there is any valid input from stdin. r, err := stdinReader() if err != nil { - return nil, nil, err + return nil, "", err } return decodeStdin(r) + case explicitlySpecifyingPurlList(userInput): + filepath := strings.TrimPrefix(userInput, purlInputPrefix) + return openFile(filepath) + case explicitlySpecifyingSBOM(userInput): filepath := strings.TrimPrefix(userInput, "sbom:") - return parseSBOM("sbom", filepath) + return openFile(filepath) case isPossibleSBOM(userInput): - return parseSBOM("", userInput) + return openFile(userInput) default: - return nil, nil, errDoesNotProvide + return nil, "", errDoesNotProvide } } -func parseSBOM(scheme, path string) (io.ReadSeeker, *inputInfo, error) { - r, err := openFile(path) - if err != nil { - return nil, nil, err - } - info := newInputInfo(scheme, "sbom") - return r, info, nil -} - -func decodeStdin(r io.Reader) (io.ReadSeeker, *inputInfo, error) { +func decodeStdin(r io.Reader) (io.ReadSeeker, string, error) { b, err := io.ReadAll(r) if err != nil { - return nil, nil, fmt.Errorf("failed reading stdin: %w", err) + return nil, "", fmt.Errorf("failed reading stdin: %w", err) } reader := bytes.NewReader(b) _, err = reader.Seek(0, io.SeekStart) if err != nil { - return nil, nil, fmt.Errorf("failed to parse stdin: %w", err) + return nil, "", fmt.Errorf("failed to parse stdin: %w", err) } - return reader, newInputInfo("", "sbom"), nil + return reader, "", nil } func stdinReader() (io.Reader, error) { @@ -131,37 +130,26 @@ func stdinReader() (io.Reader, error) { return os.Stdin, nil } -func closeFile(f *os.File) { - if f == nil { - return - } - - err := f.Close() - if err != nil { - log.Warnf("failed to close file %s: %v", f.Name(), err) - } -} - -func openFile(path string) (*os.File, error) { +func openFile(path string) (io.ReadSeekCloser, string, error) { expandedPath, err := homedir.Expand(path) if err != nil { - return nil, fmt.Errorf("unable to open SBOM: %w", err) + return nil, path, fmt.Errorf("unable to open SBOM: %w", err) } f, err := os.Open(expandedPath) if err != nil { - return nil, fmt.Errorf("unable to open file %s: %w", expandedPath, err) + return nil, path, fmt.Errorf("unable to open file %s: %w", expandedPath, err) } - return f, nil + return f, path, nil } func isPossibleSBOM(userInput string) bool { - f, err := openFile(userInput) + f, path, err := openFile(userInput) if err != nil { return false } - defer closeFile(f) + defer log.CloseAndLogError(f, path) mType, err := mimetype.DetectReader(f) if err != nil { @@ -185,3 +173,7 @@ func isAncestorOfMimetype(mType *mimetype.MIME, expected string) bool { func explicitlySpecifyingSBOM(userInput string) bool { return strings.HasPrefix(userInput, "sbom:") } + +func explicitlySpecifyingPurlList(userInput string) bool { + return strings.HasPrefix(userInput, purlInputPrefix) +} diff --git a/grype/pkg/syft_sbom_provider_test.go b/grype/pkg/syft_sbom_provider_test.go index 2d2e48614d1..5d18d06570c 100644 --- a/grype/pkg/syft_sbom_provider_test.go +++ b/grype/pkg/syft_sbom_provider_test.go @@ -1,15 +1,19 @@ package pkg import ( + "slices" "strings" "testing" "github.com/go-test/deep" + "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/anchore/grype/grype/distro" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/linux" + "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) @@ -219,8 +223,8 @@ func TestParseSyftJSON(t *testing.T) { }, }, }, - Distro: &linux.Release{ - Name: "alpine", + Distro: &distro.Distro{ + Type: "alpine", Version: "3.12.0", }, }, @@ -337,9 +341,184 @@ var springImageTestCase = struct { RepoDigests: []string{"springio/gs-spring-boot-docker@sha256:39c2ffc784f5f34862e22c1f2ccdbcb62430736114c13f60111eabdb79decb08"}, }, }, - Distro: &linux.Release{ - Name: "debian", + Distro: &distro.Distro{ + Type: "debian", Version: "9", }, }, } + +func Test_PurlList(t *testing.T) { + tests := []struct { + name string + userInput string + context Context + pkgs []Package + wantErr require.ErrorAssertionFunc + }{ + { + name: "takes multiple purls", + userInput: "purl:test-fixtures/purl/valid-purl.txt", + context: Context{ + Distro: &distro.Distro{ + Type: "debian", + IDLike: []string{"debian"}, + Version: "8", + }, + Source: &source.Description{ + Metadata: SBOMFileMetadata{ + Path: "test-fixtures/purl/valid-purl.txt", + }, + }, + }, + pkgs: []Package{ + { + Name: "ant", + Version: "1.10.8", + Type: pkg.JavaPkg, + PURL: "pkg:maven/org.apache.ant/ant@1.10.8", + Metadata: JavaMetadata{ + PomArtifactID: "ant", + PomGroupID: "org.apache.ant", + }, + }, + { + Name: "log4j-core", + Version: "2.14.1", + Type: pkg.JavaPkg, + PURL: "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1", + Metadata: JavaMetadata{ + PomArtifactID: "log4j-core", + PomGroupID: "org.apache.logging.log4j", + }, + }, + { + Name: "sysv-rc", + Version: "2.88dsf-59", + Type: pkg.DebPkg, + PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-8&upstream=sysvinit", + Distro: &distro.Distro{Type: distro.Debian, Version: "8", Codename: "", IDLike: []string{"debian"}}, + Upstreams: []UpstreamPackage{ + { + Name: "sysvinit", + }, + }, + }, + }, + }, + { + name: "infer context when distro is present for multiple similar purls", + userInput: "purl:test-fixtures/purl/homogeneous-os.txt", + context: Context{ + Distro: &distro.Distro{ + Type: "alpine", + IDLike: []string{"alpine"}, + Version: "3.20.3", + }, + Source: &source.Description{ + Metadata: SBOMFileMetadata{ + Path: "test-fixtures/purl/homogeneous-os.txt", + }, + }, + }, + pkgs: []Package{ + { + Name: "openssl", + Version: "3.2.1", + Type: pkg.ApkPkg, + PURL: "pkg:apk/openssl@3.2.1?arch=aarch64&distro=alpine-3.20.3", + Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, + }, + { + Name: "curl", + Version: "7.61.1", + Type: pkg.ApkPkg, + PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3", + Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, + }, + }, + }, + { + name: "different distro info in purls does not infer context", + userInput: "purl:test-fixtures/purl/different-os.txt", + context: Context{ + // important: no distro info inferred + Source: &source.Description{ + Metadata: SBOMFileMetadata{ + Path: "test-fixtures/purl/different-os.txt", + }, + }, + }, + pkgs: []Package{ + { + Name: "openssl", + Version: "3.2.1", + Type: pkg.ApkPkg, + PURL: "pkg:apk/openssl@3.2.1?arch=aarch64&distro=alpine-3.20.3", + Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.3", Codename: "", IDLike: []string{"alpine"}}, + }, + { + Name: "curl", + Version: "7.61.1", + Type: pkg.ApkPkg, + PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.2", + Distro: &distro.Distro{Type: distro.Alpine, Version: "3.20.2", Codename: "", IDLike: []string{"alpine"}}, + }, + }, + }, + { + name: "fails on path with nonexistant file", + userInput: "purl:tttt/empty.txt", + wantErr: require.Error, + }, + { + name: "fails on invalid path", + userInput: "purl:~&&", + wantErr: require.Error, + }, + { + name: "fails for empty purl file", + userInput: "purl:test-fixtures/purl/empty.json", + wantErr: require.Error, + }, + { + name: "fails on invalid purl in file", + userInput: "purl:test-fixtures/purl/invalid-purl.txt", + wantErr: require.Error, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.wantErr == nil { + tc.wantErr = require.NoError + } + + packages, ctx, _, err := Provide(tc.userInput, ProviderConfig{}) + + tc.wantErr(t, err) + if err != nil { + require.Nil(t, packages) + return + } + + if d := cmp.Diff(tc.context, ctx, diffOpts...); d != "" { + t.Errorf("unexpected context (-want +got):\n%s", d) + } + require.Len(t, packages, len(tc.pkgs)) + + slices.SortFunc(packages, func(a, b Package) int { + return strings.Compare(a.Name, b.Name) + }) + slices.SortFunc(tc.pkgs, func(a, b Package) int { + return strings.Compare(a.Name, b.Name) + }) + + for idx, expected := range tc.pkgs { + if d := cmp.Diff(expected, packages[idx], diffOpts...); d != "" { + t.Errorf("unexpected context (-want +got):\n%s", d) + } + } + }) + } +} diff --git a/grype/presenter/internal/test_helpers.go b/grype/presenter/internal/test_helpers.go index aab61adc688..aa2b528abb8 100644 --- a/grype/presenter/internal/test_helpers.go +++ b/grype/presenter/internal/test_helpers.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/clio" + "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/pkg" "github.com/anchore/grype/grype/presenter/models" @@ -15,7 +16,6 @@ import ( "github.com/anchore/stereoscope/pkg/image" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/linux" syftPkg "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/sbom" syftSource "github.com/anchore/syft/syft/source" @@ -506,8 +506,8 @@ func generateContext(t *testing.T, scheme SyftSource) pkg.Context { return pkg.Context{ Source: &desc, - Distro: &linux.Release{ - Name: "centos", + Distro: &distro.Distro{ + Type: "centos", IDLike: []string{ "centos", }, diff --git a/grype/presenter/json/presenter_test.go b/grype/presenter/json/presenter_test.go index 3910b55ce9e..dc2929ae518 100644 --- a/grype/presenter/json/presenter_test.go +++ b/grype/presenter/json/presenter_test.go @@ -12,11 +12,11 @@ import ( "github.com/anchore/clio" "github.com/anchore/go-testutils" + "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/pkg" "github.com/anchore/grype/grype/presenter/internal" "github.com/anchore/grype/grype/presenter/models" - "github.com/anchore/syft/syft/linux" "github.com/anchore/syft/syft/source" ) @@ -85,8 +85,8 @@ func TestEmptyJsonPresenter(t *testing.T) { ctx := pkg.Context{ Source: &source.Description{}, - Distro: &linux.Release{ - ID: "centos", + Distro: &distro.Distro{ + Type: "centos", IDLike: []string{"rhel"}, Version: "8.0", }, diff --git a/grype/presenter/models/distribution.go b/grype/presenter/models/distribution.go index e105a8dac79..a8b9d792afa 100644 --- a/grype/presenter/models/distribution.go +++ b/grype/presenter/models/distribution.go @@ -2,8 +2,6 @@ package models import ( "github.com/anchore/grype/grype/distro" - "github.com/anchore/grype/internal/log" - "github.com/anchore/syft/syft/linux" ) // distribution provides information about a detected Linux distribution. @@ -14,24 +12,11 @@ type distribution struct { } // newDistribution creates a struct with the Linux distribution to be represented in JSON. -func newDistribution(r *linux.Release) distribution { - if r == nil { +func newDistribution(d *distro.Distro) distribution { + if d == nil { return distribution{} } - // attempt to use the strong distro type (like the matchers do) - d, err := distro.NewFromRelease(*r) - if err != nil { - log.Warnf("unable to determine linux distribution: %+v", err) - - // as a fallback use the raw release information - return distribution{ - Name: r.ID, - Version: r.VersionID, - IDLike: cleanIDLike(r.IDLike), - } - } - return distribution{ Name: d.Name(), Version: d.Version, diff --git a/grype/presenter/models/document_test.go b/grype/presenter/models/document_test.go index d81f0dc144f..0cb95916935 100644 --- a/grype/presenter/models/document_test.go +++ b/grype/presenter/models/document_test.go @@ -7,10 +7,10 @@ import ( "github.com/stretchr/testify/assert" "github.com/anchore/clio" + "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/pkg" "github.com/anchore/grype/grype/vulnerability" - "github.com/anchore/syft/syft/linux" syftPkg "github.com/anchore/syft/syft/pkg" syftSource "github.com/anchore/syft/syft/source" ) @@ -75,8 +75,8 @@ func TestPackagesAreSorted(t *testing.T) { Source: &syftSource.Description{ Metadata: syftSource.DirectoryMetadata{}, }, - Distro: &linux.Release{ - ID: "centos", + Distro: &distro.Distro{ + Type: "centos", IDLike: []string{"rhel"}, Version: "8.0", }, @@ -136,8 +136,8 @@ func TestFixSuggestedVersion(t *testing.T) { Source: &syftSource.Description{ Metadata: syftSource.DirectoryMetadata{}, }, - Distro: &linux.Release{ - ID: "centos", + Distro: &distro.Distro{ + Type: "centos", IDLike: []string{"rhel"}, Version: "8.0", }, diff --git a/grype/presenter/models/source.go b/grype/presenter/models/source.go index 6bbb1fe6158..f21c0674f2f 100644 --- a/grype/presenter/models/source.go +++ b/grype/presenter/models/source.go @@ -15,9 +15,9 @@ type source struct { // newSource creates a new source object to be represented into JSON. func newSource(src syftSource.Description) (source, error) { switch m := src.Metadata.(type) { - case pkg.PURLFileMetadata: + case pkg.SBOMFileMetadata: return source{ - Type: "purl-file", + Type: "sbom-file", Target: m.Path, }, nil case pkg.PURLLiteralMetadata: diff --git a/grype/presenter/models/source_test.go b/grype/presenter/models/source_test.go index 325b772a4e8..851fdf9a076 100644 --- a/grype/presenter/models/source_test.go +++ b/grype/presenter/models/source_test.go @@ -65,12 +65,12 @@ func TestNewSource(t *testing.T) { { name: "purl-file", metadata: syftSource.Description{ - Metadata: pkg.PURLFileMetadata{ + Metadata: pkg.SBOMFileMetadata{ Path: "/path/to/purls.txt", }, }, expected: source{ - Type: "purl-file", + Type: "sbom-file", Target: "/path/to/purls.txt", }, }, diff --git a/grype/presenter/sarif/presenter.go b/grype/presenter/sarif/presenter.go index ac8d0b2843c..4bd6a3cc765 100644 --- a/grype/presenter/sarif/presenter.go +++ b/grype/presenter/sarif/presenter.go @@ -407,8 +407,8 @@ func (p Presenter) resultMessage(m models.Match) sarif.Message { src = fmt.Sprintf("at: %s", path) case pkg.PURLLiteralMetadata: src = fmt.Sprintf("from purl literal %q", meta.PURL) - case pkg.PURLFileMetadata: - src = fmt.Sprintf("from purl file %s", meta.Path) + case pkg.SBOMFileMetadata: + src = fmt.Sprintf("from SBOM file %s", meta.Path) } message := fmt.Sprintf("A %s vulnerability in %s package: %s, version %s was found %s", severityText(m), m.Artifact.Type, m.Artifact.Name, m.Artifact.Version, src) diff --git a/grype/version/constraint_expression_test.go b/grype/version/constraint_expression_test.go index 5b0420ed343..4f5e8ba40f6 100644 --- a/grype/version/constraint_expression_test.go +++ b/grype/version/constraint_expression_test.go @@ -1,11 +1,11 @@ package version import ( - "github.com/google/go-cmp/cmp" - "github.com/stretchr/testify/require" "testing" "github.com/go-test/deep" + "github.com/google/go-cmp/cmp" + "github.com/stretchr/testify/require" ) func TestScanExpression(t *testing.T) { diff --git a/grype/vulnerability_matcher.go b/grype/vulnerability_matcher.go index e0889ae50e7..0df6da3ca63 100644 --- a/grype/vulnerability_matcher.go +++ b/grype/vulnerability_matcher.go @@ -20,7 +20,6 @@ import ( "github.com/anchore/grype/grype/vulnerability" "github.com/anchore/grype/internal/bus" "github.com/anchore/grype/internal/log" - "github.com/anchore/syft/syft/linux" syftPkg "github.com/anchore/syft/syft/pkg" ) @@ -139,7 +138,7 @@ func (m *VulnerabilityMatcher) mergeIgnoredMatches(allIgnoredMatches ...[]match. //nolint:funlen func (m *VulnerabilityMatcher) searchDBForMatches( - release *linux.Release, + d *distro.Distro, packages []pkg.Package, progressMonitor *monitorWriter, ) (match.Matches, error) { @@ -147,19 +146,6 @@ func (m *VulnerabilityMatcher) searchDBForMatches( var allIgnored []match.IgnoredMatch matcherIndex, defaultMatcher := newMatcherIndex(m.Matchers) - var d *distro.Distro - if release != nil { - var err error - d, err = distro.NewFromRelease(*release) - if err != nil { - log.Warnf("unable to determine linux distribution: %+v", err) - } - if d != nil && d.Disabled() { - log.Warnf("unsupported linux distribution: %s", d.Name()) - return match.NewMatches(), nil - } - } - if defaultMatcher == nil { defaultMatcher = stock.NewStockMatcher(stock.MatcherConfig{UseCPEs: true}) } diff --git a/grype/vulnerability_matcher_test.go b/grype/vulnerability_matcher_test.go index baf5658b849..b9a7010578e 100644 --- a/grype/vulnerability_matcher_test.go +++ b/grype/vulnerability_matcher_test.go @@ -29,7 +29,6 @@ import ( "github.com/anchore/grype/internal/bus" "github.com/anchore/syft/syft/cpe" "github.com/anchore/syft/syft/file" - "github.com/anchore/syft/syft/linux" syftPkg "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) @@ -264,9 +263,9 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { }, }, context: pkg.Context{ - Distro: &linux.Release{ - ID: "debian", - VersionID: "8", + Distro: &distro.Distro{ + Type: "debian", + Version: "8", }, }, }, @@ -281,9 +280,9 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { neutron2013Pkg, }, context: pkg.Context{ - Distro: &linux.Release{ - ID: "debian", - VersionID: "8", + Distro: &distro.Distro{ + Type: "debian", + Version: "8", }, }, }, @@ -336,9 +335,9 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { neutron2013Pkg, }, context: pkg.Context{ - Distro: &linux.Release{ - ID: "debian", - VersionID: "8", + Distro: &distro.Distro{ + Type: "debian", + Version: "8", }, }, }, @@ -410,9 +409,9 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) { }, }, }, - Distro: &linux.Release{ - ID: "debian", - VersionID: "8", + Distro: &distro.Distro{ + Type: "debian", + Version: "8", }, }, }, @@ -1069,9 +1068,9 @@ func Test_fatalErrors(t *testing.T) { }, }, pkg.Context{ - Distro: &linux.Release{ - ID: "debian", - VersionID: "8", + Distro: &distro.Distro{ + Type: "debian", + Version: "8", }, }, ) diff --git a/test/integration/match_by_image_test.go b/test/integration/match_by_image_test.go index 254fa2459e6..9a226cc2157 100644 --- a/test/integration/match_by_image_test.go +++ b/test/integration/match_by_image_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/require" "github.com/anchore/grype/grype" + "github.com/anchore/grype/grype/distro" "github.com/anchore/grype/grype/match" "github.com/anchore/grype/grype/matcher" "github.com/anchore/grype/grype/matcher/dotnet" @@ -30,7 +31,6 @@ import ( "github.com/anchore/syft/syft" "github.com/anchore/syft/syft/cataloging/pkgcataloging" "github.com/anchore/syft/syft/cpe" - "github.com/anchore/syft/syft/linux" syftPkg "github.com/anchore/syft/syft/pkg" "github.com/anchore/syft/syft/source" ) @@ -787,7 +787,7 @@ func TestMatchByImage(t *testing.T) { }, }) - actualResults := grype.FindVulnerabilitiesForPackage(theProvider, s.Artifacts.LinuxDistribution, matchers, pkg.FromCollection(s.Artifacts.Packages, pkg.SynthesisConfig{})) + actualResults := grype.FindVulnerabilitiesForPackage(theProvider, distro.FromRelease(s.Artifacts.LinuxDistribution), matchers, pkg.FromCollection(s.Artifacts.Packages, pkg.SynthesisConfig{})) for _, m := range actualResults.Sorted() { for _, d := range m.Details { observedMatchers.Add(string(d.Matcher)) @@ -945,7 +945,6 @@ func vexMatches(t *testing.T, ignoredMatches []match.IgnoredMatch, vexStatus vex }, }, }, - Distro: &linux.Release{}, } vexedMatches, ignoredMatches, err := vexMatcher.ApplyVEX(pctx, &matches, ignoredMatches) From 335da0011ecc593d669a26f37824f44d515ea98c Mon Sep 17 00:00:00 2001 From: Alan Pope Date: Tue, 13 May 2025 17:00:04 +0100 Subject: [PATCH 091/109] fix(windows): close listing file before removing it - fixes #2233 (#2657) Signed-off-by: Alan Pope Signed-off-by: Keith Zantow Co-authored-by: Keith Zantow --- grype/db/v5/distribution/curator.go | 1 + grype/db/v6/distribution/client.go | 1 + 2 files changed, 2 insertions(+) diff --git a/grype/db/v5/distribution/curator.go b/grype/db/v5/distribution/curator.go index cff1c83d571..609d67a565b 100644 --- a/grype/db/v5/distribution/curator.go +++ b/grype/db/v5/distribution/curator.go @@ -477,6 +477,7 @@ func (c Curator) ListingFromURL() (Listing, error) { return Listing{}, fmt.Errorf("unable to create listing temp file: %w", err) } defer func() { + log.CloseAndLogError(tempFile, tempFile.Name()) err := c.fs.RemoveAll(tempFile.Name()) if err != nil { log.Errorf("failed to remove file (%s): %w", tempFile.Name(), err) diff --git a/grype/db/v6/distribution/client.go b/grype/db/v6/distribution/client.go index 15ef6fc027f..92ec2e4afb7 100644 --- a/grype/db/v6/distribution/client.go +++ b/grype/db/v6/distribution/client.go @@ -177,6 +177,7 @@ func (c client) Latest() (*LatestDocument, error) { return nil, fmt.Errorf("unable to create listing temp file: %w", err) } defer func() { + log.CloseAndLogError(tempFile, tempFile.Name()) err := c.fs.RemoveAll(tempFile.Name()) if err != nil { log.WithFields("error", err, "file", tempFile.Name()).Errorf("failed to remove file") From 04227196a70f31cc1ac80f7e69cd34b1abc2dc7b Mon Sep 17 00:00:00 2001 From: Adam Chovanec <23148601+chovanecadam@users.noreply.github.com> Date: Wed, 14 May 2025 11:02:12 +0200 Subject: [PATCH 092/109] minor: add CPE provider to the help text (#2660) Signed-off-by: Adam Chovanec Co-authored-by: Adam Chovanec --- cmd/grype/cli/commands/root.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/grype/cli/commands/root.go b/cmd/grype/cli/commands/root.go index b2e64ed6f0d..937797e04c5 100644 --- a/cmd/grype/cli/commands/root.go +++ b/cmd/grype/cli/commands/root.go @@ -65,6 +65,7 @@ You can also explicitly specify the scheme to use: {{.appName}} registry:yourrepo/yourimage:tag pull image directly from a registry (no container runtime required) {{.appName}} purl:path/to/purl/file read a newline separated file of package URLs from a path on disk {{.appName}} PURL read a single package PURL directly (e.g. pkg:apk/openssl@3.2.1?distro=alpine-3.20.3) + {{.appName}} CPE read a single CPE directly (e.g. cpe:2.3:a:openssl:openssl:3.0.14:*:*:*:*:*) You can also pipe in Syft JSON directly: syft yourimage:tag -o json | {{.appName}} From 615126d0ce91e1cc40bc77183538a2433675814c 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, 14 May 2025 16:40:29 +0000 Subject: [PATCH 093/109] chore(deps): update anchore dependencies (#2662) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 +-- grype/pkg/package_test.go | 29 ++++++++++++++++++- .../snapshot/TestCycloneDxPresenterDir.golden | 14 ++++----- .../TestCycloneDxPresenterImage.golden | 12 ++++---- .../snapshot/TestJsonDirsPresenter.golden | 2 +- .../snapshot/TestJsonImgsPresenter.golden | 2 +- .../compare_sbom_input_vs_lib_test.go | 1 + 8 files changed, 47 insertions(+), 19 deletions(-) diff --git a/go.mod b/go.mod index 1ae7937ba62..3a20a536650 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 github.com/anchore/stereoscope v0.1.4 - github.com/anchore/syft v1.23.2-0.20250512173324-621d21eb04ca + github.com/anchore/syft v1.24.0 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 diff --git a/go.sum b/go.sum index c050d466770..35ea72a4d16 100644 --- a/go.sum +++ b/go.sum @@ -708,8 +708,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiE github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek= github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw= -github.com/anchore/syft v1.23.2-0.20250512173324-621d21eb04ca h1:GY1N+iXgj9rdibkf46vm+VQkASGGtW4NtDSNDAZSUuk= -github.com/anchore/syft v1.23.2-0.20250512173324-621d21eb04ca/go.mod h1:rD7NI0LzopzDuVe5SW+NOUAcbaRe8TYwwourGwBYZZk= +github.com/anchore/syft v1.24.0 h1:l5snrcZdZ+eFGiEtLhlVmjLSihWcWEs94wgauFj4cvU= +github.com/anchore/syft v1.24.0/go.mod h1:rD7NI0LzopzDuVe5SW+NOUAcbaRe8TYwwourGwBYZZk= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= diff --git a/grype/pkg/package_test.go b/grype/pkg/package_test.go index bb64df534f9..37ac0ed022e 100644 --- a/grype/pkg/package_test.go +++ b/grype/pkg/package_test.go @@ -377,7 +377,7 @@ func TestNew(t *testing.T) { }, }, { - name: "dart-pub-metadata", + name: "dart-publock-metadata", syftPkg: syftPkg.Package{ Metadata: syftPkg.DartPubspecLockEntry{ Name: "a", @@ -385,6 +385,33 @@ func TestNew(t *testing.T) { }, }, }, + { + name: "dart-pubspec-metadata", + syftPkg: syftPkg.Package{ + Metadata: syftPkg.DartPubspec{ + Homepage: "a", + Repository: "a", + Documentation: "a", + PublishTo: "a", + Environment: &syftPkg.DartPubspecEnvironment{ + SDK: "a", + Flutter: "a", + }, + Platforms: []string{"a"}, + IgnoredAdvisories: []string{"a"}, + }, + }, + }, + { + name: "homebrew-formula-metadata", + syftPkg: syftPkg.Package{ + Metadata: syftPkg.HomebrewFormula{ + Tap: "a", + Homepage: "a", + Description: "a", + }, + }, + }, { name: "dotnet-metadata", syftPkg: syftPkg.Package{ diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden index 45d6a15345b..e3a41a698e1 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.6", - "serialNumber": "urn:uuid:3ebe8aaa-da14-4729-b919-7e57658361e8", + "serialNumber": "urn:uuid:4485e0dd-b234-49c0-aa8c-d15c8366b021", "version": 1, "metadata": { - "timestamp": "2025-05-09T13:51:45-04:00", + "timestamp": "2025-05-14T12:14:15-04:00", "tools": { "components": [ { @@ -19,7 +19,7 @@ "component": { "bom-ref": "163686ac6e30c752", "type": "file", - "name": "/var/folders/c0/4y79v5k56bz8v34chcmvq2k80000gp/T/TestCycloneDxPresenterDir3160715530/001" + "name": "/var/folders/c0/4y79v5k56bz8v34chcmvq2k80000gp/T/TestCycloneDxPresenterDir1467223745/001" } }, "components": [ @@ -57,7 +57,7 @@ ] }, { - "bom-ref": "pkg:deb/package-2@2.2.2?package-id=d4131374f6ed43cf", + "bom-ref": "pkg:deb/package-2@2.2.2?package-id=74378afe15713625", "type": "library", "name": "package-2", "version": "2.2.2", @@ -89,7 +89,7 @@ ], "vulnerabilities": [ { - "bom-ref": "urn:uuid:244fe426-e979-4e33-8dc4-e0d8883de4f6", + "bom-ref": "urn:uuid:bb69589b-676f-4b14-8d38-3b30e956dad9", "id": "CVE-1999-0001", "source": {}, "references": [ @@ -113,7 +113,7 @@ ] }, { - "bom-ref": "urn:uuid:8a90ec6f-64c4-468d-a8a8-4ee72d24c83d", + "bom-ref": "urn:uuid:efde6b26-6cf1-4a14-8498-45b12bb0ffff", "id": "CVE-1999-0002", "source": {}, "references": [ @@ -132,7 +132,7 @@ ], "affects": [ { - "ref": "pkg:deb/package-2@2.2.2?package-id=d4131374f6ed43cf" + "ref": "pkg:deb/package-2@2.2.2?package-id=74378afe15713625" } ] } diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden index 760e35f44ab..1a1a6b48c85 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.6", - "serialNumber": "urn:uuid:792e3ebc-6cc7-4b3a-8fbb-bfb5532ba5c1", + "serialNumber": "urn:uuid:675d23a7-ee77-4eee-89fc-fe0a722b1a37", "version": 1, "metadata": { - "timestamp": "2025-05-09T13:51:45-04:00", + "timestamp": "2025-05-14T12:14:15-04:00", "tools": { "components": [ { @@ -58,7 +58,7 @@ ] }, { - "bom-ref": "pkg:deb/package-2@2.2.2?package-id=d4131374f6ed43cf", + "bom-ref": "pkg:deb/package-2@2.2.2?package-id=74378afe15713625", "type": "library", "name": "package-2", "version": "2.2.2", @@ -90,7 +90,7 @@ ], "vulnerabilities": [ { - "bom-ref": "urn:uuid:1565a551-e4bb-4012-8863-41407f2dedd3", + "bom-ref": "urn:uuid:cc3f752c-5853-4fa2-ad6b-8b93d12a6429", "id": "CVE-1999-0001", "source": {}, "references": [ @@ -114,7 +114,7 @@ ] }, { - "bom-ref": "urn:uuid:702719fe-6a8c-4890-b65f-065fc25e15cf", + "bom-ref": "urn:uuid:584bdd00-6d16-424d-8e61-ff7867f7756d", "id": "CVE-1999-0002", "source": {}, "references": [ @@ -133,7 +133,7 @@ ], "affects": [ { - "ref": "pkg:deb/package-2@2.2.2?package-id=d4131374f6ed43cf" + "ref": "pkg:deb/package-2@2.2.2?package-id=74378afe15713625" } ] } diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden index 23b6442b112..3993bf16682 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden @@ -134,7 +134,7 @@ } ], "artifact": { - "id": "d4131374f6ed43cf", + "id": "74378afe15713625", "name": "package-2", "version": "2.2.2", "type": "deb", diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden index 7ae3d3de88a..bae23a253ee 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden @@ -134,7 +134,7 @@ } ], "artifact": { - "id": "d4131374f6ed43cf", + "id": "74378afe15713625", "name": "package-2", "version": "2.2.2", "type": "deb", diff --git a/test/integration/compare_sbom_input_vs_lib_test.go b/test/integration/compare_sbom_input_vs_lib_test.go index 25e5fe8a0e3..eafc94cdbc2 100644 --- a/test/integration/compare_sbom_input_vs_lib_test.go +++ b/test/integration/compare_sbom_input_vs_lib_test.go @@ -63,6 +63,7 @@ func TestCompareSBOMInputToLibResults(t *testing.T) { string(syftPkg.ConanPkg), string(syftPkg.HexPkg), string(syftPkg.PortagePkg), + string(syftPkg.HomebrewPkg), string(syftPkg.CocoapodsPkg), string(syftPkg.HackagePkg), string(syftPkg.NixPkg), From a17a3bbe04988087745bf81d532a68e0385673af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 12:41:58 -0400 Subject: [PATCH 094/109] chore(deps): bump github/codeql-action from 3.28.17 to 3.28.18 (#2665) Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.17 to 3.28.18. - [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/60168efe1c415ce0f5521ea06d5c2062adbeed1b...ff0a06e83cb2de871e5a09832bc6a81e7276941f) --- updated-dependencies: - dependency-name: github/codeql-action dependency-version: 3.28.18 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 | 4 ++-- .github/workflows/scorecards.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index aad2ff2623b..89657d393f1 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -63,7 +63,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 + uses: github/codeql-action/init@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -85,4 +85,4 @@ jobs: run: make grype - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v3.28.17 + uses: github/codeql-action/analyze@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v3.28.18 diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index ed2e72967bd..8d90e6f855e 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -38,6 +38,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@60168efe1c415ce0f5521ea06d5c2062adbeed1b # v1.0.26 + uses: github/codeql-action/upload-sarif@ff0a06e83cb2de871e5a09832bc6a81e7276941f # v1.0.26 with: sarif_file: results.sarif From 8b0880fc72a42cdac7e4a723b7cd2145294d0abf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 12:42:07 -0400 Subject: [PATCH 095/109] chore(deps): bump anchore/sbom-action from 0.19.0 to 0.20.0 (#2664) Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.19.0 to 0.20.0. - [Release notes](https://github.com/anchore/sbom-action/releases) - [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md) - [Commits](https://github.com/anchore/sbom-action/compare/9f7302141466aa6482940f15371237e9d9f4c34a...e11c554f704a0b820cbf8c51673f6945e0731532) --- updated-dependencies: - dependency-name: anchore/sbom-action dependency-version: 0.20.0 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/release.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 0f1c1398f9a..1c17ee739cf 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -170,7 +170,7 @@ jobs: # for updating brew formula in anchore/homebrew-syft GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }} - - uses: anchore/sbom-action@9f7302141466aa6482940f15371237e9d9f4c34a # v0.19.0 + - uses: anchore/sbom-action@e11c554f704a0b820cbf8c51673f6945e0731532 # v0.20.0 continue-on-error: true with: artifact-name: sbom.spdx.json From 4d630fdfd3e8b2d4e3c02674c60bf355dfba4d7b 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, 16 May 2025 19:48:10 +0000 Subject: [PATCH 096/109] chore(deps): update anchore dependencies (#2668) * chore(deps): update anchore dependencies --------- Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com> --- go.mod | 10 +++++----- go.sum | 20 +++++++++---------- .../snapshot/TestCycloneDxPresenterDir.golden | 14 ++++++------- .../TestCycloneDxPresenterImage.golden | 12 +++++------ .../snapshot/TestJsonDirsPresenter.golden | 2 +- .../snapshot/TestJsonImgsPresenter.golden | 2 +- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/go.mod b/go.mod index 3a20a536650..f6db9d072b5 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 github.com/anchore/stereoscope v0.1.4 - github.com/anchore/syft v1.24.0 + github.com/anchore/syft v1.25.1 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 @@ -93,14 +93,15 @@ require ( github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.11.7 // indirect - github.com/ProtonMail/go-crypto v1.1.6 // indirect + github.com/ProtonMail/go-crypto v1.2.0 // indirect github.com/STARRY-S/zip v0.2.1 // indirect github.com/acobaugh/osrelease v0.1.0 // indirect github.com/agext/levenshtein v1.2.1 // indirect github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb // indirect + github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec // indirect github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6 // indirect - github.com/andybalholm/brotli v1.1.1 // indirect + github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect github.com/aquasecurity/go-version v0.0.1 // indirect github.com/aws/aws-sdk-go v1.44.288 // indirect @@ -189,7 +190,6 @@ require ( github.com/kevinburke/ssh_config v1.2.0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect - github.com/knqyf263/go-rpmdb v0.1.1 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect @@ -201,7 +201,7 @@ require ( github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect - github.com/mholt/archives v0.1.1 // indirect + github.com/mholt/archives v0.1.2 // indirect github.com/minio/minlz v1.0.0 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect diff --git a/go.sum b/go.sum index 35ea72a4d16..2200baeef73 100644 --- a/go.sum +++ b/go.sum @@ -660,8 +660,8 @@ github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= -github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw= -github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= +github.com/ProtonMail/go-crypto v1.2.0 h1:+PhXXn4SPGd+qk76TlEePBfOfivE0zkWFenhGhFLzWs= +github.com/ProtonMail/go-crypto v1.2.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg= github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= @@ -696,6 +696,8 @@ github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 h1:2SqmFgE7h+Ql4 github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722/go.mod h1:oFuE8YuTCM+spgMXhePGzk3asS94yO9biUfDzVTFqNw= 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-rpmdb v0.0.0-20250516171929-f77691e1faec h1:SjjPMOXTzpuU1ZME4XeoHyek+dry3/C7I8gzaCo02eg= +github.com/anchore/go-rpmdb v0.0.0-20250516171929-f77691e1faec/go.mod h1:eQVa6QFGzKy0qMcnW2pez0XBczvgwSjw9vA23qifEyU= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6 h1:Ha+LSCVuXYSYGi7wIkJK6G8g6jI3LH7y6LbyEVyp4Io= @@ -708,12 +710,12 @@ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiE github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek= github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw= -github.com/anchore/syft v1.24.0 h1:l5snrcZdZ+eFGiEtLhlVmjLSihWcWEs94wgauFj4cvU= -github.com/anchore/syft v1.24.0/go.mod h1:rD7NI0LzopzDuVe5SW+NOUAcbaRe8TYwwourGwBYZZk= +github.com/anchore/syft v1.25.1 h1:HaG5/0r1UdZ7zyscEFeFz0pQsBLTXdCgEDXa5LqFjcg= +github.com/anchore/syft v1.25.1/go.mod h1:xa15pYmHrXKe7IlvaO+EAD/krawWYUtILTpMcL/S+Gw= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= -github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= +github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ= +github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= 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= @@ -1243,8 +1245,6 @@ github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f h1:GvCU5GX github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f/go.mod h1:q59u9px8b7UTj0nIjEjvmTWekazka6xIt6Uogz5Dm+8= github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4Kn3JPupAwwWuo4AzYp16P0OyLO9d7OnMZc/c= github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao= -github.com/knqyf263/go-rpmdb v0.1.1 h1:oh68mTCvp1XzxdU7EfafcWzzfstUZAEa3MW0IJye584= -github.com/knqyf263/go-rpmdb v0.1.1/go.mod h1:9LQcoMCMQ9vrF7HcDtXfvqGO4+ddxFQ8+YF/0CVGDww= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 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= @@ -1304,8 +1304,8 @@ github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4 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= -github.com/mholt/archives v0.1.1 h1:c7J3qXN1FB54y0qiUXiq9Bxk4eCUc8pdXWwOhZdRzeY= -github.com/mholt/archives v0.1.1/go.mod h1:FQVz01Q2uXKB/35CXeW/QFO23xT+hSCGZHVtha78U4I= +github.com/mholt/archives v0.1.2 h1:UBSe5NfYKHI1sy+S5dJsEsG9jsKKk8NJA4HCC+xTI4A= +github.com/mholt/archives v0.1.2/go.mod h1:D7QzTHgw3ctfS6wgOO9dN+MFgdZpbksGCxprUOwZWDs= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden index e3a41a698e1..34638ea5544 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.6", - "serialNumber": "urn:uuid:4485e0dd-b234-49c0-aa8c-d15c8366b021", + "serialNumber": "urn:uuid:c7b9f230-9fb8-43f7-af7a-824cd878a853", "version": 1, "metadata": { - "timestamp": "2025-05-14T12:14:15-04:00", + "timestamp": "2025-05-16T15:26:00-04:00", "tools": { "components": [ { @@ -19,12 +19,12 @@ "component": { "bom-ref": "163686ac6e30c752", "type": "file", - "name": "/var/folders/c0/4y79v5k56bz8v34chcmvq2k80000gp/T/TestCycloneDxPresenterDir1467223745/001" + "name": "/var/folders/8y/ct5nbgtj4p30k10kfmq4p4s00000gn/T/TestCycloneDxPresenterDir76562573/001" } }, "components": [ { - "bom-ref": "9baa2db122fea516", + "bom-ref": "bbb0ba712c2b94ea", "type": "library", "name": "package-1", "version": "1.1.1", @@ -89,7 +89,7 @@ ], "vulnerabilities": [ { - "bom-ref": "urn:uuid:bb69589b-676f-4b14-8d38-3b30e956dad9", + "bom-ref": "urn:uuid:983f094f-a20c-4aed-b6f1-d9b417c706cc", "id": "CVE-1999-0001", "source": {}, "references": [ @@ -108,12 +108,12 @@ ], "affects": [ { - "ref": "9baa2db122fea516" + "ref": "bbb0ba712c2b94ea" } ] }, { - "bom-ref": "urn:uuid:efde6b26-6cf1-4a14-8498-45b12bb0ffff", + "bom-ref": "urn:uuid:ae00e132-f2c5-4d07-896d-31aaf91e04d1", "id": "CVE-1999-0002", "source": {}, "references": [ diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden index 1a1a6b48c85..bafdf481d93 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden @@ -2,10 +2,10 @@ "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", "bomFormat": "CycloneDX", "specVersion": "1.6", - "serialNumber": "urn:uuid:675d23a7-ee77-4eee-89fc-fe0a722b1a37", + "serialNumber": "urn:uuid:e9977dcd-e35e-4053-ab49-932f99e4d240", "version": 1, "metadata": { - "timestamp": "2025-05-14T12:14:15-04:00", + "timestamp": "2025-05-16T15:26:00-04:00", "tools": { "components": [ { @@ -25,7 +25,7 @@ }, "components": [ { - "bom-ref": "9baa2db122fea516", + "bom-ref": "bbb0ba712c2b94ea", "type": "library", "name": "package-1", "version": "1.1.1", @@ -90,7 +90,7 @@ ], "vulnerabilities": [ { - "bom-ref": "urn:uuid:cc3f752c-5853-4fa2-ad6b-8b93d12a6429", + "bom-ref": "urn:uuid:9f6e21d3-d245-4ee3-9558-cb5cc4936b32", "id": "CVE-1999-0001", "source": {}, "references": [ @@ -109,12 +109,12 @@ ], "affects": [ { - "ref": "9baa2db122fea516" + "ref": "bbb0ba712c2b94ea" } ] }, { - "bom-ref": "urn:uuid:584bdd00-6d16-424d-8e61-ff7867f7756d", + "bom-ref": "urn:uuid:4fc03529-3b07-41e2-9bc9-c0b2989ccf8e", "id": "CVE-1999-0002", "source": {}, "references": [ diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden index 3993bf16682..d77afa9e331 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden @@ -57,7 +57,7 @@ } ], "artifact": { - "id": "9baa2db122fea516", + "id": "bbb0ba712c2b94ea", "name": "package-1", "version": "1.1.1", "type": "rpm", diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden index bae23a253ee..b49085fb678 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden @@ -57,7 +57,7 @@ } ], "artifact": { - "id": "9baa2db122fea516", + "id": "bbb0ba712c2b94ea", "name": "package-1", "version": "1.1.1", "type": "rpm", From 2890987e498ca64387ff28cb3c367ecf72741b11 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Mon, 19 May 2025 09:30:40 -0400 Subject: [PATCH 097/109] remove getDB() from vulnerability provider (#2669) Signed-off-by: Alex Goodman --- grype/db/v6/db.go | 1 - grype/db/v6/refs.go | 8 +++++++- grype/db/v6/store.go | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/grype/db/v6/db.go b/grype/db/v6/db.go index 43dba7a33f0..002eb1c15b2 100644 --- a/grype/db/v6/db.go +++ b/grype/db/v6/db.go @@ -57,7 +57,6 @@ type Reader interface { AffectedPackageStoreReader AffectedCPEStoreReader io.Closer - getDB() *gorm.DB attachBlobValue(...blobable) error } diff --git a/grype/db/v6/refs.go b/grype/db/v6/refs.go index 228c1479965..6a2df8975ed 100644 --- a/grype/db/v6/refs.go +++ b/grype/db/v6/refs.go @@ -2,6 +2,8 @@ package v6 import ( "slices" + + "gorm.io/gorm" ) type ref[ID, T any] struct { @@ -38,7 +40,7 @@ func fillRefs[T, R any](reader Reader, handles []*T, getRef refProvider[T, R], r // load a map with all id -> ref results var values []R - tx := reader.getDB().Where("id IN (?)", ids) + tx := reader.(lowLevelReader).GetDB().Where("id IN (?)", ids) err := tx.Find(&values).Error if err != nil { return err @@ -73,3 +75,7 @@ func ptrs[T any](values []T) []*T { } return out } + +type lowLevelReader interface { + GetDB() *gorm.DB +} diff --git a/grype/db/v6/store.go b/grype/db/v6/store.go index 8a39bb5c678..336f89c9102 100644 --- a/grype/db/v6/store.go +++ b/grype/db/v6/store.go @@ -23,7 +23,7 @@ type store struct { writable bool } -func (s *store) getDB() *gorm.DB { +func (s *store) GetDB() *gorm.DB { return s.db } From cc4e074aa69fb1a5a853ca0333c7b48f18ded8d2 Mon Sep 17 00:00:00 2001 From: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com> Date: Mon, 19 May 2025 09:56:35 -0400 Subject: [PATCH 098/109] chore: update dockerfile base images to latest rolling tags (#2671) Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- Dockerfile | 3 +-- Dockerfile.debug | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 5eaab88c663..a02f87b98e5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,4 @@ -FROM gcr.io/distroless/static-debian11@sha256:5759d194607e472ff80fff5833442d3991dd89b219c96552837a2c8f74058617 AS build - +FROM gcr.io/distroless/static-debian12:latest AS build FROM scratch # needed for version check HTTPS request diff --git a/Dockerfile.debug b/Dockerfile.debug index db7a781d275..64c06a734e3 100644 --- a/Dockerfile.debug +++ b/Dockerfile.debug @@ -1,5 +1,4 @@ -FROM gcr.io/distroless/static-debian11:debug@sha256:c66a6ecb5aa7704a68c89d3ead1398adc7f16e214dda5f5f8e5d44351bcbf67d - +FROM gcr.io/distroless/static-debian12:debug # create the /tmp dir, which is needed for image content cache WORKDIR /tmp From 2675456100b222e65b407dc4ed7a0494a7fc7dcb Mon Sep 17 00:00:00 2001 From: Will Murphy Date: Mon, 19 May 2025 12:08:47 -0400 Subject: [PATCH 099/109] fix: ensure temp dir on same volume as final db dir (#2672) Previously, when GRYPE_DB_CACHE_DIR was set to a docker volume, the grype temp dir could be created under the /tmp volume in the container but the final destination be under the named volume, which caused os.Rename to fail with an invalid cross-volume link error. Now, ensure that the temporary and final DB location are on the same volume by creating the temp dir under GRYPE_DB_CACHE_DIR. Signed-off-by: Will Murphy --- grype/db/v6/installation/curator.go | 24 +++- grype/db/v6/installation/curator_test.go | 147 +++++++++++++++++++++++ 2 files changed, 166 insertions(+), 5 deletions(-) diff --git a/grype/db/v6/installation/curator.go b/grype/db/v6/installation/curator.go index cb408c5fbff..1c8c853c2aa 100644 --- a/grype/db/v6/installation/curator.go +++ b/grype/db/v6/installation/curator.go @@ -298,7 +298,14 @@ func (c curator) update(current *db.Description) (*distribution.Archive, error) if err != nil { return nil, fmt.Errorf("unable to resolve vulnerability DB URL: %w", err) } - dest, err := c.client.Download(url, filepath.Dir(c.config.DBRootDir), mon.downloadProgress.Manual) + + // Ensure parent of DBRootDir exists for the download client to create a temp dir within DBRootDir + // This might be redundant if DBRootDir must already exist, but good for safety. + if err := os.MkdirAll(c.config.DBRootDir, 0o700); err != nil { + return nil, fmt.Errorf("unable to create db root dir %s for download: %w", c.config.DBRootDir, err) + } + + dest, err := c.client.Download(url, c.config.DBRootDir, mon.downloadProgress.Manual) if err != nil { return nil, fmt.Errorf("unable to update vulnerability database: %w", err) } @@ -307,6 +314,8 @@ func (c curator) update(current *db.Description) (*distribution.Archive, error) mon.downloadProgress.SetCompleted() if err = c.activate(dest, url, mon); err != nil { + log.Warnf("Failed to activate downloaded database from %s, attempting cleanup of temporary download directory.", dest) + removeAllOrLog(c.fs, dest) return nil, fmt.Errorf("unable to activate new vulnerability database: %w", err) } @@ -397,7 +406,7 @@ func (c curator) setLastSuccessfulUpdateCheck() { // is a prerequisite for a successful update). filePath := filepath.Join(c.config.DBDirectoryPath(), lastUpdateCheckFileName) - fh, err := c.fs.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644) + fh, err := c.fs.OpenFile(filePath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0o644) if err != nil { log.WithFields("error", err).Trace("unable to write last update check timestamp") return @@ -414,7 +423,7 @@ func (c curator) Import(reference string) error { mon.Set("preparing") defer mon.SetCompleted() - if err := os.MkdirAll(c.config.DBRootDir, 0700); err != nil { + if err := os.MkdirAll(c.config.DBRootDir, 0o700); err != nil { return fmt.Errorf("unable to create db root dir: %w", err) } @@ -424,7 +433,7 @@ func (c curator) Import(reference string) error { mon.Set("downloading") var err error - tempDir, err = c.client.Download(reference, filepath.Dir(c.config.DBRootDir), mon.downloadProgress.Manual) + tempDir, err = c.client.Download(reference, c.config.DBRootDir, mon.downloadProgress.Manual) if err != nil { return fmt.Errorf("unable to update vulnerability database: %w", err) } @@ -526,12 +535,17 @@ func (c curator) replaceDB(dbDirPath string) error { } // ensure parent db directory exists - if err := c.fs.MkdirAll(filepath.Dir(dbDir), 0700); err != nil { + if err = c.fs.MkdirAll(filepath.Dir(dbDir), 0o700); err != nil { return fmt.Errorf("unable to create db parent directory: %w", err) } // activate the new db cache by moving the temp dir to final location + // the rename should be safe because the temp dir is under GRYPE_DB_CACHE_DIR + // and so on the same filesystem as the final location err = c.fs.Rename(dbDirPath, dbDir) + if err != nil { + err = fmt.Errorf("failed to move database directory to activate: %w", err) + } log.WithFields("from", dbDirPath, "to", dbDir, "error", err).Debug("moved database directory to activate") return err } diff --git a/grype/db/v6/installation/curator_test.go b/grype/db/v6/installation/curator_test.go index c00a893fbfe..9d60d34582f 100644 --- a/grype/db/v6/installation/curator_test.go +++ b/grype/db/v6/installation/curator_test.go @@ -758,6 +758,153 @@ func Test_isRehydrationNeeded(t *testing.T) { } } +func TestCurator_Update_UsesDBRootDirForDownloadTempBase(t *testing.T) { + c := newTestCurator(t) // This sets up c.fs as afero.NewOsFs() rooted in t.TempDir() + mc := c.client.(*mockClient) + + // This is the path that the mocked Download method will return. + // It simulates a temporary directory created by the download client within DBRootDir. + expectedDownloadedContentPath := filepath.Join(c.config.DBRootDir, "temp-downloaded-db-content-123") + + // Pre-create this directory and make it look like a valid DB source for the hydrator and replaceDB. + require.NoError(t, c.fs.MkdirAll(expectedDownloadedContentPath, 0755)) + // Write minimal valid DB metadata so that hydration/activation can proceed far enough. + // Using existing helpers to create a semblance of a DB. + writeTestDB(t, c.fs, expectedDownloadedContentPath) // This creates a basic DB file and import metadata. + + // Mock client responses + mc.On("IsUpdateAvailable", mock.Anything).Return(&distribution.Archive{}, nil) + // CRUCIAL ASSERTION: + // Verify that Download is called with c.config.DBRootDir as its second argument (baseDirForTemp). + // It will return the expectedDownloadedContentPath, simulating successful download and extraction. + mc.On("Download", mock.Anything, c.config.DBRootDir, mock.Anything).Return(expectedDownloadedContentPath, nil) + + hydrateCalled := false + c.hydrator = func(path string) error { + // Ensure hydrator is called with the path returned by Download + assert.Equal(t, expectedDownloadedContentPath, path, "hydrator called with incorrect path") + hydrateCalled = true + return nil // Simulate successful hydration + } + + // Call Update to trigger the download and activation sequence + updated, err := c.Update() + + // Assertions + require.NoError(t, err, "Update should succeed") + require.True(t, updated, "Update should report true") + mc.AssertExpectations(t) // Verifies that Download was called with the expected arguments + assert.True(t, hydrateCalled, "expected hydrator to be called") + + // Check if the DB was "activated" (i.e., renamed) + finalDBPath := c.config.DBDirectoryPath() + _, err = c.fs.Stat(finalDBPath) + require.NoError(t, err, "final DB directory should exist after successful update") + // And the temporary downloaded content path should no longer exist as it was renamed + _, err = c.fs.Stat(expectedDownloadedContentPath) + require.True(t, os.IsNotExist(err), "temporary download path should not exist after rename") +} + +func TestCurator_Update_CleansUpDownloadDirOnActivationFailure(t *testing.T) { + c := newTestCurator(t) // Sets up c.fs as afero.NewOsFs() rooted in t.TempDir() + mc := c.client.(*mockClient) + + // This is the path that the mocked Download method will return. + // This directory should be cleaned up if activation fails. + downloadedContentPath := filepath.Join(c.config.DBRootDir, "temp-download-to-be-cleaned-up") + + // Simulate the download client successfully creating this directory. + require.NoError(t, c.fs.MkdirAll(downloadedContentPath, 0755)) + // Optionally, put a dummy file inside to make the cleanup more tangible. + require.NoError(t, afero.WriteFile(c.fs, filepath.Join(downloadedContentPath, "dummy_file.txt"), []byte("test data"), 0644)) + + // Mock client responses + mc.On("IsUpdateAvailable", mock.Anything).Return(&distribution.Archive{}, nil) + // Download is called with DBRootDir as base, and returns the path to the (simulated) downloaded content. + mc.On("Download", mock.Anything, c.config.DBRootDir, mock.Anything).Return(downloadedContentPath, nil) + + // Configure the hydrator to fail, which will cause c.activate() to fail. + expectedHydrationError := "simulated hydration failure" + c.hydrator = func(path string) error { + assert.Equal(t, downloadedContentPath, path, "hydrator called with incorrect path") + return errors.New(expectedHydrationError) + } + + // Call Update, expecting it to fail during activation. + updated, err := c.Update() + + // Assertions + require.Error(t, err, "Update should fail due to activation error") + require.Contains(t, err.Error(), expectedHydrationError, "Error message should reflect hydration failure") + require.False(t, updated, "Update should report false on failure") + mc.AssertExpectations(t) // Verifies Download was called as expected. + + // CRUCIAL ASSERTION: + // Verify that the temporary download directory was cleaned up. + _, statErr := c.fs.Stat(downloadedContentPath) + require.True(t, os.IsNotExist(statErr), "expected temporary download directory to be cleaned up after activation failure") +} + +// Test for the Import path (URL case) - very similar to the Update tests +func TestCurator_Import_URL_UsesDBRootDirForDownloadTempBaseAndCleansUp(t *testing.T) { + t.Run("successful import from URL", func(t *testing.T) { + c := newTestCurator(t) + mc := c.client.(*mockClient) + + importURL := "http://localhost/some/db.tar.gz" + expectedDownloadedContentPath := filepath.Join(c.config.DBRootDir, "temp-imported-db-content-url") + + require.NoError(t, c.fs.MkdirAll(expectedDownloadedContentPath, 0755)) + writeTestDB(t, c.fs, expectedDownloadedContentPath) + + mc.On("Download", importURL, c.config.DBRootDir, mock.Anything).Return(expectedDownloadedContentPath, nil) + + hydrateCalled := false + c.hydrator = func(path string) error { + assert.Equal(t, expectedDownloadedContentPath, path) + hydrateCalled = true + return nil + } + + err := c.Import(importURL) + + require.NoError(t, err) + mc.AssertExpectations(t) + assert.True(t, hydrateCalled) + _, err = c.fs.Stat(c.config.DBDirectoryPath()) + require.NoError(t, err, "final DB directory should exist") + _, err = c.fs.Stat(expectedDownloadedContentPath) + require.True(t, os.IsNotExist(err), "temp import path should not exist after rename") + }) + + t.Run("import from URL fails activation", func(t *testing.T) { + c := newTestCurator(t) + mc := c.client.(*mockClient) + + importURL := "http://localhost/some/other/db.tar.gz" + downloadedContentPath := filepath.Join(c.config.DBRootDir, "temp-imported-to-cleanup-url") + + require.NoError(t, c.fs.MkdirAll(downloadedContentPath, 0755)) + require.NoError(t, afero.WriteFile(c.fs, filepath.Join(downloadedContentPath, "dummy.txt"), []byte("test"), 0644)) + + mc.On("Download", importURL, c.config.DBRootDir, mock.Anything).Return(downloadedContentPath, nil) + + expectedHydrationError := "simulated hydration failure for import" + c.hydrator = func(path string) error { + return errors.New(expectedHydrationError) + } + + err := c.Import(importURL) + + require.Error(t, err) + require.Contains(t, err.Error(), expectedHydrationError) + mc.AssertExpectations(t) + + _, statErr := c.fs.Stat(downloadedContentPath) + require.True(t, os.IsNotExist(statErr), "expected temp import directory to be cleaned up") + }) +} + func setupTestDB(t *testing.T, dbDir string) db.ReadWriter { s, err := db.NewWriter(db.Config{ DBDirPath: dbDir, From a932ab5b3740535bd3ddaac981c5609e91456a25 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, 20 May 2025 12:28:54 -0400 Subject: [PATCH 100/109] chore(deps): update tools to latest versions (#2673) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.binny.yaml b/.binny.yaml index d73cbb63c33..8bed612d6e1 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -98,7 +98,7 @@ tools: # used for triggering a release - name: gh version: - want: v2.72.0 + want: v2.73.0 method: github-release with: repo: cli/cli From c4fa2eb28d1aad9e958bbd628b08da63a032e6c9 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 20 May 2025 17:33:16 -0400 Subject: [PATCH 101/109] Parse java group ID and artifact ID from PURL when missing (#2675) * parse java groupid and artifactid from purl Signed-off-by: Alex Goodman * bump syft Signed-off-by: Alex Goodman --------- Signed-off-by: Alex Goodman --- go.mod | 2 +- go.sum | 4 +-- grype/pkg/package.go | 50 ++++++++++++++++++++++++++++----- grype/pkg/package_test.go | 13 +++++++++ grype/pkg/purl_provider_test.go | 23 +++++++++++++++ 5 files changed, 82 insertions(+), 10 deletions(-) diff --git a/go.mod b/go.mod index f6db9d072b5..ddbe8ce0646 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 github.com/anchore/stereoscope v0.1.4 - github.com/anchore/syft v1.25.1 + github.com/anchore/syft v1.25.2-0.20250520195608-ac883f52edb8 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 diff --git a/go.sum b/go.sum index 2200baeef73..cae4fb29223 100644 --- a/go.sum +++ b/go.sum @@ -710,8 +710,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiE github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek= github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw= -github.com/anchore/syft v1.25.1 h1:HaG5/0r1UdZ7zyscEFeFz0pQsBLTXdCgEDXa5LqFjcg= -github.com/anchore/syft v1.25.1/go.mod h1:xa15pYmHrXKe7IlvaO+EAD/krawWYUtILTpMcL/S+Gw= +github.com/anchore/syft v1.25.2-0.20250520195608-ac883f52edb8 h1:v/sMVa0MB+/IET9Q2ACFFRTcuWFSQMuIHRuHPzJ8XH4= +github.com/anchore/syft v1.25.2-0.20250520195608-ac883f52edb8/go.mod h1:xa15pYmHrXKe7IlvaO+EAD/krawWYUtILTpMcL/S+Gw= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ= diff --git a/grype/pkg/package.go b/grype/pkg/package.go index abf6b244d30..dc0634eed7e 100644 --- a/grype/pkg/package.go +++ b/grype/pkg/package.go @@ -212,6 +212,7 @@ func dataFromPkg(p syftPkg.Package) (any, []UpstreamPackage) { var metadata interface{} var upstreams []UpstreamPackage + // use the metadata to determine the type of package switch p.Metadata.(type) { case syftPkg.GolangModuleEntry, syftPkg.GolangBinaryBuildinfoEntry: metadata = golangMetadataFromPkg(p) @@ -226,7 +227,7 @@ func dataFromPkg(p syftPkg.Package) (any, []UpstreamPackage) { metadata = *m } case syftPkg.JavaArchive: - if m := javaDataFromPkg(p); m != nil { + if m := javaDataFromPkgMetadata(p); m != nil { metadata = *m } case syftPkg.ApkDBEntry: @@ -236,6 +237,13 @@ func dataFromPkg(p syftPkg.Package) (any, []UpstreamPackage) { metadata = javaVMDataFromPkg(p) } + // there are still cases where we could still fill the metadata from other info (such as the PURL) + if metadata == nil { + if p.Type == syftPkg.JavaPkg { + metadata = javaDataFromPkgData(p) + } + } + return metadata, upstreams } @@ -309,7 +317,7 @@ func dpkgDataFromPkg(p syftPkg.Package) (upstreams []UpstreamPackage) { }) } default: - log.Warnf("unable to extract DPKG metadata for %s", p) + log.Debugf("unable to extract DPKG metadata for %s", p) } return upstreams @@ -343,7 +351,7 @@ func handleSourceRPM(pkgName, sourceRpm string) []UpstreamPackage { var upstreams []UpstreamPackage name, version := getNameAndELVersion(sourceRpm) if name == "" && version == "" { - log.Warnf("unable to extract name and version from SourceRPM=%q ", sourceRpm) + log.Debugf("unable to extract name and version from SourceRPM=%q", sourceRpm) } else if name != pkgName { // don't include matches if the source package name matches the current package name if name != "" && version != "" { @@ -364,13 +372,17 @@ func getNameAndELVersion(sourceRpm string) (string, string) { return groupMatches["name"], version } -func javaDataFromPkg(p syftPkg.Package) (metadata *JavaMetadata) { +func javaDataFromPkgMetadata(p syftPkg.Package) (metadata *JavaMetadata) { if value, ok := p.Metadata.(syftPkg.JavaArchive); ok { var artifactID, groupID, name string if value.PomProperties != nil { artifactID = value.PomProperties.ArtifactID groupID = value.PomProperties.GroupID + } else { + // get the group ID / artifact ID from the PURL + artifactID, groupID = javaGroupArtifactIDFromPurl(p.PURL) } + if value.Manifest != nil { for _, kv := range value.Manifest.Main { if kv.Key == "Name" { @@ -396,12 +408,36 @@ func javaDataFromPkg(p syftPkg.Package) (metadata *JavaMetadata) { ManifestName: name, ArchiveDigests: archiveDigests, } - } else { - log.Warnf("unable to extract Java metadata for %s", p) } return metadata } +func javaDataFromPkgData(p syftPkg.Package) (metadata *JavaMetadata) { + switch p.Type { + case syftPkg.JavaPkg: + artifactID, groupID := javaGroupArtifactIDFromPurl(p.PURL) + if artifactID != "" && groupID != "" { + metadata = &JavaMetadata{ + PomArtifactID: artifactID, + PomGroupID: groupID, + } + } + default: + log.Debugf("unable to extract metadata for %s", p) + } + + return metadata +} + +func javaGroupArtifactIDFromPurl(p string) (string, string) { + purl, err := packageurl.FromString(p) + if err != nil { + log.WithFields("purl", purl, "error", err).Debug("unable to parse java PURL") + return "", "" + } + return purl.Name, purl.Namespace +} + func apkDataFromPkg(p syftPkg.Package) (upstreams []UpstreamPackage) { if value, ok := p.Metadata.(syftPkg.ApkDBEntry); ok { if value.OriginPackage != "" { @@ -410,7 +446,7 @@ func apkDataFromPkg(p syftPkg.Package) (upstreams []UpstreamPackage) { }) } } else { - log.Warnf("unable to extract APK metadata for %s", p) + log.Debugf("unable to extract APK metadata for %s", p) } return upstreams } diff --git a/grype/pkg/package_test.go b/grype/pkg/package_test.go index 37ac0ed022e..622e0be5d9f 100644 --- a/grype/pkg/package_test.go +++ b/grype/pkg/package_test.go @@ -842,6 +842,19 @@ func TestNew(t *testing.T) { }, }, }, + { + name: "pe binary metadata", + syftPkg: syftPkg.Package{ + Metadata: syftPkg.PEBinary{ + VersionResources: syftPkg.KeyValues{ + { + Key: "k", + Value: "k", + }, + }, + }, + }, + }, } // capture each observed metadata type, we should see all of them relate to what syft provides by the end of testing diff --git a/grype/pkg/purl_provider_test.go b/grype/pkg/purl_provider_test.go index a63d55735ba..ca8b7c371b1 100644 --- a/grype/pkg/purl_provider_test.go +++ b/grype/pkg/purl_provider_test.go @@ -39,6 +39,29 @@ func Test_PurlProvider(t *testing.T) { }, }, }, + { + name: "java metadata decoded from purl", + userInput: "pkg:maven/org.apache.commons/commons-lang3@3.12.0", + context: Context{ + Source: &source.Description{ + Metadata: PURLLiteralMetadata{ + PURL: "pkg:maven/org.apache.commons/commons-lang3@3.12.0", + }, + }, + }, + pkgs: []Package{ + { + Name: "commons-lang3", + Version: "3.12.0", + Type: pkg.JavaPkg, + PURL: "pkg:maven/org.apache.commons/commons-lang3@3.12.0", + Metadata: JavaMetadata{ + PomArtifactID: "commons-lang3", + PomGroupID: "org.apache.commons", + }, + }, + }, + }, { name: "os with codename", userInput: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit", From 3f52c46d440ac17b2924c633826ce8fd30a5e16d 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, 20 May 2025 22:12:30 +0000 Subject: [PATCH 102/109] chore(deps): update anchore dependencies (#2676) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: wagoodman <590471+wagoodman@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 ddbe8ce0646..9e8c0e0b4aa 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 github.com/anchore/stereoscope v0.1.4 - github.com/anchore/syft v1.25.2-0.20250520195608-ac883f52edb8 + github.com/anchore/syft v1.26.0 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 diff --git a/go.sum b/go.sum index cae4fb29223..832e552925d 100644 --- a/go.sum +++ b/go.sum @@ -710,8 +710,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiE github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek= github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw= -github.com/anchore/syft v1.25.2-0.20250520195608-ac883f52edb8 h1:v/sMVa0MB+/IET9Q2ACFFRTcuWFSQMuIHRuHPzJ8XH4= -github.com/anchore/syft v1.25.2-0.20250520195608-ac883f52edb8/go.mod h1:xa15pYmHrXKe7IlvaO+EAD/krawWYUtILTpMcL/S+Gw= +github.com/anchore/syft v1.26.0 h1:u3x143wnU9ZuKxksrTugUtZ2dt1hY9fpc8dbd/iMysI= +github.com/anchore/syft v1.26.0/go.mod h1:xa15pYmHrXKe7IlvaO+EAD/krawWYUtILTpMcL/S+Gw= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ= From 48d074062e5be35c10858442a38fc4c21ea44d70 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 22 May 2025 15:01:41 -0400 Subject: [PATCH 103/109] chore(deps): bump github.com/anchore/syft from 1.26.0 to 1.26.1 (#2678) Bumps [github.com/anchore/syft](https://github.com/anchore/syft) from 1.26.0 to 1.26.1. - [Release notes](https://github.com/anchore/syft/releases) - [Changelog](https://github.com/anchore/syft/blob/main/.goreleaser.yaml) - [Commits](https://github.com/anchore/syft/compare/v1.26.0...v1.26.1) --- updated-dependencies: - dependency-name: github.com/anchore/syft dependency-version: 1.26.1 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 | 22 +++++++++++----------- go.sum | 44 ++++++++++++++++++++++---------------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/go.mod b/go.mod index 9e8c0e0b4aa..fc0230ed17d 100644 --- a/go.mod +++ b/go.mod @@ -19,7 +19,7 @@ require ( github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 github.com/anchore/stereoscope v0.1.4 - github.com/anchore/syft v1.26.0 + github.com/anchore/syft v1.26.1 github.com/aquasecurity/go-pep440-version v0.0.1 github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de github.com/bmatcuk/doublestar/v2 v2.0.4 @@ -36,7 +36,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.2.1 github.com/gohugoio/hashstructure v0.5.0 github.com/google/go-cmp v0.7.0 - github.com/google/go-containerregistry v0.20.3 + github.com/google/go-containerregistry v0.20.4 github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b @@ -143,7 +143,7 @@ require ( github.com/distribution/reference v0.6.0 // indirect github.com/docker/cli v28.1.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker-credential-helpers v0.8.2 // indirect + github.com/docker/docker-credential-helpers v0.9.3 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect github.com/docker/go-units v0.5.0 // indirect @@ -263,7 +263,7 @@ require ( github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect github.com/vbatts/go-mtree v0.5.4 // indirect - github.com/vbatts/tar-split v0.11.6 // indirect + github.com/vbatts/tar-split v0.12.1 // indirect github.com/vifraa/gopom v1.0.0 // indirect github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect @@ -274,18 +274,18 @@ require ( go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.29.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/sdk v1.33.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.29.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/sdk v1.35.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect go.uber.org/multierr v1.11.0 // indirect go4.org v0.0.0-20230225012048-214862532bf5 // indirect golang.org/x/crypto v0.38.0 // indirect golang.org/x/mod v0.24.0 // indirect golang.org/x/net v0.40.0 // indirect - golang.org/x/oauth2 v0.25.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect golang.org/x/sync v0.14.0 // indirect golang.org/x/sys v0.33.0 // indirect golang.org/x/term v0.32.0 // indirect diff --git a/go.sum b/go.sum index 832e552925d..796b2571be6 100644 --- a/go.sum +++ b/go.sum @@ -710,8 +710,8 @@ github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiE github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI= github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek= github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw= -github.com/anchore/syft v1.26.0 h1:u3x143wnU9ZuKxksrTugUtZ2dt1hY9fpc8dbd/iMysI= -github.com/anchore/syft v1.26.0/go.mod h1:xa15pYmHrXKe7IlvaO+EAD/krawWYUtILTpMcL/S+Gw= +github.com/anchore/syft v1.26.1 h1:v64AqoYzPGZols2O20xquE93bJr0EH3VqAKsB0fr0WA= +github.com/anchore/syft v1.26.1/go.mod h1:OyN/wK6s7lmV2kAUm4zcQbZhLznToASL2r+iRQHcIKY= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ= @@ -879,8 +879,8 @@ github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBi github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I= github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo= -github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= +github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= +github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= @@ -1070,8 +1070,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= -github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= +github.com/google/go-containerregistry v0.20.4 h1:w/Fdj3ef046SdV/GJU69cCnreaLpqbTo1X9XPyHbkd4= +github.com/google/go-containerregistry v0.20.4/go.mod h1:Q14vdOOzug02bwnhMkZKD4e30pDaD9W65qzXpyzF49E= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs= github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY= @@ -1564,8 +1564,8 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/vbatts/go-mtree v0.5.4 h1:OMAb8jaCyiFA7zXj0Zc/oARcxBDBoeu2LizjB8BVJl0= github.com/vbatts/go-mtree v0.5.4/go.mod h1:5GqJbVhm9BBiCc4K5uc/c42FPgXulHaQs4sFUEfIWMo= -github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= -github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= +github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= +github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0= github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o= github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc= @@ -1625,24 +1625,24 @@ go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5 go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0 h1:WDdP9acbMYjbKIyJUhTvtzj601sVJOqgWdUxSdR/Ysc= go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.29.0/go.mod h1:BLbf7zbNIONBLPwvFnwNHGj4zge8uTCM/UPIVW1Mq2I= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= -go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/sdk/metric v1.29.0 h1:K2CfmJohnRgvZ9UAj2/FhIf/okdWcNdBwe1m8xFXiSY= -go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= @@ -1842,8 +1842,8 @@ golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4= -golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= -golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= From b6bef1e335026ce6e9e141f8ddc96ed57445cc8a Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Tue, 27 May 2025 10:36:08 -0400 Subject: [PATCH 104/109] expose setup of clio setup config (#2689) Signed-off-by: Alex Goodman --- cmd/grype/cli/cli.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/grype/cli/cli.go b/cmd/grype/cli/cli.go index 76ddf5a54e3..49d887db338 100644 --- a/cmd/grype/cli/cli.go +++ b/cmd/grype/cli/cli.go @@ -34,8 +34,8 @@ func Command(id clio.Identification) *cobra.Command { return cmd } -func create(id clio.Identification) (clio.Application, *cobra.Command) { - clioCfg := clio.NewSetupConfig(id). +func SetupConfig(id clio.Identification) *clio.SetupConfig { + return 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 @@ -92,6 +92,10 @@ func create(id clio.Identification) (clio.Application, *cobra.Command) { } return 1 }) +} + +func create(id clio.Identification) (clio.Application, *cobra.Command) { + clioCfg := SetupConfig(id) app := clio.New(*clioCfg) From ffd268da2de9a22006a7f16c55d3cfbe4f988508 Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Wed, 28 May 2025 07:57:38 -0400 Subject: [PATCH 105/109] chore: move cyclonedx validation to unit test (#2690) Signed-off-by: Keith Zantow --- .binny.yaml | 8 - Taskfile.yaml | 6 - grype/presenter/cyclonedx/presenter_test.go | 70 + .../snapshot/TestCycloneDxPresenterDir.golden | 4 +- .../TestCycloneDxPresenterImage.golden | 4 +- grype/presenter/internal/test_helpers.go | 4 +- .../snapshot/TestJsonDirsPresenter.golden | 4 +- .../snapshot/TestJsonImgsPresenter.golden | 4 +- .../snapshot/TestPresenter_Present.golden | 4 +- schema/cyclonedx/.gitignore | 2 - schema/cyclonedx/Makefile | 14 - schema/cyclonedx/README.md | 20 - schema/cyclonedx/cyclonedx.json | 3800 -------- schema/cyclonedx/cyclonedx.xsd | 8290 ----------------- schema/cyclonedx/spdx.xsd | 3079 ------ 15 files changed, 82 insertions(+), 15231 deletions(-) delete mode 100644 schema/cyclonedx/.gitignore delete mode 100644 schema/cyclonedx/Makefile delete mode 100644 schema/cyclonedx/README.md delete mode 100644 schema/cyclonedx/cyclonedx.json delete mode 100644 schema/cyclonedx/cyclonedx.xsd delete mode 100644 schema/cyclonedx/spdx.xsd diff --git a/.binny.yaml b/.binny.yaml index 8bed612d6e1..f62b8c74dc2 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -47,14 +47,6 @@ tools: with: repo: sigstore/cosign - # used in integration tests to verify JSON schemas - - name: yajsv - version: - want: v1.4.1 - method: github-release - with: - repo: neilpa/yajsv - # used to release all artifacts - name: goreleaser version: diff --git a/Taskfile.yaml b/Taskfile.yaml index 5e08e9b61a0..f7cda008a4c 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -57,7 +57,6 @@ tasks: - task: check-licenses - task: lint - task: check-json-schema-drift - - task: validate-cyclonedx-schema # TODO: while developing v6, we need to disable this check (since v5 and v6 are imported in the same codebase) # - task: validate-grype-db-schema @@ -177,11 +176,6 @@ tasks: cmds: - .github/scripts/json-schema-drift-check.sh - validate-cyclonedx-schema: - desc: Run integration tests - cmds: - - "cd schema/cyclonedx && make" - validate-grype-db-schema: desc: Ensure the codebase is only referencing a single grype-db schema version (multiple is not allowed) cmds: diff --git a/grype/presenter/cyclonedx/presenter_test.go b/grype/presenter/cyclonedx/presenter_test.go index 6c4637e658c..093bb92304e 100644 --- a/grype/presenter/cyclonedx/presenter_test.go +++ b/grype/presenter/cyclonedx/presenter_test.go @@ -3,6 +3,9 @@ package cyclonedx import ( "bytes" "flag" + "fmt" + "os/exec" + "strings" "testing" "github.com/google/go-cmp/cmp" @@ -16,6 +19,73 @@ import ( ) var update = flag.Bool("update", false, "update the *.golden files for cyclonedx presenters") +var validatorImage = "cyclonedx/cyclonedx-cli:0.27.2@sha256:829c9ea8f2104698bc3c1228575bfa495f6cc4ec151329323c013ca94408477f" + +func Test_CycloneDX_Valid(t *testing.T) { + tests := []struct { + name string + scheme internal.SyftSource + }{ + { + name: "json directory", + scheme: internal.DirectorySource, + }, + { + name: "json image", + scheme: internal.ImageSource, + }, + { + name: "xml directory", + scheme: internal.DirectorySource, + }, + { + name: "xml image", + scheme: internal.ImageSource, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + format := strings.Split(tc.name, " ")[0] + var buffer bytes.Buffer + + pb := internal.GeneratePresenterConfig(t, tc.scheme) + + var pres *Presenter + switch format { + case "json": + pres = NewJSONPresenter(pb) + case "xml": + pres = NewXMLPresenter(pb) + default: + t.Fatalf("invalid format: %s", format) + } + + err := pres.Present(&buffer) + require.NoError(t, err) + + contents := buffer.String() + + cmd := exec.Command("docker", "run", "--rm", "-i", "--entrypoint", "/bin/sh", validatorImage, + "-c", fmt.Sprintf("tee &> /dev/null && cyclonedx validate --input-version v1_6 --fail-on-errors --input-format %s", format)) + + out := bytes.Buffer{} + cmd.Stdout = &out + cmd.Stderr = &out + + // pipe to the docker command + cmd.Stdin = strings.NewReader(contents) + + err = cmd.Run() + if err != nil || cmd.ProcessState.ExitCode() != 0 { + // not valid + t.Fatalf("error validating CycloneDX %s document: %s \nBOM:\n%s", format, out.String(), contents) + } + }) + } +} func Test_noTypedNils(t *testing.T) { s := sbom.SBOM{ diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden index 34638ea5544..3be118a6c54 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterDir.golden @@ -28,7 +28,7 @@ "type": "library", "name": "package-1", "version": "1.1.1", - "cpe": "cpe:2.3:a:anchore:engine:0.9.2:*:*:python:*:*:*:*", + "cpe": "cpe:2.3:a:anchore:engine:0.9.2:*:*:en:*:*:*:*", "properties": [ { "name": "syft:package:type", @@ -73,7 +73,7 @@ } } ], - "cpe": "cpe:2.3:a:anchore:engine:2.2.2:*:*:python:*:*:*:*", + "cpe": "cpe:2.3:a:anchore:engine:2.2.2:*:*:en:*:*:*:*", "purl": "pkg:deb/package-2@2.2.2", "properties": [ { diff --git a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden index bafdf481d93..96c6d99278a 100644 --- a/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden +++ b/grype/presenter/cyclonedx/test-fixtures/snapshot/TestCycloneDxPresenterImage.golden @@ -29,7 +29,7 @@ "type": "library", "name": "package-1", "version": "1.1.1", - "cpe": "cpe:2.3:a:anchore:engine:0.9.2:*:*:python:*:*:*:*", + "cpe": "cpe:2.3:a:anchore:engine:0.9.2:*:*:en:*:*:*:*", "properties": [ { "name": "syft:package:type", @@ -74,7 +74,7 @@ } } ], - "cpe": "cpe:2.3:a:anchore:engine:2.2.2:*:*:python:*:*:*:*", + "cpe": "cpe:2.3:a:anchore:engine:2.2.2:*:*:en:*:*:*:*", "purl": "pkg:deb/package-2@2.2.2", "properties": [ { diff --git a/grype/presenter/internal/test_helpers.go b/grype/presenter/internal/test_helpers.go index aa2b528abb8..4011591e1c7 100644 --- a/grype/presenter/internal/test_helpers.go +++ b/grype/presenter/internal/test_helpers.go @@ -393,7 +393,7 @@ func generatePackages(t *testing.T) []syftPkg.Package { Vendor: "anchore", Product: "engine", Version: "0.9.2", - Language: "python", + Language: "en", }, }, }, @@ -415,7 +415,7 @@ func generatePackages(t *testing.T) []syftPkg.Package { Vendor: "anchore", Product: "engine", Version: "2.2.2", - Language: "python", + Language: "en", }, }, }, diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden index d77afa9e331..8d2b44b5d39 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonDirsPresenter.golden @@ -70,7 +70,7 @@ "language": "", "licenses": [], "cpes": [ - "cpe:2.3:a:anchore:engine:0.9.2:*:*:python:*:*:*:*" + "cpe:2.3:a:anchore:engine:0.9.2:*:*:en:*:*:*:*" ], "purl": "", "upstreams": [], @@ -150,7 +150,7 @@ "MIT" ], "cpes": [ - "cpe:2.3:a:anchore:engine:2.2.2:*:*:python:*:*:*:*" + "cpe:2.3:a:anchore:engine:2.2.2:*:*:en:*:*:*:*" ], "purl": "pkg:deb/package-2@2.2.2", "upstreams": [] diff --git a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden index b49085fb678..d7669215c0c 100644 --- a/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden +++ b/grype/presenter/json/test-fixtures/snapshot/TestJsonImgsPresenter.golden @@ -70,7 +70,7 @@ "language": "", "licenses": [], "cpes": [ - "cpe:2.3:a:anchore:engine:0.9.2:*:*:python:*:*:*:*" + "cpe:2.3:a:anchore:engine:0.9.2:*:*:en:*:*:*:*" ], "purl": "", "upstreams": [], @@ -150,7 +150,7 @@ "MIT" ], "cpes": [ - "cpe:2.3:a:anchore:engine:2.2.2:*:*:python:*:*:*:*" + "cpe:2.3:a:anchore:engine:2.2.2:*:*:en:*:*:*:*" ], "purl": "pkg:deb/package-2@2.2.2", "upstreams": [] diff --git a/grype/presenter/template/test-fixtures/snapshot/TestPresenter_Present.golden b/grype/presenter/template/test-fixtures/snapshot/TestPresenter_Present.golden index 0ac37fa30dc..6ac980058c4 100644 --- a/grype/presenter/template/test-fixtures/snapshot/TestPresenter_Present.golden +++ b/grype/presenter/template/test-fixtures/snapshot/TestPresenter_Present.golden @@ -2,11 +2,11 @@ Identified distro as centos version 8.0. Vulnerability: CVE-1999-0001 Severity: Low Package: package-1 version 1.1.1 (rpm) - CPEs: ["cpe:2.3:a:anchore:engine:0.9.2:*:*:python:*:*:*:*"] + CPEs: ["cpe:2.3:a:anchore:engine:0.9.2:*:*:en:*:*:*:*"] Matched by: dpkg-matcher Vulnerability: CVE-1999-0002 Severity: Critical Package: package-2 version 2.2.2 (deb) - CPEs: ["cpe:2.3:a:anchore:engine:2.2.2:*:*:python:*:*:*:*"] + CPEs: ["cpe:2.3:a:anchore:engine:2.2.2:*:*:en:*:*:*:*"] Matched by: dpkg-matcher diff --git a/schema/cyclonedx/.gitignore b/schema/cyclonedx/.gitignore deleted file mode 100644 index 472d439fa14..00000000000 --- a/schema/cyclonedx/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -bom.xml -bom.json diff --git a/schema/cyclonedx/Makefile b/schema/cyclonedx/Makefile deleted file mode 100644 index 028ea982c1d..00000000000 --- a/schema/cyclonedx/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -.DEFAULT_GOAL := validate-schema - -.PHONY: validate-schema -validate-schema: validate-schema-xml validate-schema-json - -.PHONY: validate-schema-xml -validate-schema-xml: - go run ../../cmd/grype -c ../../test/grype-test-config.yaml ubuntu:latest -v -o cyclonedx-xml > bom.xml - xmllint --noout --schema ./cyclonedx.xsd bom.xml - -.PHONY: validate-schema-json -validate-schema-json: - go run ../../cmd/grype -c ../../test/grype-test-config.yaml ubuntu:latest -v -o cyclonedx-json > bom.json - ../../.tool/yajsv -s cyclonedx.json bom.json diff --git a/schema/cyclonedx/README.md b/schema/cyclonedx/README.md deleted file mode 100644 index db17f8414e6..00000000000 --- a/schema/cyclonedx/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# CycloneDX Schemas - -`grype` generates a CycloneDX output. This validation is similar to what is done in `syft`, validating output against CycloneDX schemas. - -Validation is done with `xmllint`, which requires a copy of all schemas because it can't work with HTTP references. The schemas are modified to reference local copies of dependent schemas. - -## Updating - -You will need to go to https://github.com/CycloneDX/specification/blob/1.5/schema and download the latest `bom-#.#.xsd` and `spdx.xsd`. - -Additionally, for `xmllint` to function you will need to patch the bom schema with the location to the SPDX schema by changing: - -```xml - -``` - -To: -```xml - -``` diff --git a/schema/cyclonedx/cyclonedx.json b/schema/cyclonedx/cyclonedx.json deleted file mode 100644 index cffc2b99136..00000000000 --- a/schema/cyclonedx/cyclonedx.json +++ /dev/null @@ -1,3800 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "http://cyclonedx.org/schema/bom-1.6.schema.json", - "type": "object", - "title": "CycloneDX Software Bill of Materials Standard", - "$comment" : "CycloneDX JSON schema is published under the terms of the Apache License 2.0.", - "required": [ - "bomFormat", - "specVersion", - "version" - ], - "additionalProperties": false, - "properties": { - "$schema": { - "type": "string", - "enum": [ - "http://cyclonedx.org/schema/bom-1.6.schema.json" - ] - }, - "bomFormat": { - "type": "string", - "title": "BOM Format", - "description": "Specifies the format of the BOM. This helps to identify the file as CycloneDX since BOMs do not have a filename convention nor does JSON schema support namespaces. This value MUST be \"CycloneDX\".", - "enum": [ - "CycloneDX" - ] - }, - "specVersion": { - "type": "string", - "title": "CycloneDX Specification Version", - "description": "The version of the CycloneDX specification a BOM conforms to (starting at version 1.2).", - "examples": ["1.5"] - }, - "serialNumber": { - "type": "string", - "title": "BOM Serial Number", - "description": "Every BOM generated SHOULD have a unique serial number, even if the contents of the BOM have not changed over time. If specified, the serial number MUST conform to RFC-4122. Use of serial numbers are RECOMMENDED.", - "examples": ["urn:uuid:3e671687-395b-41f5-a30f-a58921a69b79"], - "pattern": "^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$" - }, - "version": { - "type": "integer", - "title": "BOM Version", - "description": "Whenever an existing BOM is modified, either manually or through automated processes, the version of the BOM SHOULD be incremented by 1. When a system is presented with multiple BOMs with identical serial numbers, the system SHOULD use the most recent version of the BOM. The default version is '1'.", - "minimum": 1, - "default": 1, - "examples": [1] - }, - "metadata": { - "$ref": "#/definitions/metadata", - "title": "BOM Metadata", - "description": "Provides additional information about a BOM." - }, - "components": { - "type": "array", - "items": {"$ref": "#/definitions/component"}, - "uniqueItems": true, - "title": "Components", - "description": "A list of software and hardware components." - }, - "services": { - "type": "array", - "items": {"$ref": "#/definitions/service"}, - "uniqueItems": true, - "title": "Services", - "description": "A list of services. This may include microservices, function-as-a-service, and other types of network or intra-process services." - }, - "externalReferences": { - "type": "array", - "items": {"$ref": "#/definitions/externalReference"}, - "title": "External References", - "description": "External references provide a way to document systems, sites, and information that may be relevant, but are not included with the BOM. They may also establish specific relationships within or external to the BOM." - }, - "dependencies": { - "type": "array", - "items": {"$ref": "#/definitions/dependency"}, - "uniqueItems": true, - "title": "Dependencies", - "description": "Provides the ability to document dependency relationships." - }, - "compositions": { - "type": "array", - "items": {"$ref": "#/definitions/compositions"}, - "uniqueItems": true, - "title": "Compositions", - "description": "Compositions describe constituent parts (including components, services, and dependency relationships) and their completeness. The completeness of vulnerabilities expressed in a BOM may also be described." - }, - "vulnerabilities": { - "type": "array", - "items": {"$ref": "#/definitions/vulnerability"}, - "uniqueItems": true, - "title": "Vulnerabilities", - "description": "Vulnerabilities identified in components or services." - }, - "annotations": { - "type": "array", - "items": {"$ref": "#/definitions/annotations"}, - "uniqueItems": true, - "title": "Annotations", - "description": "Comments made by people, organizations, or tools about any object with a bom-ref, such as components, services, vulnerabilities, or the BOM itself. Unlike inventory information, annotations may contain opinion or commentary from various stakeholders. Annotations may be inline (with inventory) or externalized via BOM-Link, and may optionally be signed." - }, - "formulation": { - "type": "array", - "items": {"$ref": "#/definitions/formula"}, - "uniqueItems": true, - "title": "Formulation", - "description": "Describes how a component or service was manufactured or deployed. This is achieved through the use of formulas, workflows, tasks, and steps, which declare the precise steps to reproduce along with the observed formulas describing the steps which transpired in the manufacturing process." - }, - "properties": { - "type": "array", - "title": "Properties", - "description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is OPTIONAL.", - "items": { - "$ref": "#/definitions/property" - } - }, - "signature": { - "$ref": "#/definitions/signature", - "title": "Signature", - "description": "Enveloped signature in [JSON Signature Format (JSF)](https://cyberphone.github.io/doc/security/jsf.html)." - } - }, - "definitions": { - "refType": { - "description": "Identifier for referable and therefore interlink-able elements.", - "type": "string", - "minLength": 1, - "$comment": "value SHOULD not start with the BOM-Link intro 'urn:cdx:'" - }, - "refLinkType": { - "description": "Descriptor for an element identified by the attribute 'bom-ref' in the same BOM document.\nIn contrast to `bomLinkElementType`.", - "allOf": [{"$ref": "#/definitions/refType"}] - }, - "bomLinkDocumentType": { - "title": "BOM-Link Document", - "description": "Descriptor for another BOM document. See https://cyclonedx.org/capabilities/bomlink/", - "type": "string", - "format": "iri-reference", - "pattern": "^urn:cdx:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/[1-9][0-9]*$", - "$comment": "part of the pattern is based on `bom.serialNumber`'s pattern" - }, - "bomLinkElementType": { - "title": "BOM-Link Element", - "description": "Descriptor for an element in a BOM document. See https://cyclonedx.org/capabilities/bomlink/", - "type": "string", - "format": "iri-reference", - "pattern": "^urn:cdx:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/[1-9][0-9]*#.+$", - "$comment": "part of the pattern is based on `bom.serialNumber`'s pattern" - }, - "bomLink": { - "anyOf": [ - { - "title": "BOM-Link Document", - "$ref": "#/definitions/bomLinkDocumentType" - }, - { - "title": "BOM-Link Element", - "$ref": "#/definitions/bomLinkElementType" - } - ] - }, - "metadata": { - "type": "object", - "title": "BOM Metadata Object", - "additionalProperties": false, - "properties": { - "timestamp": { - "type": "string", - "format": "date-time", - "title": "Timestamp", - "description": "The date and time (timestamp) when the BOM was created." - }, - "lifecycles": { - "type": "array", - "title": "Lifecycles", - "description": "", - "items": { - "type": "object", - "title": "Lifecycle", - "description": "The product lifecycle(s) that this BOM represents.", - "oneOf": [ - { - "required": ["phase"], - "additionalProperties": false, - "properties": { - "phase": { - "type": "string", - "title": "Phase", - "description": "A pre-defined phase in the product lifecycle.\n\n* __design__ = BOM produced early in the development lifecycle containing inventory of components and services that are proposed or planned to be used. The inventory may need to be procured, retrieved, or resourced prior to use.\n* __pre-build__ = BOM consisting of information obtained prior to a build process and may contain source files and development artifacts and manifests. The inventory may need to be resolved and retrieved prior to use.\n* __build__ = BOM consisting of information obtained during a build process where component inventory is available for use. The precise versions of resolved components are usually available at this time as well as the provenance of where the components were retrieved from.\n* __post-build__ = BOM consisting of information obtained after a build process has completed and the resulting components(s) are available for further analysis. Built components may exist as the result of a CI/CD process, may have been installed or deployed to a system or device, and may need to be retrieved or extracted from the system or device.\n* __operations__ = BOM produced that represents inventory that is running and operational. This may include staging or production environments and will generally encompass multiple SBOMs describing the applications and operating system, along with HBOMs describing the hardware that makes up the system. Operations Bill of Materials (OBOM) can provide full-stack inventory of runtime environments, configurations, and additional dependencies.\n* __discovery__ = BOM consisting of information observed through network discovery providing point-in-time enumeration of embedded, on-premise, and cloud-native services such as server applications, connected devices, microservices, and serverless functions.\n* __decommission__ = BOM containing inventory that will be, or has been retired from operations.", - "enum": [ - "design", - "pre-build", - "build", - "post-build", - "operations", - "discovery", - "decommission" - ] - } - } - }, - { - "required": ["name"], - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "title": "Name", - "description": "The name of the lifecycle phase" - }, - "description": { - "type": "string", - "title": "Description", - "description": "The description of the lifecycle phase" - } - } - } - ] - } - }, - "tools": { - "oneOf": [ - { - "type": "object", - "title": "Creation Tools", - "description": "The tool(s) used in the creation of the BOM.", - "additionalProperties": false, - "properties": { - "components": { - "type": "array", - "items": {"$ref": "#/definitions/component"}, - "uniqueItems": true, - "title": "Components", - "description": "A list of software and hardware components used as tools" - }, - "services": { - "type": "array", - "items": {"$ref": "#/definitions/service"}, - "uniqueItems": true, - "title": "Services", - "description": "A list of services used as tools. This may include microservices, function-as-a-service, and other types of network or intra-process services." - } - } - }, - { - "type": "array", - "title": "Creation Tools (legacy)", - "description": "[Deprecated] The tool(s) used in the creation of the BOM.", - "items": {"$ref": "#/definitions/tool"} - } - ] - }, - "authors" :{ - "type": "array", - "title": "Authors", - "description": "The person(s) who created the BOM. Authors are common in BOMs created through manual processes. BOMs created through automated means may not have authors.", - "items": {"$ref": "#/definitions/organizationalContact"} - }, - "component": { - "title": "Component", - "description": "The component that the BOM describes.", - "$ref": "#/definitions/component" - }, - "manufacture": { - "title": "Manufacture", - "description": "The organization that manufactured the component that the BOM describes.", - "$ref": "#/definitions/organizationalEntity" - }, - "supplier": { - "title": "Supplier", - "description": " The organization that supplied the component that the BOM describes. The supplier may often be the manufacturer, but may also be a distributor or repackager.", - "$ref": "#/definitions/organizationalEntity" - }, - "licenses": { - "title": "BOM License(s)", - "$ref": "#/definitions/licenseChoice" - }, - "properties": { - "type": "array", - "title": "Properties", - "description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is OPTIONAL.", - "items": {"$ref": "#/definitions/property"} - } - } - }, - "tool": { - "type": "object", - "title": "Tool", - "description": "[Deprecated] - DO NOT USE. This will be removed in a future version. This will be removed in a future version. Use component or service instead. Information about the automated or manual tool used", - "additionalProperties": false, - "properties": { - "vendor": { - "type": "string", - "title": "Tool Vendor", - "description": "The name of the vendor who created the tool" - }, - "name": { - "type": "string", - "title": "Tool Name", - "description": "The name of the tool" - }, - "version": { - "type": "string", - "title": "Tool Version", - "description": "The version of the tool" - }, - "hashes": { - "type": "array", - "items": {"$ref": "#/definitions/hash"}, - "title": "Hashes", - "description": "The hashes of the tool (if applicable)." - }, - "externalReferences": { - "type": "array", - "items": {"$ref": "#/definitions/externalReference"}, - "title": "External References", - "description": "External references provide a way to document systems, sites, and information that may be relevant, but are not included with the BOM. They may also establish specific relationships within or external to the BOM." - } - } - }, - "organizationalEntity": { - "type": "object", - "title": "Organizational Entity Object", - "description": "", - "additionalProperties": false, - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the object elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "name": { - "type": "string", - "title": "Name", - "description": "The name of the organization", - "examples": [ - "Example Inc." - ] - }, - "url": { - "type": "array", - "items": { - "type": "string", - "format": "iri-reference" - }, - "title": "URL", - "description": "The URL of the organization. Multiple URLs are allowed.", - "examples": ["https://example.com"] - }, - "contact": { - "type": "array", - "title": "Contact", - "description": "A contact at the organization. Multiple contacts are allowed.", - "items": {"$ref": "#/definitions/organizationalContact"} - } - } - }, - "organizationalContact": { - "type": "object", - "title": "Organizational Contact Object", - "description": "", - "additionalProperties": false, - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the object elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "name": { - "type": "string", - "title": "Name", - "description": "The name of a contact", - "examples": ["Contact name"] - }, - "email": { - "type": "string", - "format": "idn-email", - "title": "Email Address", - "description": "The email address of the contact.", - "examples": ["firstname.lastname@example.com"] - }, - "phone": { - "type": "string", - "title": "Phone", - "description": "The phone number of the contact.", - "examples": ["800-555-1212"] - } - } - }, - "component": { - "type": "object", - "title": "Component Object", - "required": [ - "type", - "name" - ], - "additionalProperties": false, - "properties": { - "type": { - "type": "string", - "enum": [ - "application", - "framework", - "library", - "container", - "platform", - "operating-system", - "device", - "device-driver", - "firmware", - "file", - "machine-learning-model", - "data" - ], - "title": "Component Type", - "description": "Specifies the type of component. For software components, classify as application if no more specific appropriate classification is available or cannot be determined for the component. Types include:\n\n* __application__ = A software application. Refer to [https://en.wikipedia.org/wiki/Application_software](https://en.wikipedia.org/wiki/Application_software) for information about applications.\n* __framework__ = A software framework. Refer to [https://en.wikipedia.org/wiki/Software_framework](https://en.wikipedia.org/wiki/Software_framework) for information on how frameworks vary slightly from libraries.\n* __library__ = A software library. Refer to [https://en.wikipedia.org/wiki/Library_(computing)](https://en.wikipedia.org/wiki/Library_(computing))\n for information about libraries. All third-party and open source reusable components will likely be a library. If the library also has key features of a framework, then it should be classified as a framework. If not, or is unknown, then specifying library is RECOMMENDED.\n* __container__ = A packaging and/or runtime format, not specific to any particular technology, which isolates software inside the container from software outside of a container through virtualization technology. Refer to [https://en.wikipedia.org/wiki/OS-level_virtualization](https://en.wikipedia.org/wiki/OS-level_virtualization)\n* __platform__ = A runtime environment which interprets or executes software. This may include runtimes such as those that execute bytecode or low-code/no-code application platforms.\n* __operating-system__ = A software operating system without regard to deployment model (i.e. installed on physical hardware, virtual machine, image, etc) Refer to [https://en.wikipedia.org/wiki/Operating_system](https://en.wikipedia.org/wiki/Operating_system)\n* __device__ = A hardware device such as a processor, or chip-set. A hardware device containing firmware SHOULD include a component for the physical hardware itself, and another component of type 'firmware' or 'operating-system' (whichever is relevant), describing information about the software running on the device.\n See also the list of [known device properties](https://github.com/CycloneDX/cyclonedx-property-taxonomy/blob/main/cdx/device.md).\n* __device-driver__ = A special type of software that operates or controls a particular type of device. Refer to [https://en.wikipedia.org/wiki/Device_driver](https://en.wikipedia.org/wiki/Device_driver)\n* __firmware__ = A special type of software that provides low-level control over a devices hardware. Refer to [https://en.wikipedia.org/wiki/Firmware](https://en.wikipedia.org/wiki/Firmware)\n* __file__ = A computer file. Refer to [https://en.wikipedia.org/wiki/Computer_file](https://en.wikipedia.org/wiki/Computer_file) for information about files.\n* __machine-learning-model__ = A model based on training data that can make predictions or decisions without being explicitly programmed to do so.\n* __data__ = A collection of discrete values that convey information.", - "examples": ["library"] - }, - "mime-type": { - "type": "string", - "title": "Mime-Type", - "description": "The optional mime-type of the component. When used on file components, the mime-type can provide additional context about the kind of file being represented such as an image, font, or executable. Some library or framework components may also have an associated mime-type.", - "examples": ["image/jpeg"], - "pattern": "^[-+a-z0-9.]+/[-+a-z0-9.]+$" - }, - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the component elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "supplier": { - "title": "Component Supplier", - "description": " The organization that supplied the component. The supplier may often be the manufacturer, but may also be a distributor or repackager.", - "$ref": "#/definitions/organizationalEntity" - }, - "author": { - "type": "string", - "title": "Component Author", - "description": "The person(s) or organization(s) that authored the component", - "examples": ["Acme Inc"] - }, - "publisher": { - "type": "string", - "title": "Component Publisher", - "description": "The person(s) or organization(s) that published the component", - "examples": ["Acme Inc"] - }, - "group": { - "type": "string", - "title": "Component Group", - "description": "The grouping name or identifier. This will often be a shortened, single name of the company or project that produced the component, or the source package or domain name. Whitespace and special characters should be avoided. Examples include: apache, org.apache.commons, and apache.org.", - "examples": ["com.acme"] - }, - "name": { - "type": "string", - "title": "Component Name", - "description": "The name of the component. This will often be a shortened, single name of the component. Examples: commons-lang3 and jquery", - "examples": ["tomcat-catalina"] - }, - "version": { - "type": "string", - "title": "Component Version", - "description": "The component version. The version should ideally comply with semantic versioning but is not enforced.", - "examples": ["9.0.14"] - }, - "description": { - "type": "string", - "title": "Component Description", - "description": "Specifies a description for the component" - }, - "scope": { - "type": "string", - "enum": [ - "required", - "optional", - "excluded" - ], - "title": "Component Scope", - "description": "Specifies the scope of the component. If scope is not specified, 'required' scope SHOULD be assumed by the consumer of the BOM.", - "default": "required" - }, - "hashes": { - "type": "array", - "title": "Component Hashes", - "items": {"$ref": "#/definitions/hash"} - }, - "licenses": { - "$ref": "#/definitions/licenseChoice", - "title": "Component License(s)" - }, - "copyright": { - "type": "string", - "title": "Component Copyright", - "description": "A copyright notice informing users of the underlying claims to copyright ownership in a published work.", - "examples": ["Acme Inc"] - }, - "cpe": { - "type": "string", - "title": "Component Common Platform Enumeration (CPE)", - "description": "Specifies a well-formed CPE name that conforms to the CPE 2.2 or 2.3 specification. See [https://nvd.nist.gov/products/cpe](https://nvd.nist.gov/products/cpe)", - "examples": ["cpe:2.3:a:acme:component_framework:-:*:*:*:*:*:*:*"] - }, - "purl": { - "type": "string", - "title": "Component Package URL (purl)", - "description": "Specifies the package-url (purl). The purl, if specified, MUST be valid and conform to the specification defined at: [https://github.com/package-url/purl-spec](https://github.com/package-url/purl-spec)", - "examples": ["pkg:maven/com.acme/tomcat-catalina@9.0.14?packaging=jar"] - }, - "swid": { - "$ref": "#/definitions/swid", - "title": "SWID Tag", - "description": "Specifies metadata and content for [ISO-IEC 19770-2 Software Identification (SWID) Tags](https://www.iso.org/standard/65666.html)." - }, - "modified": { - "type": "boolean", - "title": "Component Modified From Original", - "description": "[Deprecated] - DO NOT USE. This will be removed in a future version. Use the pedigree element instead to supply information on exactly how the component was modified. A boolean value indicating if the component has been modified from the original. A value of true indicates the component is a derivative of the original. A value of false indicates the component has not been modified from the original." - }, - "pedigree": { - "type": "object", - "title": "Component Pedigree", - "description": "Component pedigree is a way to document complex supply chain scenarios where components are created, distributed, modified, redistributed, combined with other components, etc. Pedigree supports viewing this complex chain from the beginning, the end, or anywhere in the middle. It also provides a way to document variants where the exact relation may not be known.", - "additionalProperties": false, - "properties": { - "ancestors": { - "type": "array", - "title": "Ancestors", - "description": "Describes zero or more components in which a component is derived from. This is commonly used to describe forks from existing projects where the forked version contains a ancestor node containing the original component it was forked from. For example, Component A is the original component. Component B is the component being used and documented in the BOM. However, Component B contains a pedigree node with a single ancestor documenting Component A - the original component from which Component B is derived from.", - "items": {"$ref": "#/definitions/component"} - }, - "descendants": { - "type": "array", - "title": "Descendants", - "description": "Descendants are the exact opposite of ancestors. This provides a way to document all forks (and their forks) of an original or root component.", - "items": {"$ref": "#/definitions/component"} - }, - "variants": { - "type": "array", - "title": "Variants", - "description": "Variants describe relations where the relationship between the components are not known. For example, if Component A contains nearly identical code to Component B. They are both related, but it is unclear if one is derived from the other, or if they share a common ancestor.", - "items": {"$ref": "#/definitions/component"} - }, - "commits": { - "type": "array", - "title": "Commits", - "description": "A list of zero or more commits which provide a trail describing how the component deviates from an ancestor, descendant, or variant.", - "items": {"$ref": "#/definitions/commit"} - }, - "patches": { - "type": "array", - "title": "Patches", - "description": ">A list of zero or more patches describing how the component deviates from an ancestor, descendant, or variant. Patches may be complimentary to commits or may be used in place of commits.", - "items": {"$ref": "#/definitions/patch"} - }, - "notes": { - "type": "string", - "title": "Notes", - "description": "Notes, observations, and other non-structured commentary describing the components pedigree." - } - } - }, - "externalReferences": { - "type": "array", - "items": {"$ref": "#/definitions/externalReference"}, - "title": "External References", - "description": "External references provide a way to document systems, sites, and information that may be relevant, but are not included with the BOM. They may also establish specific relationships within or external to the BOM." - }, - "components": { - "type": "array", - "items": {"$ref": "#/definitions/component"}, - "uniqueItems": true, - "title": "Components", - "description": "A list of software and hardware components included in the parent component. This is not a dependency tree. It provides a way to specify a hierarchical representation of component assemblies, similar to system → subsystem → parts assembly in physical supply chains." - }, - "evidence": { - "$ref": "#/definitions/componentEvidence", - "title": "Evidence", - "description": "Provides the ability to document evidence collected through various forms of extraction or analysis." - }, - "releaseNotes": { - "$ref": "#/definitions/releaseNotes", - "title": "Release notes", - "description": "Specifies optional release notes." - }, - "modelCard": { - "$ref": "#/definitions/modelCard", - "title": "Machine Learning Model Card" - }, - "data": { - "type": "array", - "items": {"$ref": "#/definitions/componentData"}, - "title": "Data", - "description": "This object SHOULD be specified for any component of type `data` and MUST NOT be specified for other component types." - }, - "properties": { - "type": "array", - "title": "Properties", - "description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is OPTIONAL.", - "items": {"$ref": "#/definitions/property"} - }, - "signature": { - "$ref": "#/definitions/signature", - "title": "Signature", - "description": "Enveloped signature in [JSON Signature Format (JSF)](https://cyberphone.github.io/doc/security/jsf.html)." - } - } - }, - "swid": { - "type": "object", - "title": "SWID Tag", - "description": "Specifies metadata and content for ISO-IEC 19770-2 Software Identification (SWID) Tags.", - "required": [ - "tagId", - "name" - ], - "additionalProperties": false, - "properties": { - "tagId": { - "type": "string", - "title": "Tag ID", - "description": "Maps to the tagId of a SoftwareIdentity." - }, - "name": { - "type": "string", - "title": "Name", - "description": "Maps to the name of a SoftwareIdentity." - }, - "version": { - "type": "string", - "title": "Version", - "default": "0.0", - "description": "Maps to the version of a SoftwareIdentity." - }, - "tagVersion": { - "type": "integer", - "title": "Tag Version", - "default": 0, - "description": "Maps to the tagVersion of a SoftwareIdentity." - }, - "patch": { - "type": "boolean", - "title": "Patch", - "default": false, - "description": "Maps to the patch of a SoftwareIdentity." - }, - "text": { - "title": "Attachment text", - "description": "Specifies the metadata and content of the SWID tag.", - "$ref": "#/definitions/attachment" - }, - "url": { - "type": "string", - "title": "URL", - "description": "The URL to the SWID file.", - "format": "iri-reference" - } - } - }, - "attachment": { - "type": "object", - "title": "Attachment", - "description": "Specifies the metadata and content for an attachment.", - "required": [ - "content" - ], - "additionalProperties": false, - "properties": { - "contentType": { - "type": "string", - "title": "Content-Type", - "description": "Specifies the content type of the text. Defaults to text/plain if not specified.", - "default": "text/plain" - }, - "encoding": { - "type": "string", - "title": "Encoding", - "description": "Specifies the optional encoding the text is represented in.", - "enum": [ - "base64" - ] - }, - "content": { - "type": "string", - "title": "Attachment Text", - "description": "The attachment data. Proactive controls such as input validation and sanitization should be employed to prevent misuse of attachment text." - } - } - }, - "hash": { - "type": "object", - "title": "Hash Objects", - "required": [ - "alg", - "content" - ], - "additionalProperties": false, - "properties": { - "alg": { - "$ref": "#/definitions/hash-alg" - }, - "content": { - "$ref": "#/definitions/hash-content" - } - } - }, - "hash-alg": { - "type": "string", - "enum": [ - "MD5", - "SHA-1", - "SHA-256", - "SHA-384", - "SHA-512", - "SHA3-256", - "SHA3-384", - "SHA3-512", - "BLAKE2b-256", - "BLAKE2b-384", - "BLAKE2b-512", - "BLAKE3" - ], - "title": "Hash Algorithm" - }, - "hash-content": { - "type": "string", - "title": "Hash Content (value)", - "examples": ["3942447fac867ae5cdb3229b658f4d48"], - "pattern": "^([a-fA-F0-9]{32}|[a-fA-F0-9]{40}|[a-fA-F0-9]{64}|[a-fA-F0-9]{96}|[a-fA-F0-9]{128})$" - }, - "license": { - "type": "object", - "title": "License Object", - "oneOf": [ - { - "required": ["id"] - }, - { - "required": ["name"] - } - ], - "additionalProperties": false, - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the license elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "id": { - "$ref": "spdx.schema.json", - "title": "License ID (SPDX)", - "description": "A valid SPDX license ID", - "examples": ["Apache-2.0"] - }, - "name": { - "type": "string", - "title": "License Name", - "description": "If SPDX does not define the license used, this field may be used to provide the license name", - "examples": ["Acme Software License"] - }, - "text": { - "title": "License text", - "description": "An optional way to include the textual content of a license.", - "$ref": "#/definitions/attachment" - }, - "url": { - "type": "string", - "title": "License URL", - "description": "The URL to the license file. If specified, a 'license' externalReference should also be specified for completeness", - "examples": ["https://www.apache.org/licenses/LICENSE-2.0.txt"], - "format": "iri-reference" - }, - "licensing": { - "type": "object", - "title": "Licensing information", - "description": "Licensing details describing the licensor/licensee, license type, renewal and expiration dates, and other important metadata", - "additionalProperties": false, - "properties": { - "altIds": { - "type": "array", - "title": "Alternate License Identifiers", - "description": "License identifiers that may be used to manage licenses and their lifecycle", - "items": { - "type": "string" - } - }, - "licensor": { - "title": "Licensor", - "description": "The individual or organization that grants a license to another individual or organization", - "type": "object", - "additionalProperties": false, - "properties": { - "organization": { - "title": "Licensor (Organization)", - "description": "The organization that granted the license", - "$ref": "#/definitions/organizationalEntity" - }, - "individual": { - "title": "Licensor (Individual)", - "description": "The individual, not associated with an organization, that granted the license", - "$ref": "#/definitions/organizationalContact" - } - }, - "oneOf":[ - { - "required": ["organization"] - }, - { - "required": ["individual"] - } - ] - }, - "licensee": { - "title": "Licensee", - "description": "The individual or organization for which a license was granted to", - "type": "object", - "additionalProperties": false, - "properties": { - "organization": { - "title": "Licensee (Organization)", - "description": "The organization that was granted the license", - "$ref": "#/definitions/organizationalEntity" - }, - "individual": { - "title": "Licensee (Individual)", - "description": "The individual, not associated with an organization, that was granted the license", - "$ref": "#/definitions/organizationalContact" - } - }, - "oneOf":[ - { - "required": ["organization"] - }, - { - "required": ["individual"] - } - ] - }, - "purchaser": { - "title": "Purchaser", - "description": "The individual or organization that purchased the license", - "type": "object", - "additionalProperties": false, - "properties": { - "organization": { - "title": "Purchaser (Organization)", - "description": "The organization that purchased the license", - "$ref": "#/definitions/organizationalEntity" - }, - "individual": { - "title": "Purchaser (Individual)", - "description": "The individual, not associated with an organization, that purchased the license", - "$ref": "#/definitions/organizationalContact" - } - }, - "oneOf":[ - { - "required": ["organization"] - }, - { - "required": ["individual"] - } - ] - }, - "purchaseOrder": { - "type": "string", - "title": "Purchase Order", - "description": "The purchase order identifier the purchaser sent to a supplier or vendor to authorize a purchase" - }, - "licenseTypes": { - "type": "array", - "title": "License Type", - "description": "The type of license(s) that was granted to the licensee\n\n* __academic__ = A license that grants use of software solely for the purpose of education or research.\n* __appliance__ = A license covering use of software embedded in a specific piece of hardware.\n* __client-access__ = A Client Access License (CAL) allows client computers to access services provided by server software.\n* __concurrent-user__ = A Concurrent User license (aka floating license) limits the number of licenses for a software application and licenses are shared among a larger number of users.\n* __core-points__ = A license where the core of a computer's processor is assigned a specific number of points.\n* __custom-metric__ = A license for which consumption is measured by non-standard metrics.\n* __device__ = A license that covers a defined number of installations on computers and other types of devices.\n* __evaluation__ = A license that grants permission to install and use software for trial purposes.\n* __named-user__ = A license that grants access to the software to one or more pre-defined users.\n* __node-locked__ = A license that grants access to the software on one or more pre-defined computers or devices.\n* __oem__ = An Original Equipment Manufacturer license that is delivered with hardware, cannot be transferred to other hardware, and is valid for the life of the hardware.\n* __perpetual__ = A license where the software is sold on a one-time basis and the licensee can use a copy of the software indefinitely.\n* __processor-points__ = A license where each installation consumes points per processor.\n* __subscription__ = A license where the licensee pays a fee to use the software or service.\n* __user__ = A license that grants access to the software or service by a specified number of users.\n* __other__ = Another license type.\n", - "items": { - "type": "string", - "enum": [ - "academic", - "appliance", - "client-access", - "concurrent-user", - "core-points", - "custom-metric", - "device", - "evaluation", - "named-user", - "node-locked", - "oem", - "perpetual", - "processor-points", - "subscription", - "user", - "other" - ] - } - }, - "lastRenewal": { - "type": "string", - "format": "date-time", - "title": "Last Renewal", - "description": "The timestamp indicating when the license was last renewed. For new purchases, this is often the purchase or acquisition date. For non-perpetual licenses or subscriptions, this is the timestamp of when the license was last renewed." - }, - "expiration": { - "type": "string", - "format": "date-time", - "title": "Expiration", - "description": "The timestamp indicating when the current license expires (if applicable)." - } - } - }, - "properties": { - "type": "array", - "title": "Properties", - "description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is OPTIONAL.", - "items": {"$ref": "#/definitions/property"} - } - } - }, - "licenseChoice": { - "title": "License Choice", - "description": "EITHER (list of SPDX licenses and/or named licenses) OR (tuple of one SPDX License Expression)", - "type": "array", - "oneOf": [ - { - "title": "Multiple licenses", - "description": "A list of SPDX licenses and/or named licenses.", - "type": "array", - "items": { - "type": "object", - "required": ["license"], - "additionalProperties": false, - "properties": { - "license": {"$ref": "#/definitions/license"} - } - } - }, - { - "title": "SPDX License Expression", - "description": "A tuple of exactly one SPDX License Expression.", - "type": "array", - "additionalItems": false, - "minItems": 1, - "maxItems": 1, - "items": [{ - "type": "object", - "additionalProperties": false, - "required": ["expression"], - "properties": { - "expression": { - "type": "string", - "title": "SPDX License Expression", - "examples": [ - "Apache-2.0 AND (MIT OR GPL-2.0-only)", - "GPL-3.0-only WITH Classpath-exception-2.0" - ] - }, - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the license elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - } - } - }] - } - ] - }, - "commit": { - "type": "object", - "title": "Commit", - "description": "Specifies an individual commit", - "additionalProperties": false, - "properties": { - "uid": { - "type": "string", - "title": "UID", - "description": "A unique identifier of the commit. This may be version control specific. For example, Subversion uses revision numbers whereas git uses commit hashes." - }, - "url": { - "type": "string", - "title": "URL", - "description": "The URL to the commit. This URL will typically point to a commit in a version control system.", - "format": "iri-reference" - }, - "author": { - "title": "Author", - "description": "The author who created the changes in the commit", - "$ref": "#/definitions/identifiableAction" - }, - "committer": { - "title": "Committer", - "description": "The person who committed or pushed the commit", - "$ref": "#/definitions/identifiableAction" - }, - "message": { - "type": "string", - "title": "Message", - "description": "The text description of the contents of the commit" - } - } - }, - "patch": { - "type": "object", - "title": "Patch", - "description": "Specifies an individual patch", - "required": [ - "type" - ], - "additionalProperties": false, - "properties": { - "type": { - "type": "string", - "enum": [ - "unofficial", - "monkey", - "backport", - "cherry-pick" - ], - "title": "Type", - "description": "Specifies the purpose for the patch including the resolution of defects, security issues, or new behavior or functionality.\n\n* __unofficial__ = A patch which is not developed by the creators or maintainers of the software being patched. Refer to [https://en.wikipedia.org/wiki/Unofficial_patch](https://en.wikipedia.org/wiki/Unofficial_patch)\n* __monkey__ = A patch which dynamically modifies runtime behavior. Refer to [https://en.wikipedia.org/wiki/Monkey_patch](https://en.wikipedia.org/wiki/Monkey_patch)\n* __backport__ = A patch which takes code from a newer version of software and applies it to older versions of the same software. Refer to [https://en.wikipedia.org/wiki/Backporting](https://en.wikipedia.org/wiki/Backporting)\n* __cherry-pick__ = A patch created by selectively applying commits from other versions or branches of the same software." - }, - "diff": { - "title": "Diff", - "description": "The patch file (or diff) that show changes. Refer to [https://en.wikipedia.org/wiki/Diff](https://en.wikipedia.org/wiki/Diff)", - "$ref": "#/definitions/diff" - }, - "resolves": { - "type": "array", - "items": {"$ref": "#/definitions/issue"}, - "title": "Resolves", - "description": "A collection of issues the patch resolves" - } - } - }, - "diff": { - "type": "object", - "title": "Diff", - "description": "The patch file (or diff) that show changes. Refer to https://en.wikipedia.org/wiki/Diff", - "additionalProperties": false, - "properties": { - "text": { - "title": "Diff text", - "description": "Specifies the optional text of the diff", - "$ref": "#/definitions/attachment" - }, - "url": { - "type": "string", - "title": "URL", - "description": "Specifies the URL to the diff", - "format": "iri-reference" - } - } - }, - "issue": { - "type": "object", - "title": "Diff", - "description": "An individual issue that has been resolved.", - "required": [ - "type" - ], - "additionalProperties": false, - "properties": { - "type": { - "type": "string", - "enum": [ - "defect", - "enhancement", - "security" - ], - "title": "Type", - "description": "Specifies the type of issue" - }, - "id": { - "type": "string", - "title": "ID", - "description": "The identifier of the issue assigned by the source of the issue" - }, - "name": { - "type": "string", - "title": "Name", - "description": "The name of the issue" - }, - "description": { - "type": "string", - "title": "Description", - "description": "A description of the issue" - }, - "source": { - "type": "object", - "title": "Source", - "description": "The source of the issue where it is documented", - "additionalProperties": false, - "properties": { - "name": { - "type": "string", - "title": "Name", - "description": "The name of the source. For example 'National Vulnerability Database', 'NVD', and 'Apache'" - }, - "url": { - "type": "string", - "title": "URL", - "description": "The url of the issue documentation as provided by the source", - "format": "iri-reference" - } - } - }, - "references": { - "type": "array", - "items": { - "type": "string", - "format": "iri-reference" - }, - "title": "References", - "description": "A collection of URL's for reference. Multiple URLs are allowed.", - "examples": ["https://example.com"] - } - } - }, - "identifiableAction": { - "type": "object", - "title": "Identifiable Action", - "description": "Specifies an individual commit", - "additionalProperties": false, - "properties": { - "timestamp": { - "type": "string", - "format": "date-time", - "title": "Timestamp", - "description": "The timestamp in which the action occurred" - }, - "name": { - "type": "string", - "title": "Name", - "description": "The name of the individual who performed the action" - }, - "email": { - "type": "string", - "format": "idn-email", - "title": "E-mail", - "description": "The email address of the individual who performed the action" - } - } - }, - "externalReference": { - "type": "object", - "title": "External Reference", - "description": "External references provide a way to document systems, sites, and information that may be relevant, but are not included with the BOM. They may also establish specific relationships within or external to the BOM.", - "required": [ - "url", - "type" - ], - "additionalProperties": false, - "properties": { - "url": { - "anyOf": [ - { - "title": "URL", - "type": "string", - "format": "iri-reference" - }, - { - "title": "BOM-Link", - "$ref": "#/definitions/bomLink" - } - ], - "title": "URL", - "description": "The URI (URL or URN) to the external reference. External references are URIs and therefore can accept any URL scheme including https ([RFC-7230](https://www.ietf.org/rfc/rfc7230.txt)), mailto ([RFC-2368](https://www.ietf.org/rfc/rfc2368.txt)), tel ([RFC-3966](https://www.ietf.org/rfc/rfc3966.txt)), and dns ([RFC-4501](https://www.ietf.org/rfc/rfc4501.txt)). External references may also include formally registered URNs such as [CycloneDX BOM-Link](https://cyclonedx.org/capabilities/bomlink/) to reference CycloneDX BOMs or any object within a BOM. BOM-Link transforms applicable external references into relationships that can be expressed in a BOM or across BOMs." - }, - "comment": { - "type": "string", - "title": "Comment", - "description": "An optional comment describing the external reference" - }, - "type": { - "type": "string", - "title": "Type", - "description": "Specifies the type of external reference.\n\n* __vcs__ = Version Control System\n* __issue-tracker__ = Issue or defect tracking system, or an Application Lifecycle Management (ALM) system\n* __website__ = Website\n* __advisories__ = Security advisories\n* __bom__ = Bill of Materials (SBOM, OBOM, HBOM, SaaSBOM, etc)\n* __mailing-list__ = Mailing list or discussion group\n* __social__ = Social media account\n* __chat__ = Real-time chat platform\n* __documentation__ = Documentation, guides, or how-to instructions\n* __support__ = Community or commercial support\n* __distribution__ = Direct or repository download location\n* __distribution-intake__ = The location where a component was published to. This is often the same as \"distribution\" but may also include specialized publishing processes that act as an intermediary\n* __license__ = The URL to the license file. If a license URL has been defined in the license node, it should also be defined as an external reference for completeness\n* __build-meta__ = Build-system specific meta file (i.e. pom.xml, package.json, .nuspec, etc)\n* __build-system__ = URL to an automated build system\n* __release-notes__ = URL to release notes\n* __security-contact__ = Specifies a way to contact the maintainer, supplier, or provider in the event of a security incident. Common URIs include links to a disclosure procedure, a mailto (RFC-2368) that specifies an email address, a tel (RFC-3966) that specifies a phone number, or dns (RFC-4501) that specifies the records containing DNS Security TXT\n* __model-card__ = A model card describes the intended uses of a machine learning model, potential limitations, biases, ethical considerations, training parameters, datasets used to train the model, performance metrics, and other relevant data useful for ML transparency\n* __log__ = A record of events that occurred in a computer system or application, such as problems, errors, or information on current operations\n* __configuration__ = Parameters or settings that may be used by other components or services\n* __evidence__ = Information used to substantiate a claim\n* __formulation__ = Describes how a component or service was manufactured or deployed\n* __attestation__ = Human or machine-readable statements containing facts, evidence, or testimony\n* __threat-model__ = An enumeration of identified weaknesses, threats, and countermeasures, dataflow diagram (DFD), attack tree, and other supporting documentation in human-readable or machine-readable format\n* __adversary-model__ = The defined assumptions, goals, and capabilities of an adversary.\n* __risk-assessment__ = Identifies and analyzes the potential of future events that may negatively impact individuals, assets, and/or the environment. Risk assessments may also include judgments on the tolerability of each risk.\n* __vulnerability-assertion__ = A Vulnerability Disclosure Report (VDR) which asserts the known and previously unknown vulnerabilities that affect a component, service, or product including the analysis and findings describing the impact (or lack of impact) that the reported vulnerability has on a component, service, or product.\n* __exploitability-statement__ = A Vulnerability Exploitability eXchange (VEX) which asserts the known vulnerabilities that do not affect a product, product family, or organization, and optionally the ones that do. The VEX should include the analysis and findings describing the impact (or lack of impact) that the reported vulnerability has on the product, product family, or organization.\n* __pentest-report__ = Results from an authorized simulated cyberattack on a component or service, otherwise known as a penetration test\n* __static-analysis-report__ = SARIF or proprietary machine or human-readable report for which static analysis has identified code quality, security, and other potential issues with the source code\n* __dynamic-analysis-report__ = Dynamic analysis report that has identified issues such as vulnerabilities and misconfigurations\n* __runtime-analysis-report__ = Report generated by analyzing the call stack of a running application\n* __component-analysis-report__ = Report generated by Software Composition Analysis (SCA), container analysis, or other forms of component analysis\n* __maturity-report__ = Report containing a formal assessment of an organization, business unit, or team against a maturity model\n* __certification-report__ = Industry, regulatory, or other certification from an accredited (if applicable) certification body\n* __quality-metrics__ = Report or system in which quality metrics can be obtained\n* __codified-infrastructure__ = Code or configuration that defines and provisions virtualized infrastructure, commonly referred to as Infrastructure as Code (IaC)\n* __poam__ = Plans of Action and Milestones (POAM) compliment an \"attestation\" external reference. POAM is defined by NIST as a \"document that identifies tasks needing to be accomplished. It details resources required to accomplish the elements of the plan, any milestones in meeting the tasks and scheduled completion dates for the milestones\".\n* __other__ = Use this if no other types accurately describe the purpose of the external reference", - "enum": [ - "vcs", - "issue-tracker", - "website", - "advisories", - "bom", - "mailing-list", - "social", - "chat", - "documentation", - "support", - "distribution", - "distribution-intake", - "license", - "build-meta", - "build-system", - "release-notes", - "security-contact", - "model-card", - "log", - "configuration", - "evidence", - "formulation", - "attestation", - "threat-model", - "adversary-model", - "risk-assessment", - "vulnerability-assertion", - "exploitability-statement", - "pentest-report", - "static-analysis-report", - "dynamic-analysis-report", - "runtime-analysis-report", - "component-analysis-report", - "maturity-report", - "certification-report", - "codified-infrastructure", - "quality-metrics", - "poam", - "other" - ] - }, - "hashes": { - "type": "array", - "items": {"$ref": "#/definitions/hash"}, - "title": "Hashes", - "description": "The hashes of the external reference (if applicable)." - } - } - }, - "dependency": { - "type": "object", - "title": "Dependency", - "description": "Defines the direct dependencies of a component or service. Components or services that do not have their own dependencies MUST be declared as empty elements within the graph. Components or services that are not represented in the dependency graph MAY have unknown dependencies. It is RECOMMENDED that implementations assume this to be opaque and not an indicator of a object being dependency-free. It is RECOMMENDED to leverage compositions to indicate unknown dependency graphs.", - "required": [ - "ref" - ], - "additionalProperties": false, - "properties": { - "ref": { - "$ref": "#/definitions/refLinkType", - "title": "Reference", - "description": "References a component or service by its bom-ref attribute" - }, - "dependsOn": { - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/refLinkType" - }, - "title": "Depends On", - "description": "The bom-ref identifiers of the components or services that are dependencies of this dependency object." - } - } - }, - "service": { - "type": "object", - "title": "Service Object", - "required": [ - "name" - ], - "additionalProperties": false, - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the service elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "provider": { - "title": "Provider", - "description": "The organization that provides the service.", - "$ref": "#/definitions/organizationalEntity" - }, - "group": { - "type": "string", - "title": "Service Group", - "description": "The grouping name, namespace, or identifier. This will often be a shortened, single name of the company or project that produced the service or domain name. Whitespace and special characters should be avoided.", - "examples": ["com.acme"] - }, - "name": { - "type": "string", - "title": "Service Name", - "description": "The name of the service. This will often be a shortened, single name of the service.", - "examples": ["ticker-service"] - }, - "version": { - "type": "string", - "title": "Service Version", - "description": "The service version.", - "examples": ["1.0.0"] - }, - "description": { - "type": "string", - "title": "Service Description", - "description": "Specifies a description for the service" - }, - "endpoints": { - "type": "array", - "items": { - "type": "string", - "format": "iri-reference" - }, - "title": "Endpoints", - "description": "The endpoint URIs of the service. Multiple endpoints are allowed.", - "examples": ["https://example.com/api/v1/ticker"] - }, - "authenticated": { - "type": "boolean", - "title": "Authentication Required", - "description": "A boolean value indicating if the service requires authentication. A value of true indicates the service requires authentication prior to use. A value of false indicates the service does not require authentication." - }, - "x-trust-boundary": { - "type": "boolean", - "title": "Crosses Trust Boundary", - "description": "A boolean value indicating if use of the service crosses a trust zone or boundary. A value of true indicates that by using the service, a trust boundary is crossed. A value of false indicates that by using the service, a trust boundary is not crossed." - }, - "trustZone": { - "type": "string", - "title": "Trust Zone", - "description": "The name of the trust zone the service resides in." - }, - "data": { - "type": "array", - "items": {"$ref": "#/definitions/serviceData"}, - "title": "Data", - "description": "Specifies information about the data including the directional flow of data and the data classification." - }, - "licenses": { - "$ref": "#/definitions/licenseChoice", - "title": "Component License(s)" - }, - "externalReferences": { - "type": "array", - "items": {"$ref": "#/definitions/externalReference"}, - "title": "External References", - "description": "External references provide a way to document systems, sites, and information that may be relevant, but are not included with the BOM. They may also establish specific relationships within or external to the BOM." - }, - "services": { - "type": "array", - "items": {"$ref": "#/definitions/service"}, - "uniqueItems": true, - "title": "Services", - "description": "A list of services included or deployed behind the parent service. This is not a dependency tree. It provides a way to specify a hierarchical representation of service assemblies." - }, - "releaseNotes": { - "$ref": "#/definitions/releaseNotes", - "title": "Release notes", - "description": "Specifies optional release notes." - }, - "properties": { - "type": "array", - "title": "Properties", - "description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is OPTIONAL.", - "items": {"$ref": "#/definitions/property"} - }, - "signature": { - "$ref": "#/definitions/signature", - "title": "Signature", - "description": "Enveloped signature in [JSON Signature Format (JSF)](https://cyberphone.github.io/doc/security/jsf.html)." - } - } - }, - "serviceData": { - "type": "object", - "title": "Hash Objects", - "required": [ - "flow", - "classification" - ], - "additionalProperties": false, - "properties": { - "flow": { - "$ref": "#/definitions/dataFlowDirection", - "title": "Directional Flow", - "description": "Specifies the flow direction of the data. Direction is relative to the service. Inbound flow states that data enters the service. Outbound flow states that data leaves the service. Bi-directional states that data flows both ways, and unknown states that the direction is not known." - }, - "classification": { - "$ref": "#/definitions/dataClassification" - }, - "name": { - "type": "string", - "title": "Name", - "description": "Name for the defined data", - "examples": [ - "Credit card reporting" - ] - }, - "description": { - "type": "string", - "title": "Description", - "description": "Short description of the data content and usage", - "examples": [ - "Credit card information being exchanged in between the web app and the database" - ] - }, - "governance": { - "type": "object", - "title": "Data Governance", - "$ref": "#/definitions/dataGovernance" - }, - "source": { - "type": "array", - "items": { - "anyOf": [ - { - "title": "URL", - "type": "string", - "format": "iri-reference" - }, - { - "title": "BOM-Link Element", - "$ref": "#/definitions/bomLinkElementType" - } - ] - }, - "title": "Source", - "description": "The URI, URL, or BOM-Link of the components or services the data came in from" - }, - "destination": { - "type": "array", - "items": { - "anyOf": [ - { - "title": "URL", - "type": "string", - "format": "iri-reference" - }, - { - "title": "BOM-Link Element", - "$ref": "#/definitions/bomLinkElementType" - } - ] - }, - "title": "Destination", - "description": "The URI, URL, or BOM-Link of the components or services the data is sent to" - } - } - }, - "dataFlowDirection": { - "type": "string", - "enum": [ - "inbound", - "outbound", - "bi-directional", - "unknown" - ], - "title": "Data flow direction", - "description": "Specifies the flow direction of the data. Direction is relative to the service. Inbound flow states that data enters the service. Outbound flow states that data leaves the service. Bi-directional states that data flows both ways, and unknown states that the direction is not known." - }, - - "copyright": { - "type": "object", - "title": "Copyright", - "required": [ - "text" - ], - "additionalProperties": false, - "properties": { - "text": { - "type": "string", - "title": "Copyright Text" - } - } - }, - "componentEvidence": { - "type": "object", - "title": "Evidence", - "description": "Provides the ability to document evidence collected through various forms of extraction or analysis.", - "additionalProperties": false, - "properties": { - "identity": { - "type": "object", - "description": "Evidence that substantiates the identity of a component.", - "required": [ "field" ], - "additionalProperties": false, - "properties": { - "field": { - "type": "string", - "enum": [ - "group", "name", "version", "purl", "cpe", "swid", "hash" - ], - "title": "Field", - "description": "The identity field of the component which the evidence describes." - }, - "confidence": { - "type": "number", - "minimum": 0, - "maximum": 1, - "title": "Confidence", - "description": "The overall confidence of the evidence from 0 - 1, where 1 is 100% confidence." - }, - "methods": { - "type": "array", - "title": "Methods", - "description": "The methods used to extract and/or analyze the evidence.", - "items": { - "type": "object", - "required": [ - "technique" , - "confidence" - ], - "additionalProperties": false, - "properties": { - "technique": { - "title": "Technique", - "description": "The technique used in this method of analysis.", - "type": "string", - "enum": [ - "source-code-analysis", - "binary-analysis", - "manifest-analysis", - "ast-fingerprint", - "hash-comparison", - "instrumentation", - "dynamic-analysis", - "filename", - "attestation", - "other" - ] - }, - "confidence": { - "type": "number", - "minimum": 0, - "maximum": 1, - "title": "Confidence", - "description": "The confidence of the evidence from 0 - 1, where 1 is 100% confidence. Confidence is specific to the technique used. Each technique of analysis can have independent confidence." - }, - "value": { - "type": "string", - "title": "Value", - "description": "The value or contents of the evidence." - } - } - } - }, - "tools": { - "type": "array", - "uniqueItems": true, - "items": { - "anyOf": [ - { - "title": "Ref", - "$ref": "#/definitions/refLinkType" - }, - { - "title": "BOM-Link Element", - "$ref": "#/definitions/bomLinkElementType" - } - ] - }, - "title": "BOM References", - "description": "The object in the BOM identified by its bom-ref. This is often a component or service, but may be any object type supporting bom-refs. Tools used for analysis should already be defined in the BOM, either in the metadata/tools, components, or formulation." - } - } - }, - "occurrences": { - "type": "array", - "title": "Occurrences", - "description": "Evidence of individual instances of a component spread across multiple locations.", - "items": { - "type": "object", - "required": [ "location" ], - "additionalProperties": false, - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the occurrence elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "location": { - "type": "string", - "title": "Location", - "description": "The location or path to where the component was found." - } - } - } - }, - "callstack": { - "type": "object", - "description": "Evidence of the components use through the callstack.", - "additionalProperties": false, - "properties": { - "frames": { - "type": "array", - "title": "Methods", - "items": { - "type": "object", - "required": [ - "module" - ], - "additionalProperties": false, - "properties": { - "package": { - "title": "Package", - "description": "A package organizes modules into namespaces, providing a unique namespace for each type it contains.", - "type": "string" - }, - "module": { - "title": "Module", - "description": "A module or class that encloses functions/methods and other code.", - "type": "string" - }, - "function": { - "title": "Function", - "description": "A block of code designed to perform a particular task.", - "type": "string" - }, - "parameters": { - "title": "Parameters", - "description": "Optional arguments that are passed to the module or function.", - "type": "array", - "items": { - "type": "string" - } - }, - "line": { - "title": "Line", - "description": "The line number the code that is called resides on.", - "type": "integer" - }, - "column": { - "title": "Column", - "description": "The column the code that is called resides.", - "type": "integer" - }, - "fullFilename": { - "title": "Full Filename", - "description": "The full path and filename of the module.", - "type": "string" - } - } - } - } - } - }, - "licenses": { - "$ref": "#/definitions/licenseChoice", - "title": "Component License(s)" - }, - "copyright": { - "type": "array", - "items": {"$ref": "#/definitions/copyright"}, - "title": "Copyright" - } - } - }, - "compositions": { - "type": "object", - "title": "Compositions", - "required": [ - "aggregate" - ], - "additionalProperties": false, - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the composition elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "aggregate": { - "$ref": "#/definitions/aggregateType", - "title": "Aggregate", - "description": "Specifies an aggregate type that describe how complete a relationship is.\n\n* __complete__ = The relationship is complete. No further relationships including constituent components, services, or dependencies are known to exist.\n* __incomplete__ = The relationship is incomplete. Additional relationships exist and may include constituent components, services, or dependencies.\n* __incomplete_first_party_only__ = The relationship is incomplete. Only relationships for first-party components, services, or their dependencies are represented.\n* __incomplete_first_party_proprietary_only__ = The relationship is incomplete. Only relationships for first-party components, services, or their dependencies are represented, limited specifically to those that are proprietary.\n* __incomplete_first_party_opensource_only__ = The relationship is incomplete. Only relationships for first-party components, services, or their dependencies are represented, limited specifically to those that are opensource.\n* __incomplete_third_party_only__ = The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are represented.\n* __incomplete_third_party_proprietary_only__ = The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are represented, limited specifically to those that are proprietary.\n* __incomplete_third_party_opensource_only__ = The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are represented, limited specifically to those that are opensource.\n* __unknown__ = The relationship may be complete or incomplete. This usually signifies a 'best-effort' to obtain constituent components, services, or dependencies but the completeness is inconclusive.\n* __not_specified__ = The relationship completeness is not specified.\n" - }, - "assemblies": { - "type": "array", - "uniqueItems": true, - "items": { - "anyOf": [ - { - "title": "Ref", - "$ref": "#/definitions/refLinkType" - }, - { - "title": "BOM-Link Element", - "$ref": "#/definitions/bomLinkElementType" - } - ] - }, - "title": "BOM references", - "description": "The bom-ref identifiers of the components or services being described. Assemblies refer to nested relationships whereby a constituent part may include other constituent parts. References do not cascade to child parts. References are explicit for the specified constituent part only." - }, - "dependencies": { - "type": "array", - "uniqueItems": true, - "items": { - "type": "string" - }, - "title": "BOM references", - "description": "The bom-ref identifiers of the components or services being described. Dependencies refer to a relationship whereby an independent constituent part requires another independent constituent part. References do not cascade to transitive dependencies. References are explicit for the specified dependency only." - }, - "vulnerabilities": { - "type": "array", - "uniqueItems": true, - "items": { - "type": "string" - }, - "title": "BOM references", - "description": "The bom-ref identifiers of the vulnerabilities being described." - }, - "signature": { - "$ref": "#/definitions/signature", - "title": "Signature", - "description": "Enveloped signature in [JSON Signature Format (JSF)](https://cyberphone.github.io/doc/security/jsf.html)." - } - } - }, - "aggregateType": { - "type": "string", - "default": "not_specified", - "enum": [ - "complete", - "incomplete", - "incomplete_first_party_only", - "incomplete_first_party_proprietary_only", - "incomplete_first_party_opensource_only", - "incomplete_third_party_only", - "incomplete_third_party_proprietary_only", - "incomplete_third_party_opensource_only", - "unknown", - "not_specified" - ] - }, - "property": { - "type": "object", - "title": "Lightweight name-value pair", - "description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is OPTIONAL.", - "properties": { - "name": { - "type": "string", - "title": "Name", - "description": "The name of the property. Duplicate names are allowed, each potentially having a different value." - }, - "value": { - "type": "string", - "title": "Value", - "description": "The value of the property." - } - } - }, - "localeType": { - "type": "string", - "pattern": "^([a-z]{2})(-[A-Z]{2})?$", - "title": "Locale", - "description": "Defines a syntax for representing two character language code (ISO-639) followed by an optional two character country code. The language code MUST be lower case. If the country code is specified, the country code MUST be upper case. The language code and country code MUST be separated by a minus sign. Examples: en, en-US, fr, fr-CA" - }, - "releaseType": { - "type": "string", - "examples": [ - "major", - "minor", - "patch", - "pre-release", - "internal" - ], - "description": "The software versioning type. It is RECOMMENDED that the release type use one of 'major', 'minor', 'patch', 'pre-release', or 'internal'. Representing all possible software release types is not practical, so standardizing on the recommended values, whenever possible, is strongly encouraged.\n\n* __major__ = A major release may contain significant changes or may introduce breaking changes.\n* __minor__ = A minor release, also known as an update, may contain a smaller number of changes than major releases.\n* __patch__ = Patch releases are typically unplanned and may resolve defects or important security issues.\n* __pre-release__ = A pre-release may include alpha, beta, or release candidates and typically have limited support. They provide the ability to preview a release prior to its general availability.\n* __internal__ = Internal releases are not for public consumption and are intended to be used exclusively by the project or manufacturer that produced it." - }, - "note": { - "type": "object", - "title": "Note", - "description": "A note containing the locale and content.", - "required": [ - "text" - ], - "additionalProperties": false, - "properties": { - "locale": { - "$ref": "#/definitions/localeType", - "title": "Locale", - "description": "The ISO-639 (or higher) language code and optional ISO-3166 (or higher) country code. Examples include: \"en\", \"en-US\", \"fr\" and \"fr-CA\"" - }, - "text": { - "title": "Release note content", - "description": "Specifies the full content of the release note.", - "$ref": "#/definitions/attachment" - } - } - }, - "releaseNotes": { - "type": "object", - "title": "Release notes", - "required": [ - "type" - ], - "additionalProperties": false, - "properties": { - "type": { - "$ref": "#/definitions/releaseType", - "title": "Type", - "description": "The software versioning type the release note describes." - }, - "title": { - "type": "string", - "title": "Title", - "description": "The title of the release." - }, - "featuredImage": { - "type": "string", - "format": "iri-reference", - "title": "Featured image", - "description": "The URL to an image that may be prominently displayed with the release note." - }, - "socialImage": { - "type": "string", - "format": "iri-reference", - "title": "Social image", - "description": "The URL to an image that may be used in messaging on social media platforms." - }, - "description": { - "type": "string", - "title": "Description", - "description": "A short description of the release." - }, - "timestamp": { - "type": "string", - "format": "date-time", - "title": "Timestamp", - "description": "The date and time (timestamp) when the release note was created." - }, - "aliases": { - "type": "array", - "items": { - "type": "string" - }, - "title": "Aliases", - "description": "One or more alternate names the release may be referred to. This may include unofficial terms used by development and marketing teams (e.g. code names)." - }, - "tags": { - "type": "array", - "items": { - "type": "string" - }, - "title": "Tags", - "description": "One or more tags that may aid in search or retrieval of the release note." - }, - "resolves": { - "type": "array", - "items": {"$ref": "#/definitions/issue"}, - "title": "Resolves", - "description": "A collection of issues that have been resolved." - }, - "notes": { - "type": "array", - "items": {"$ref": "#/definitions/note"}, - "title": "Notes", - "description": "Zero or more release notes containing the locale and content. Multiple note objects may be specified to support release notes in a wide variety of languages." - }, - "properties": { - "type": "array", - "title": "Properties", - "description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is OPTIONAL.", - "items": {"$ref": "#/definitions/property"} - } - } - }, - "advisory": { - "type": "object", - "title": "Advisory", - "description": "Title and location where advisory information can be obtained. An advisory is a notification of a threat to a component, service, or system.", - "required": ["url"], - "additionalProperties": false, - "properties": { - "title": { - "type": "string", - "title": "Title", - "description": "An optional name of the advisory." - }, - "url": { - "type": "string", - "title": "URL", - "format": "iri-reference", - "description": "Location where the advisory can be obtained." - } - } - }, - "cwe": { - "type": "integer", - "minimum": 1, - "title": "CWE", - "description": "Integer representation of a Common Weaknesses Enumerations (CWE). For example 399 (of https://cwe.mitre.org/data/definitions/399.html)" - }, - "severity": { - "type": "string", - "title": "Severity", - "description": "Textual representation of the severity of the vulnerability adopted by the analysis method. If the analysis method uses values other than what is provided, the user is expected to translate appropriately.", - "enum": [ - "critical", - "high", - "medium", - "low", - "info", - "none", - "unknown" - ] - }, - "scoreMethod": { - "type": "string", - "title": "Method", - "description": "Specifies the severity or risk scoring methodology or standard used.\n\n* CVSSv2 - [Common Vulnerability Scoring System v2](https://www.first.org/cvss/v2/)\n* CVSSv3 - [Common Vulnerability Scoring System v3](https://www.first.org/cvss/v3-0/)\n* CVSSv31 - [Common Vulnerability Scoring System v3.1](https://www.first.org/cvss/v3-1/)\n* CVSSv4 - [Common Vulnerability Scoring System v4](https://www.first.org/cvss/v4-0/)\n* OWASP - [OWASP Risk Rating Methodology](https://owasp.org/www-community/OWASP_Risk_Rating_Methodology)\n* SSVC - [Stakeholder Specific Vulnerability Categorization](https://github.com/CERTCC/SSVC) (all versions)", - "enum": [ - "CVSSv2", - "CVSSv3", - "CVSSv31", - "CVSSv4", - "OWASP", - "SSVC", - "other" - ] - }, - "impactAnalysisState": { - "type": "string", - "title": "Impact Analysis State", - "description": "Declares the current state of an occurrence of a vulnerability, after automated or manual analysis. \n\n* __resolved__ = the vulnerability has been remediated. \n* __resolved\\_with\\_pedigree__ = the vulnerability has been remediated and evidence of the changes are provided in the affected components pedigree containing verifiable commit history and/or diff(s). \n* __exploitable__ = the vulnerability may be directly or indirectly exploitable. \n* __in\\_triage__ = the vulnerability is being investigated. \n* __false\\_positive__ = the vulnerability is not specific to the component or service and was falsely identified or associated. \n* __not\\_affected__ = the component or service is not affected by the vulnerability. Justification should be specified for all not_affected cases.", - "enum": [ - "resolved", - "resolved_with_pedigree", - "exploitable", - "in_triage", - "false_positive", - "not_affected" - ] - }, - "impactAnalysisJustification": { - "type": "string", - "title": "Impact Analysis Justification", - "description": "The rationale of why the impact analysis state was asserted. \n\n* __code\\_not\\_present__ = the code has been removed or tree-shaked. \n* __code\\_not\\_reachable__ = the vulnerable code is not invoked at runtime. \n* __requires\\_configuration__ = exploitability requires a configurable option to be set/unset. \n* __requires\\_dependency__ = exploitability requires a dependency that is not present. \n* __requires\\_environment__ = exploitability requires a certain environment which is not present. \n* __protected\\_by\\_compiler__ = exploitability requires a compiler flag to be set/unset. \n* __protected\\_at\\_runtime__ = exploits are prevented at runtime. \n* __protected\\_at\\_perimeter__ = attacks are blocked at physical, logical, or network perimeter. \n* __protected\\_by\\_mitigating\\_control__ = preventative measures have been implemented that reduce the likelihood and/or impact of the vulnerability.", - "enum": [ - "code_not_present", - "code_not_reachable", - "requires_configuration", - "requires_dependency", - "requires_environment", - "protected_by_compiler", - "protected_at_runtime", - "protected_at_perimeter", - "protected_by_mitigating_control" - ] - }, - "rating": { - "type": "object", - "title": "Rating", - "description": "Defines the severity or risk ratings of a vulnerability.", - "additionalProperties": false, - "properties": { - "source": { - "$ref": "#/definitions/vulnerabilitySource", - "description": "The source that calculated the severity or risk rating of the vulnerability." - }, - "score": { - "type": "number", - "title": "Score", - "description": "The numerical score of the rating." - }, - "severity": { - "$ref": "#/definitions/severity", - "description": "Textual representation of the severity that corresponds to the numerical score of the rating." - }, - "method": { - "$ref": "#/definitions/scoreMethod" - }, - "vector": { - "type": "string", - "title": "Vector", - "description": "Textual representation of the metric values used to score the vulnerability" - }, - "justification": { - "type": "string", - "title": "Justification", - "description": "An optional reason for rating the vulnerability as it was" - } - } - }, - "vulnerabilitySource": { - "type": "object", - "title": "Source", - "description": "The source of vulnerability information. This is often the organization that published the vulnerability.", - "additionalProperties": false, - "properties": { - "url": { - "type": "string", - "title": "URL", - "description": "The url of the vulnerability documentation as provided by the source.", - "examples": [ - "https://nvd.nist.gov/vuln/detail/CVE-2021-39182" - ] - }, - "name": { - "type": "string", - "title": "Name", - "description": "The name of the source.", - "examples": [ - "NVD", - "National Vulnerability Database", - "OSS Index", - "VulnDB", - "GitHub Advisories" - ] - } - } - }, - "vulnerability": { - "type": "object", - "title": "Vulnerability", - "description": "Defines a weakness in a component or service that could be exploited or triggered by a threat source.", - "additionalProperties": false, - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the vulnerability elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "id": { - "type": "string", - "title": "ID", - "description": "The identifier that uniquely identifies the vulnerability.", - "examples": [ - "CVE-2021-39182", - "GHSA-35m5-8cvj-8783", - "SNYK-PYTHON-ENROCRYPT-1912876" - ] - }, - "source": { - "$ref": "#/definitions/vulnerabilitySource", - "description": "The source that published the vulnerability." - }, - "references": { - "type": "array", - "title": "References", - "description": "Zero or more pointers to vulnerabilities that are the equivalent of the vulnerability specified. Often times, the same vulnerability may exist in multiple sources of vulnerability intelligence, but have different identifiers. References provide a way to correlate vulnerabilities across multiple sources of vulnerability intelligence.", - "items": { - "type": "object", - "required": [ - "id", - "source" - ], - "additionalProperties": false, - "properties": { - "id": { - "type": "string", - "title": "ID", - "description": "An identifier that uniquely identifies the vulnerability.", - "examples": [ - "CVE-2021-39182", - "GHSA-35m5-8cvj-8783", - "SNYK-PYTHON-ENROCRYPT-1912876" - ] - }, - "source": { - "$ref": "#/definitions/vulnerabilitySource", - "description": "The source that published the vulnerability." - } - } - } - }, - "ratings": { - "type": "array", - "title": "Ratings", - "description": "List of vulnerability ratings", - "items": { - "$ref": "#/definitions/rating" - } - }, - "cwes": { - "type": "array", - "title": "CWEs", - "description": "List of Common Weaknesses Enumerations (CWEs) codes that describes this vulnerability. For example 399 (of https://cwe.mitre.org/data/definitions/399.html)", - "examples": [399], - "items": { - "$ref": "#/definitions/cwe" - } - }, - "description": { - "type": "string", - "title": "Description", - "description": "A description of the vulnerability as provided by the source." - }, - "detail": { - "type": "string", - "title": "Details", - "description": "If available, an in-depth description of the vulnerability as provided by the source organization. Details often include information useful in understanding root cause." - }, - "recommendation": { - "type": "string", - "title": "Recommendation", - "description": "Recommendations of how the vulnerability can be remediated or mitigated." - }, - "workaround": { - "type": "string", - "title": "Workarounds", - "description": "A bypass, usually temporary, of the vulnerability that reduces its likelihood and/or impact. Workarounds often involve changes to configuration or deployments." - }, - "proofOfConcept": { - "type": "object", - "title": "Proof of Concept", - "description": "Evidence used to reproduce the vulnerability.", - "properties": { - "reproductionSteps": { - "type": "string", - "title": "Steps to Reproduce", - "description": "Precise steps to reproduce the vulnerability." - }, - "environment": { - "type": "string", - "title": "Environment", - "description": "A description of the environment in which reproduction was possible." - }, - "supportingMaterial": { - "type": "array", - "title": "Supporting Material", - "description": "Supporting material that helps in reproducing or understanding how reproduction is possible. This may include screenshots, payloads, and PoC exploit code.", - "items": { "$ref": "#/definitions/attachment" } - } - } - }, - "advisories": { - "type": "array", - "title": "Advisories", - "description": "Published advisories of the vulnerability if provided.", - "items": { - "$ref": "#/definitions/advisory" - } - }, - "created": { - "type": "string", - "format": "date-time", - "title": "Created", - "description": "The date and time (timestamp) when the vulnerability record was created in the vulnerability database." - }, - "published": { - "type": "string", - "format": "date-time", - "title": "Published", - "description": "The date and time (timestamp) when the vulnerability record was first published." - }, - "updated": { - "type": "string", - "format": "date-time", - "title": "Updated", - "description": "The date and time (timestamp) when the vulnerability record was last updated." - }, - "rejected": { - "type": "string", - "format": "date-time", - "title": "Rejected", - "description": "The date and time (timestamp) when the vulnerability record was rejected (if applicable)." - }, - "credits": { - "type": "object", - "title": "Credits", - "description": "Individuals or organizations credited with the discovery of the vulnerability.", - "additionalProperties": false, - "properties": { - "organizations": { - "type": "array", - "title": "Organizations", - "description": "The organizations credited with vulnerability discovery.", - "items": { - "$ref": "#/definitions/organizationalEntity" - } - }, - "individuals": { - "type": "array", - "title": "Individuals", - "description": "The individuals, not associated with organizations, that are credited with vulnerability discovery.", - "items": { - "$ref": "#/definitions/organizationalContact" - } - } - } - }, - "tools": { - "oneOf": [ - { - "type": "object", - "title": "Tools", - "description": "The tool(s) used to identify, confirm, or score the vulnerability.", - "additionalProperties": false, - "properties": { - "components": { - "type": "array", - "items": {"$ref": "#/definitions/component"}, - "uniqueItems": true, - "title": "Components", - "description": "A list of software and hardware components used as tools" - }, - "services": { - "type": "array", - "items": {"$ref": "#/definitions/service"}, - "uniqueItems": true, - "title": "Services", - "description": "A list of services used as tools. This may include microservices, function-as-a-service, and other types of network or intra-process services." - } - } - }, - { - "type": "array", - "title": "Tools (legacy)", - "description": "[Deprecated] The tool(s) used to identify, confirm, or score the vulnerability.", - "items": {"$ref": "#/definitions/tool"} - } - ] - }, - "analysis": { - "type": "object", - "title": "Impact Analysis", - "description": "An assessment of the impact and exploitability of the vulnerability.", - "additionalProperties": false, - "properties": { - "state": { - "$ref": "#/definitions/impactAnalysisState" - }, - "justification": { - "$ref": "#/definitions/impactAnalysisJustification" - }, - "response": { - "type": "array", - "title": "Response", - "description": "A response to the vulnerability by the manufacturer, supplier, or project responsible for the affected component or service. More than one response is allowed. Responses are strongly encouraged for vulnerabilities where the analysis state is exploitable.", - "items": { - "type": "string", - "enum": [ - "can_not_fix", - "will_not_fix", - "update", - "rollback", - "workaround_available" - ] - } - }, - "detail": { - "type": "string", - "title": "Detail", - "description": "Detailed description of the impact including methods used during assessment. If a vulnerability is not exploitable, this field should include specific details on why the component or service is not impacted by this vulnerability." - }, - "firstIssued": { - "type": "string", - "format": "date-time", - "title": "First Issued", - "description": "The date and time (timestamp) when the analysis was first issued." - }, - "lastUpdated": { - "type": "string", - "format": "date-time", - "title": "Last Updated", - "description": "The date and time (timestamp) when the analysis was last updated." - } - } - }, - "affects": { - "type": "array", - "uniqueItems": true, - "items": { - "type": "object", - "required": [ - "ref" - ], - "additionalProperties": false, - "properties": { - "ref": { - "anyOf": [ - { - "title": "Ref", - "$ref": "#/definitions/refLinkType" - }, - { - "title": "BOM-Link Element", - "$ref": "#/definitions/bomLinkElementType" - } - ], - "title": "Reference", - "description": "References a component or service by the objects bom-ref" - }, - "versions": { - "type": "array", - "title": "Versions", - "description": "Zero or more individual versions or range of versions.", - "items": { - "type": "object", - "oneOf": [ - { - "required": ["version"] - }, - { - "required": ["range"] - } - ], - "additionalProperties": false, - "properties": { - "version": { - "description": "A single version of a component or service.", - "$ref": "#/definitions/version" - }, - "range": { - "description": "A version range specified in Package URL Version Range syntax (vers) which is defined at https://github.com/package-url/purl-spec/VERSION-RANGE-SPEC.rst", - "$ref": "#/definitions/range" - }, - "status": { - "description": "The vulnerability status for the version or range of versions.", - "$ref": "#/definitions/affectedStatus", - "default": "affected" - } - } - } - } - } - }, - "title": "Affects", - "description": "The components or services that are affected by the vulnerability." - }, - "properties": { - "type": "array", - "title": "Properties", - "description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is OPTIONAL.", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "affectedStatus": { - "description": "The vulnerability status of a given version or range of versions of a product. The statuses 'affected' and 'unaffected' indicate that the version is affected or unaffected by the vulnerability. The status 'unknown' indicates that it is unknown or unspecified whether the given version is affected. There can be many reasons for an 'unknown' status, including that an investigation has not been undertaken or that a vendor has not disclosed the status.", - "type": "string", - "enum": [ - "affected", - "unaffected", - "unknown" - ] - }, - "version": { - "description": "A single version of a component or service.", - "type": "string", - "minLength": 1, - "maxLength": 1024 - }, - "range": { - "description": "A version range specified in Package URL Version Range syntax (vers) which is defined at https://github.com/package-url/purl-spec/VERSION-RANGE-SPEC.rst", - "type": "string", - "minLength": 1, - "maxLength": 1024 - }, - "annotations": { - "type": "object", - "title": "Annotations", - "description": "A comment, note, explanation, or similar textual content which provides additional context to the object(s) being annotated.", - "required": [ - "subjects", - "annotator", - "timestamp", - "text" - ], - "additionalProperties": false, - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the annotation elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "subjects": { - "type": "array", - "uniqueItems": true, - "items": { - "anyOf": [ - { - "title": "Ref", - "$ref": "#/definitions/refLinkType" - }, - { - "title": "BOM-Link Element", - "$ref": "#/definitions/bomLinkElementType" - } - ] - }, - "title": "BOM References", - "description": "The object in the BOM identified by its bom-ref. This is often a component or service, but may be any object type supporting bom-refs." - }, - "annotator": { - "type": "object", - "title": "Annotator", - "description": "The organization, person, component, or service which created the textual content of the annotation.", - "oneOf": [ - { - "required": [ - "organization" - ] - }, - { - "required": [ - "individual" - ] - }, - { - "required": [ - "component" - ] - }, - { - "required": [ - "service" - ] - } - ], - "additionalProperties": false, - "properties": { - "organization": { - "description": "The organization that created the annotation", - "$ref": "#/definitions/organizationalEntity" - }, - "individual": { - "description": "The person that created the annotation", - "$ref": "#/definitions/organizationalContact" - }, - "component": { - "description": "The tool or component that created the annotation", - "$ref": "#/definitions/component" - }, - "service": { - "description": "The service that created the annotation", - "$ref": "#/definitions/service" - } - } - }, - "timestamp": { - "type": "string", - "format": "date-time", - "title": "Timestamp", - "description": "The date and time (timestamp) when the annotation was created." - }, - "text": { - "type": "string", - "title": "Text", - "description": "The textual content of the annotation." - }, - "signature": { - "$ref": "#/definitions/signature", - "title": "Signature", - "description": "Enveloped signature in [JSON Signature Format (JSF)](https://cyberphone.github.io/doc/security/jsf.html)." - } - } - }, - "modelCard": { - "$comment": "Model card support in CycloneDX is derived from TensorFlow Model Card Toolkit released under the Apache 2.0 license and available from https://github.com/tensorflow/model-card-toolkit/blob/main/model_card_toolkit/schema/v0.0.2/model_card.schema.json. In addition, CycloneDX model card support includes portions of VerifyML, also released under the Apache 2.0 license and available from https://github.com/cylynx/verifyml/blob/main/verifyml/model_card_toolkit/schema/v0.0.4/model_card.schema.json.", - "type": "object", - "title": "Model Card", - "description": "A model card describes the intended uses of a machine learning model and potential limitations, including biases and ethical considerations. Model cards typically contain the training parameters, which datasets were used to train the model, performance metrics, and other relevant data useful for ML transparency. This object SHOULD be specified for any component of type `machine-learning-model` and MUST NOT be specified for other component types.", - "additionalProperties": false, - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the model card elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "modelParameters": { - "type": "object", - "title": "Model Parameters", - "description": "Hyper-parameters for construction of the model.", - "additionalProperties": false, - "properties": { - "approach": { - "type": "object", - "title": "Approach", - "description": "The overall approach to learning used by the model for problem solving.", - "additionalProperties": false, - "properties": { - "type": { - "type": "string", - "title": "Learning Type", - "description": "Learning types describing the learning problem or hybrid learning problem.", - "enum": [ - "supervised", - "unsupervised", - "reinforcement-learning", - "semi-supervised", - "self-supervised" - ] - } - } - }, - "task": { - "type": "string", - "title": "Task", - "description": "Directly influences the input and/or output. Examples include classification, regression, clustering, etc." - }, - "architectureFamily": { - "type": "string", - "title": "Architecture Family", - "description": "The model architecture family such as transformer network, convolutional neural network, residual neural network, LSTM neural network, etc." - }, - "modelArchitecture": { - "type": "string", - "title": "Model Architecture", - "description": "The specific architecture of the model such as GPT-1, ResNet-50, YOLOv3, etc." - }, - "datasets": { - "type": "array", - "title": "Datasets", - "description": "The datasets used to train and evaluate the model.", - "items" : { - "oneOf" : [ - { - "title": "Inline Component Data", - "$ref": "#/definitions/componentData" - }, - { - "type": "object", - "title": "Data Component Reference", - "additionalProperties": false, - "properties": { - "ref": { - "anyOf": [ - { - "title": "Ref", - "$ref": "#/definitions/refLinkType" - }, - { - "title": "BOM-Link Element", - "$ref": "#/definitions/bomLinkElementType" - } - ], - "title": "Reference", - "description": "References a data component by the components bom-ref attribute" - } - } - } - ] - } - }, - "inputs": { - "type": "array", - "title": "Inputs", - "description": "The input format(s) of the model", - "items": { "$ref": "#/definitions/inputOutputMLParameters" } - }, - "outputs": { - "type": "array", - "title": "Outputs", - "description": "The output format(s) from the model", - "items": { "$ref": "#/definitions/inputOutputMLParameters" } - } - } - }, - "quantitativeAnalysis": { - "type": "object", - "title": "Quantitative Analysis", - "description": "A quantitative analysis of the model", - "additionalProperties": false, - "properties": { - "performanceMetrics": { - "type": "array", - "title": "Performance Metrics", - "description": "The model performance metrics being reported. Examples may include accuracy, F1 score, precision, top-3 error rates, MSC, etc.", - "items": { "$ref": "#/definitions/performanceMetric" } - }, - "graphics": { "$ref": "#/definitions/graphicsCollection" } - } - }, - "considerations": { - "type": "object", - "title": "Considerations", - "description": "What considerations should be taken into account regarding the model's construction, training, and application?", - "additionalProperties": false, - "properties": { - "users": { - "type": "array", - "title": "Users", - "description": "Who are the intended users of the model?", - "items": { - "type": "string" - } - }, - "useCases": { - "type": "array", - "title": "Use Cases", - "description": "What are the intended use cases of the model?", - "items": { - "type": "string" - } - }, - "technicalLimitations": { - "type": "array", - "title": "Technical Limitations", - "description": "What are the known technical limitations of the model? E.g. What kind(s) of data should the model be expected not to perform well on? What are the factors that might degrade model performance?", - "items": { - "type": "string" - } - }, - "performanceTradeoffs": { - "type": "array", - "title": "Performance Tradeoffs", - "description": "What are the known tradeoffs in accuracy/performance of the model?", - "items": { - "type": "string" - } - }, - "ethicalConsiderations": { - "type": "array", - "title": "Ethical Considerations", - "description": "What are the ethical (or environmental) risks involved in the application of this model?", - "items": { "$ref": "#/definitions/risk" } - }, - "fairnessAssessments": { - "type": "array", - "title": "Fairness Assessments", - "description": "How does the model affect groups at risk of being systematically disadvantaged? What are the harms and benefits to the various affected groups?", - "items": { - "$ref": "#/definitions/fairnessAssessment" - } - } - } - }, - "properties": { - "type": "array", - "title": "Properties", - "description": "Provides the ability to document properties in a name-value store. This provides flexibility to include data not officially supported in the standard without having to use additional namespaces or create extensions. Unlike key-value stores, properties support duplicate names, each potentially having different values. Property names of interest to the general public are encouraged to be registered in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy). Formal registration is OPTIONAL.", - "items": {"$ref": "#/definitions/property"} - } - } - }, - "inputOutputMLParameters": { - "type": "object", - "title": "Input and Output Parameters", - "additionalProperties": false, - "properties": { - "format": { - "description": "The data format for input/output to the model. Example formats include string, image, time-series", - "type": "string" - } - } - }, - "componentData": { - "type": "object", - "additionalProperties": false, - "required": [ - "type" - ], - "properties": { - "bom-ref": { - "$ref": "#/definitions/refType", - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the dataset elsewhere in the BOM. Every bom-ref MUST be unique within the BOM." - }, - "type": { - "type": "string", - "title": "Type of Data", - "description": "The general theme or subject matter of the data being specified.\n\n* __source-code__ = Any type of code, code snippet, or data-as-code.\n* __configuration__ = Parameters or settings that may be used by other components.\n* __dataset__ = A collection of data.\n* __definition__ = Data that can be used to create new instances of what the definition defines.\n* __other__ = Any other type of data that does not fit into existing definitions.", - "enum": [ - "source-code", - "configuration", - "dataset", - "definition", - "other" - ] - }, - "name": { - "description": "The name of the dataset.", - "type": "string" - }, - "contents": { - "type": "object", - "title": "Data Contents", - "description": "The contents or references to the contents of the data being described.", - "additionalProperties": false, - "properties": { - "attachment": { - "title": "Data Attachment", - "description": "An optional way to include textual or encoded data.", - "$ref": "#/definitions/attachment" - }, - "url": { - "type": "string", - "title": "Data URL", - "description": "The URL to where the data can be retrieved.", - "format": "iri-reference" - }, - "properties": { - "type": "array", - "title": "Configuration Properties", - "description": "Provides the ability to document name-value parameters used for configuration.", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "classification": { - "$ref": "#/definitions/dataClassification" - }, - "sensitiveData": { - "type": "array", - "description": "A description of any sensitive data in a dataset.", - "items": { - "type": "string" - } - }, - "graphics": { "$ref": "#/definitions/graphicsCollection" }, - "description": { - "description": "A description of the dataset. Can describe size of dataset, whether it's used for source code, training, testing, or validation, etc.", - "type": "string" - }, - "governance": { - "type": "object", - "title": "Data Governance", - "$ref": "#/definitions/dataGovernance" - } - } - }, - "dataGovernance": { - "type": "object", - "title": "Data Governance", - "additionalProperties": false, - "properties": { - "custodians": { - "type": "array", - "title": "Data Custodians", - "description": "Data custodians are responsible for the safe custody, transport, and storage of data.", - "items": { "$ref": "#/definitions/dataGovernanceResponsibleParty" } - }, - "stewards": { - "type": "array", - "title": "Data Stewards", - "description": "Data stewards are responsible for data content, context, and associated business rules.", - "items": { "$ref": "#/definitions/dataGovernanceResponsibleParty" } - }, - "owners": { - "type": "array", - "title": "Data Owners", - "description": "Data owners are concerned with risk and appropriate access to data.", - "items": { "$ref": "#/definitions/dataGovernanceResponsibleParty" } - } - } - }, - "dataGovernanceResponsibleParty": { - "type": "object", - "additionalProperties": false, - "properties": { - "organization": { - "title": "Organization", - "$ref": "#/definitions/organizationalEntity" - }, - "contact": { - "title": "Individual", - "$ref": "#/definitions/organizationalContact" - } - }, - "oneOf":[ - { - "required": ["organization"] - }, - { - "required": ["contact"] - } - ] - }, - "graphicsCollection": { - "type": "object", - "title": "Graphics Collection", - "description": "A collection of graphics that represent various measurements.", - "additionalProperties": false, - "properties": { - "description": { - "description": "A description of this collection of graphics.", - "type": "string" - }, - "collection": { - "description": "A collection of graphics.", - "type": "array", - "items": { "$ref": "#/definitions/graphic" } - } - } - }, - "graphic": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "description": "The name of the graphic.", - "type": "string" - }, - "image": { - "title": "Graphic Image", - "description": "The graphic (vector or raster). Base64 encoding MUST be specified for binary images.", - "$ref": "#/definitions/attachment" - } - } - }, - "performanceMetric": { - "type": "object", - "additionalProperties": false, - "properties": { - "type": { - "description": "The type of performance metric.", - "type": "string" - }, - "value": { - "description": "The value of the performance metric.", - "type": "string" - }, - "slice": { - "description": "The name of the slice this metric was computed on. By default, assume this metric is not sliced.", - "type": "string" - }, - "confidenceInterval": { - "description": "The confidence interval of the metric.", - "type": "object", - "additionalProperties": false, - "properties": { - "lowerBound": { - "description": "The lower bound of the confidence interval.", - "type": "string" - }, - "upperBound": { - "description": "The upper bound of the confidence interval.", - "type": "string" - } - } - } - } - }, - "risk": { - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "description": "The name of the risk.", - "type": "string" - }, - "mitigationStrategy": { - "description": "Strategy used to address this risk.", - "type": "string" - } - } - }, - "fairnessAssessment": { - "type": "object", - "title": "Fairness Assessment", - "description": "Information about the benefits and harms of the model to an identified at risk group.", - "additionalProperties": false, - "properties": { - "groupAtRisk": { - "type": "string", - "description": "The groups or individuals at risk of being systematically disadvantaged by the model." - }, - "benefits": { - "type": "string", - "description": "Expected benefits to the identified groups." - }, - "harms": { - "type": "string", - "description": "Expected harms to the identified groups." - }, - "mitigationStrategy": { - "type": "string", - "description": "With respect to the benefits and harms outlined, please describe any mitigation strategy implemented." - } - } - }, - "dataClassification": { - "type": "string", - "title": "Data Classification", - "description": "Data classification tags data according to its type, sensitivity, and value if altered, stolen, or destroyed." - }, - "formula": { - "title": "Formula", - "description": "Describes workflows and resources that captures rules and other aspects of how the associated BOM component or service was formed.", - "type": "object", - "additionalProperties": false, - "properties": { - "bom-ref": { - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the formula elsewhere in the BOM. Every bom-ref MUST be unique within the BOM.", - "$ref": "#/definitions/refType" - }, - "components": { - "title": "Components", - "description": "Transient components that are used in tasks that constitute one or more of this formula's workflows", - "type": "array", - "items": { - "$ref": "#/definitions/component" - }, - "uniqueItems": true - }, - "services": { - "title": "Services", - "description": "Transient services that are used in tasks that constitute one or more of this formula's workflows", - "type": "array", - "items": { - "$ref": "#/definitions/service" - }, - "uniqueItems": true - }, - "workflows": { - "title": "Workflows", - "description": "List of workflows that can be declared to accomplish specific orchestrated goals and independently triggered.", - "$comment": "Different workflows can be designed to work together to perform end-to-end CI/CD builds and deployments.", - "type": "array", - "items": { - "$ref": "#/definitions/workflow" - }, - "uniqueItems": true - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "workflow": { - "title": "Workflow", - "description": "A specialized orchestration task.", - "$comment": "Workflow are as task themselves and can trigger other workflow tasks. These relationships can be modeled in the taskDependencies graph.", - "type": "object", - "required": [ - "bom-ref", - "uid", - "taskTypes" - ], - "additionalProperties": false, - "properties": { - "bom-ref": { - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the workflow elsewhere in the BOM. Every bom-ref MUST be unique within the BOM.", - "$ref": "#/definitions/refType" - }, - "uid": { - "title": "Unique Identifier (UID)", - "description": "The unique identifier for the resource instance within its deployment context.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the resource instance.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the resource instance.", - "type": "string" - }, - "resourceReferences": { - "title": "Resource references", - "description": "References to component or service resources that are used to realize the resource instance.", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/resourceReferenceChoice" - } - }, - "tasks": { - "title": "Tasks", - "description": "The tasks that comprise the workflow.", - "$comment": "Note that tasks can appear more than once as different instances (by name or UID).", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/task" - } - }, - "taskDependencies": { - "title": "Task dependency graph", - "description": "The graph of dependencies between tasks within the workflow.", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/dependency" - } - }, - "taskTypes": { - "title": "Task types", - "description": "Indicates the types of activities performed by the set of workflow tasks.", - "$comment": "Currently, these types reflect common CI/CD actions.", - "type": "array", - "items": { - "$ref": "#/definitions/taskType" - } - }, - "trigger": { - "title": "Trigger", - "description": "The trigger that initiated the task.", - "$ref": "#/definitions/trigger" - }, - "steps": { - "title": "Steps", - "description": "The sequence of steps for the task.", - "type": "array", - "items": { - "$ref": "#/definitions/step" - }, - "uniqueItems": true - }, - "inputs": { - "title": "Inputs", - "description": "Represents resources and data brought into a task at runtime by executor or task commands", - "examples": ["a `configuration` file which was declared as a local `component` or `externalReference`"], - "type": "array", - "items": { - "$ref": "#/definitions/inputType" - }, - "uniqueItems": true - }, - "outputs": { - "title": "Outputs", - "description": "Represents resources and data output from a task at runtime by executor or task commands", - "examples": ["a log file or metrics data produced by the task"], - "type": "array", - "items": { - "$ref": "#/definitions/outputType" - }, - "uniqueItems": true - }, - "timeStart": { - "title": "Time start", - "description": "The date and time (timestamp) when the task started.", - "type": "string", - "format": "date-time" - }, - "timeEnd": { - "title": "Time end", - "description": "The date and time (timestamp) when the task ended.", - "type": "string", - "format": "date-time" - }, - "workspaces": { - "title": "Workspaces", - "description": "A set of named filesystem or data resource shareable by workflow tasks.", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/workspace" - } - }, - "runtimeTopology": { - "title": "Runtime topology", - "description": "A graph of the component runtime topology for workflow's instance.", - "$comment": "A description of the runtime component and service topology. This can describe a partial or complete topology used to host and execute the task (e.g., hardware, operating systems, configurations, etc.),", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/dependency" - } - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "task": { - "title": "Task", - "description": "Describes the inputs, sequence of steps and resources used to accomplish a task and its output.", - "$comment": "Tasks are building blocks for constructing assemble CI/CD workflows or pipelines.", - "type": "object", - "required": [ - "bom-ref", - "uid", - "taskTypes" - ], - "additionalProperties": false, - "properties": { - "bom-ref": { - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the task elsewhere in the BOM. Every bom-ref MUST be unique within the BOM.", - "$ref": "#/definitions/refType" - }, - "uid": { - "title": "Unique Identifier (UID)", - "description": "The unique identifier for the resource instance within its deployment context.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the resource instance.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the resource instance.", - "type": "string" - }, - "resourceReferences": { - "title": "Resource references", - "description": "References to component or service resources that are used to realize the resource instance.", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/resourceReferenceChoice" - } - }, - "taskTypes": { - "title": "Task types", - "description": "Indicates the types of activities performed by the set of workflow tasks.", - "$comment": "Currently, these types reflect common CI/CD actions.", - "type": "array", - "items": { - "$ref": "#/definitions/taskType" - } - }, - "trigger": { - "title": "Trigger", - "description": "The trigger that initiated the task.", - "$ref": "#/definitions/trigger" - }, - "steps": { - "title": "Steps", - "description": "The sequence of steps for the task.", - "type": "array", - "items": { - "$ref": "#/definitions/step" - }, - "uniqueItems": true - }, - "inputs": { - "title": "Inputs", - "description": "Represents resources and data brought into a task at runtime by executor or task commands", - "examples": ["a `configuration` file which was declared as a local `component` or `externalReference`"], - "type": "array", - "items": { - "$ref": "#/definitions/inputType" - }, - "uniqueItems": true - }, - "outputs": { - "title": "Outputs", - "description": "Represents resources and data output from a task at runtime by executor or task commands", - "examples": ["a log file or metrics data produced by the task"], - "type": "array", - "items": { - "$ref": "#/definitions/outputType" - }, - "uniqueItems": true - }, - "timeStart": { - "title": "Time start", - "description": "The date and time (timestamp) when the task started.", - "type": "string", - "format": "date-time" - }, - "timeEnd": { - "title": "Time end", - "description": "The date and time (timestamp) when the task ended.", - "type": "string", - "format": "date-time" - }, - "workspaces": { - "title": "Workspaces", - "description": "A set of named filesystem or data resource shareable by workflow tasks.", - "type": "array", - "items": { - "$ref": "#/definitions/workspace" - }, - "uniqueItems": true - }, - "runtimeTopology": { - "title": "Runtime topology", - "description": "A graph of the component runtime topology for task's instance.", - "$comment": "A description of the runtime component and service topology. This can describe a partial or complete topology used to host and execute the task (e.g., hardware, operating systems, configurations, etc.),", - "type": "array", - "items": { - "$ref": "#/definitions/dependency" - }, - "uniqueItems": true - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "step": { - "type": "object", - "description": "Executes specific commands or tools in order to accomplish its owning task as part of a sequence.", - "additionalProperties": false, - "properties": { - "name": { - "title": "Name", - "description": "A name for the step.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the step.", - "type": "string" - }, - "commands": { - "title": "Commands", - "description": "Ordered list of commands or directives for the step", - "type": "array", - "items": { - "$ref": "#/definitions/command" - } - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "command": { - "type": "object", - "additionalProperties": false, - "properties": { - "executed": { - "title": "Executed", - "description": "A text representation of the executed command.", - "type": "string" - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "workspace": { - "title": "Workspace", - "description": "A named filesystem or data resource shareable by workflow tasks.", - "type": "object", - "required": [ - "bom-ref", - "uid" - ], - "additionalProperties": false, - "properties": { - "bom-ref": { - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the workspace elsewhere in the BOM. Every bom-ref MUST be unique within the BOM.", - "$ref": "#/definitions/refType" - }, - "uid": { - "title": "Unique Identifier (UID)", - "description": "The unique identifier for the resource instance within its deployment context.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the resource instance.", - "type": "string" - }, - "aliases": { - "title": "Aliases", - "description": "The names for the workspace as referenced by other workflow tasks. Effectively, a name mapping so other tasks can use their own local name in their steps.", - "type": "array", - "items": {"type": "string"} - }, - "description": { - "title": "Description", - "description": "A description of the resource instance.", - "type": "string" - }, - "resourceReferences": { - "title": "Resource references", - "description": "References to component or service resources that are used to realize the resource instance.", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/resourceReferenceChoice" - } - }, - "accessMode": { - "title": "Access mode", - "description": "Describes the read-write access control for the workspace relative to the owning resource instance.", - "type": "string", - "enum": [ - "read-only", - "read-write", - "read-write-once", - "write-once", - "write-only" - ] - }, - "mountPath": { - "title": "Mount path", - "description": "A path to a location on disk where the workspace will be available to the associated task's steps.", - "type": "string" - }, - "managedDataType": { - "title": "Managed data type", - "description": "The name of a domain-specific data type the workspace represents.", - "$comment": "This property is for CI/CD frameworks that are able to provide access to structured, managed data at a more granular level than a filesystem.", - "examples": ["ConfigMap","Secret"], - "type": "string" - }, - "volumeRequest": { - "title": "Volume request", - "description": "Identifies the reference to the request for a specific volume type and parameters.", - "examples": ["a kubernetes Persistent Volume Claim (PVC) name"], - "type": "string" - }, - "volume": { - "title": "Volume", - "description": "Information about the actual volume instance allocated to the workspace.", - "$comment": "The actual volume allocated may be different than the request.", - "examples": ["see https://kubernetes.io/docs/concepts/storage/persistent-volumes/"], - "$ref": "#/definitions/volume" - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "volume": { - "title": "Volume", - "description": "An identifiable, logical unit of data storage tied to a physical device.", - "type": "object", - "additionalProperties": false, - "properties": { - "uid": { - "title": "Unique Identifier (UID)", - "description": "The unique identifier for the volume instance within its deployment context.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the volume instance", - "type": "string" - }, - "mode": { - "title": "Mode", - "description": "The mode for the volume instance.", - "type": "string", - "enum": [ - "filesystem", "block" - ], - "default": "filesystem" - }, - "path": { - "title": "Path", - "description": "The underlying path created from the actual volume.", - "type": "string" - }, - "sizeAllocated": { - "title": "Size allocated", - "description": "The allocated size of the volume accessible to the associated workspace. This should include the scalar size as well as IEC standard unit in either decimal or binary form.", - "examples": ["10GB", "2Ti", "1Pi"], - "type": "string" - }, - "persistent": { - "title": "Persistent", - "description": "Indicates if the volume persists beyond the life of the resource it is associated with.", - "type": "boolean" - }, - "remote": { - "title": "Remote", - "description": "Indicates if the volume is remotely (i.e., network) attached.", - "type": "boolean" - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "trigger": { - "title": "Trigger", - "description": "Represents a resource that can conditionally activate (or fire) tasks based upon associated events and their data.", - "type": "object", - "additionalProperties": false, - "required": [ - "type", - "bom-ref", - "uid" - ], - "properties": { - "bom-ref": { - "title": "BOM Reference", - "description": "An optional identifier which can be used to reference the trigger elsewhere in the BOM. Every bom-ref MUST be unique within the BOM.", - "$ref": "#/definitions/refType" - }, - "uid": { - "title": "Unique Identifier (UID)", - "description": "The unique identifier for the resource instance within its deployment context.", - "type": "string" - }, - "name": { - "title": "Name", - "description": "The name of the resource instance.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the resource instance.", - "type": "string" - }, - "resourceReferences": { - "title": "Resource references", - "description": "References to component or service resources that are used to realize the resource instance.", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/resourceReferenceChoice" - } - }, - "type": { - "title": "Type", - "description": "The source type of event which caused the trigger to fire.", - "type": "string", - "enum": [ - "manual", - "api", - "webhook", - "scheduled" - ] - }, - "event": { - "title": "Event", - "description": "The event data that caused the associated trigger to activate.", - "$ref": "#/definitions/event" - }, - "conditions": { - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/condition" - } - }, - "timeActivated": { - "title": "Time activated", - "description": "The date and time (timestamp) when the trigger was activated.", - "type": "string", - "format": "date-time" - }, - "inputs": { - "title": "Inputs", - "description": "Represents resources and data brought into a task at runtime by executor or task commands", - "examples": ["a `configuration` file which was declared as a local `component` or `externalReference`"], - "type": "array", - "items": { - "$ref": "#/definitions/inputType" - }, - "uniqueItems": true - }, - "outputs": { - "title": "Outputs", - "description": "Represents resources and data output from a task at runtime by executor or task commands", - "examples": ["a log file or metrics data produced by the task"], - "type": "array", - "items": { - "$ref": "#/definitions/outputType" - }, - "uniqueItems": true - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "event": { - "title": "Event", - "description": "Represents something that happened that may trigger a response.", - "type": "object", - "additionalProperties": false, - "properties": { - "uid": { - "title": "Unique Identifier (UID)", - "description": "The unique identifier of the event.", - "type": "string" - }, - "description": { - "title": "Description", - "description": "A description of the event.", - "type": "string" - }, - "timeReceived": { - "title": "Time Received", - "description": "The date and time (timestamp) when the event was received.", - "type": "string", - "format": "date-time" - }, - "data": { - "title": "Data", - "description": "Encoding of the raw event data.", - "$ref": "#/definitions/attachment" - }, - "source": { - "title": "Source", - "description": "References the component or service that was the source of the event", - "$ref": "#/definitions/resourceReferenceChoice" - }, - "target": { - "title": "Target", - "description": "References the component or service that was the target of the event", - "$ref": "#/definitions/resourceReferenceChoice" - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "inputType": { - "title": "Input type", - "description": "Type that represents various input data types and formats.", - "type": "object", - "oneOf": [ - { - "required": [ - "resource" - ] - }, - { - "required": [ - "parameters" - ] - }, - { - "required": [ - "environmentVars" - ] - }, - { - "required": [ - "data" - ] - } - ], - "additionalProperties": false, - "properties": { - "source": { - "title": "Source", - "description": "A references to the component or service that provided the input to the task (e.g., reference to a service with data flow value of `inbound`)", - "examples": [ - "source code repository", - "database" - ], - "$ref": "#/definitions/resourceReferenceChoice" - }, - "target": { - "title": "Target", - "description": "A reference to the component or service that received or stored the input if not the task itself (e.g., a local, named storage workspace)", - "examples": [ - "workspace", - "directory" - ], - "$ref": "#/definitions/resourceReferenceChoice" - }, - "resource": { - "title": "Resource", - "description": "A reference to an independent resource provided as an input to a task by the workflow runtime.", - "examples": [ - "reference to a configuration file in a repository (i.e., a bom-ref)", - "reference to a scanning service used in a task (i.e., a bom-ref)" - ], - "$ref": "#/definitions/resourceReferenceChoice" - }, - "parameters": { - "title": "Parameters", - "description": "Inputs that have the form of parameters with names and values.", - "type": "array", - "uniqueItems": true, - "items": { - "$ref": "#/definitions/parameter" - } - }, - "environmentVars": { - "title": "Environment variables", - "description": "Inputs that have the form of parameters with names and values.", - "type": "array", - "uniqueItems": true, - "items": { - "oneOf": [ - { - "$ref": "#/definitions/property" - }, - { - "type": "string" - } - ] - } - }, - "data": { - "title": "Data", - "description": "Inputs that have the form of data.", - "$ref": "#/definitions/attachment" - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "outputType": { - "type": "object", - "oneOf": [ - { - "required": [ - "resource" - ] - }, - { - "required": [ - "environmentVars" - ] - }, - { - "required": [ - "data" - ] - } - ], - "additionalProperties": false, - "properties": { - "type": { - "title": "Type", - "description": "Describes the type of data output.", - "type": "string", - "enum": [ - "artifact", - "attestation", - "log", - "evidence", - "metrics", - "other" - ] - }, - "source": { - "title": "Source", - "description": "Component or service that generated or provided the output from the task (e.g., a build tool)", - "$ref": "#/definitions/resourceReferenceChoice" - }, - "target": { - "title": "Target", - "description": "Component or service that received the output from the task (e.g., reference to an artifactory service with data flow value of `outbound`)", - "examples": ["a log file described as an `externalReference` within its target domain."], - "$ref": "#/definitions/resourceReferenceChoice" - }, - "resource": { - "title": "Resource", - "description": "A reference to an independent resource generated as output by the task.", - "examples": [ - "configuration file", - "source code", - "scanning service" - ], - "$ref": "#/definitions/resourceReferenceChoice" - }, - "data": { - "title": "Data", - "description": "Outputs that have the form of data.", - "$ref": "#/definitions/attachment" - }, - "environmentVars": { - "title": "Environment variables", - "description": "Outputs that have the form of environment variables.", - "type": "array", - "items": { - "oneOf": [ - { - "$ref": "#/definitions/property" - }, - { - "type": "string" - } - ] - }, - "uniqueItems": true - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "resourceReferenceChoice": { - "title": "Resource reference choice", - "description": "A reference to a locally defined resource (e.g., a bom-ref) or an externally accessible resource.", - "$comment": "Enables reference to a resource that participates in a workflow; using either internal (bom-ref) or external (externalReference) types.", - "type": "object", - "additionalProperties": false, - "properties": { - "ref": { - "title": "BOM Reference", - "description": "References an object by its bom-ref attribute", - "anyOf": [ - { - "title": "Ref", - "$ref": "#/definitions/refLinkType" - }, - { - "title": "BOM-Link Element", - "$ref": "#/definitions/bomLinkElementType" - } - ] - }, - "externalReference": { - "title": "External reference", - "description": "Reference to an externally accessible resource.", - "$ref": "#/definitions/externalReference" - } - }, - "oneOf": [ - { - "required": [ - "ref" - ] - }, - { - "required": [ - "externalReference" - ] - } - ] - }, - "condition": { - "title": "Condition", - "description": "A condition that was used to determine a trigger should be activated.", - "type": "object", - "additionalProperties": false, - "properties": { - "description": { - "title": "Description", - "description": "Describes the set of conditions which cause the trigger to activate.", - "type": "string" - }, - "expression": { - "title": "Expression", - "description": "The logical expression that was evaluated that determined the trigger should be fired.", - "type": "string" - }, - "properties": { - "type": "array", - "title": "Properties", - "items": { - "$ref": "#/definitions/property" - } - } - } - }, - "taskType": { - "type": "string", - "enum": [ - "copy", - "clone", - "lint", - "scan", - "merge", - "build", - "test", - "deliver", - "deploy", - "release", - "clean", - "other" - ] - }, - "parameter": { - "title": "Parameter", - "description": "A representation of a functional parameter.", - "type": "object", - "additionalProperties": false, - "properties": { - "name": { - "title": "Name", - "description": "The name of the parameter.", - "type": "string" - }, - "value": { - "title": "Value", - "description": "The value of the parameter.", - "type": "string" - }, - "dataType": { - "title": "Data type", - "description": "The data type of the parameter.", - "type": "string" - } - } - }, - "signature": { - "$ref": "jsf-0.82.schema.json#/definitions/signature", - "title": "Signature", - "description": "Enveloped signature in [JSON Signature Format (JSF)](https://cyberphone.github.io/doc/security/jsf.html)." - } - } -} diff --git a/schema/cyclonedx/cyclonedx.xsd b/schema/cyclonedx/cyclonedx.xsd deleted file mode 100644 index 23dc620a095..00000000000 --- a/schema/cyclonedx/cyclonedx.xsd +++ /dev/null @@ -1,8290 +0,0 @@ - - - - - - - - - CycloneDX Bill of Materials Standard - https://cyclonedx.org/ - Apache License, Version 2.0 - - - - - - Identifier for referable and therefore interlink-able elements. - - - - - - - - - - Descriptor for an element identified by the attribute "bom-ref" in the same BOM document. - In contrast to `bomLinkElementType`. - - - - - - - - - - - - - - - - - =2.0.0|<5.0.0" - - "vers:pypi/0.0.0|0.0.1|0.0.2|0.0.3|1.0|2.0pre1" - - "vers:tomee/>=1.0.0-beta1|<=1.7.5|>=7.0.0-M1|<=7.0.7|>=7.1.0|<=7.1.2|>=8.0.0-M1|<=8.0.1" - - "vers:gem/>=2.2.0|!= 2.2.1|<2.3.0" - ]]> - - - - - - - - - - - - Descriptor for another BOM document. - See https://cyclonedx.org/capabilities/bomlink/ - - - - - - - - - - - Descriptor for an element in another BOM document. - See https://cyclonedx.org/capabilities/bomlink/ - - - - - - - - - - - - - - - - The date and time (timestamp) when the BOM was created. - - - - - - Lifecycles communicate the stage(s) in which data in the BOM was captured. Different types of data may be available at various phases of a lifecycle, such as the Software Development Lifecycle (SDLC), IT Asset Management (ITAM), and Software Asset Management (SAM). Thus, a BOM may include data specific to or only obtainable in a given lifecycle. - - - - - - - - - - - - A pre-defined phase in the product lifecycle. - - - - - - - - - The name of the lifecycle phase - - - - - - - The description of the lifecycle phase - - - - - - - - - - - - - The tool(s) used in the creation of the BOM. - - - - - - - DEPRECATED. Use tools\components or tools\services instead. - - - - - - - A list of software and hardware components used as tools. - - - - - A list of services used as tools. - - - - - - - - - - The person(s) who created the BOM. - Authors are common in BOMs created through manual processes. BOMs created through automated means may have './manufacturer' instead. - - - - - - - - - - - The component that the BOM describes. - - - - - - The organization that created the BOM. - Manufacturer is common in BOMs created through automated processes. BOMs created through manual means may have './authors' instead. - - - - - - - DEPRECATED - DO NOT USE. This will be removed in a future version. Use the `./component/manufacturer` instead. - The organization that manufactured the component that the BOM describes. - - - - - - The organization that supplied the component that the BOM describes. The - supplier may often be the manufacturer, but may also be a distributor or repackager. - - - - - - The license information for the BOM document. - This may be different from the license(s) of the component(s) that the BOM describes. - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - BOM produced early in the development lifecycle containing inventory of components and services - that are proposed or planned to be used. The inventory may need to be procured, retrieved, - or resourced prior to use. - - - - - - - BOM consisting of information obtained prior to a build process and may contain source files - and development artifacts and manifests. The inventory may need to be resolved and retrieved - prior to use. - - - - - - - BOM consisting of information obtained during a build process where component inventory is - available for use. The precise versions of resolved components are usually available at this - time as well as the provenance of where the components were retrieved from. - - - - - - - BOM consisting of information obtained after a build process has completed and the resulting - components(s) are available for further analysis. Built components may exist as the result of a - CI/CD process, may have been installed or deployed to a system or device, and may need to be - retrieved or extracted from the system or device. - - - - - - - BOM produced that represents inventory that is running and operational. This may include staging - or production environments and will generally encompass multiple SBOMs describing the applications - and operating system, along with HBOMs describing the hardware that makes up the system. Operations - Bill of Materials (OBOM) can provide full-stack inventory of runtime environments, configurations, - and additional dependencies. - - - - - - - BOM consisting of information observed through network discovery providing point-in-time - enumeration of embedded, on-premise, and cloud-native services such as server applications, - connected devices, microservices, and serverless functions. - - - - - - - BOM containing inventory that will be, or has been retired from operations. - - - - - - - - - - - The name of the organization - - - - - The physical address (location) of the organization. - - - - - The URL of the organization. Multiple URLs are allowed. - - - - - A contact person at the organization. Multiple contacts are allowed. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the object elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - Information about the automated or manual tool used - - - - - The name of the vendor who created the tool - - - - - The name of the tool - - - - - The version of the tool - - - - - - - - - - - - Provides the ability to document external references related to the tool. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - The name of the contact - - - - - The email address of the contact. - - - - - The phone number of the contact. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the object elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - The organization that supplied the component. The supplier may often - be the manufacturer, but may also be a distributor or repackager. - - - - - - The organization that created the component. - Manufacturer is common in components created through automated processes. Components created through manual means may have './authors' instead. - - - - - - - The person(s) who created the component. - Authors are common in components created through manual processes. Components created through automated means may have `./manufacturer` instead. - - - - - - - - - - - - DEPRECATED - DO NOT USE. This will be removed in a future version. Use `./authors` or `./manufacturer` instead. - The person(s) or organization(s) that authored the component. - - - - - - The person(s) or organization(s) that published the component - - - - - The grouping name or identifier. This will often be a shortened, single - name of the company or project that produced the component, or the source package or - domain name. Whitespace and special characters should be avoided. Examples include: - apache, org.apache.commons, and apache.org. - - - - - The name of the component. This will often be a shortened, single name - of the component. Examples: commons-lang3 and jquery - - - - - The component version. The version should ideally comply with semantic versioning - but is not enforced. - - - - - Specifies a description for the component - - - - - Specifies the scope of the component. If scope is not specified, 'required' - scope SHOULD be assumed by the consumer of the BOM. - - - - - - - - - - - - - A copyright notice informing users of the underlying claims to - copyright ownership in a published work. - - - - - - Specifies a well-formed CPE name that conforms to the CPE 2.2 or 2.3 specification. See https://nvd.nist.gov/products/cpe - - - - - - - Specifies the package-url (purl). The purl, if specified, MUST be valid and conform - to the specification defined at: https://github.com/package-url/purl-spec - - - - - - - Specifies the OmniBOR Artifact ID. The OmniBOR, if specified, MUST be valid and conform - to the specification defined at: https://www.iana.org/assignments/uri-schemes/prov/gitoid - - - - - - - Specifies the Software Heritage persistent identifier (SWHID). The SWHID, if specified, MUST - be valid and conform to the specification defined at: - https://docs.softwareheritage.org/devel/swh-model/persistent-identifiers.html - - - - - - - Specifies metadata and content for ISO-IEC 19770-2 Software Identification (SWID) Tags. - - - - - - - DEPRECATED - DO NOT USE. This will be removed in a future version. Use the pedigree - element instead to supply information on exactly how the component was modified. - A boolean value indicating if the component has been modified from the original. - A value of true indicates the component is a derivative of the original. - A value of false indicates the component has not been modified from the original. - - - - - - - Component pedigree is a way to document complex supply chain scenarios where components are - created, distributed, modified, redistributed, combined with other components, etc. - - - - - - Provides the ability to document external references related to the - component or to the project the component describes. - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - A list of software and hardware components included in the parent component. This is not a - dependency tree. It provides a way to specify a hierarchical representation of component - assemblies, similar to system -> subsystem -> parts assembly in physical supply chains. - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - Provides the ability to document evidence collected through various forms of extraction or analysis. - - - - - Specifies optional release notes. - - - - - A model card describes the intended uses of a machine learning model and potential - limitations, including biases and ethical considerations. Model cards typically contain the - training parameters, which datasets were used to train the model, performance metrics, and other - relevant data useful for ML transparency. This object SHOULD be specified for any component of - type `machine-learning-model` and MUST NOT be specified for other component types. - - - - - This object SHOULD be specified for any component of type `data` and MUST NOT be - specified for other component types. - - - - - - Cryptographic assets have properties that uniquely define them and that make them actionable - for further reasoning. As an example, it makes a difference if one knows the algorithm family - (e.g. AES) or the specific variant or instantiation (e.g. AES-128-GCM). This is because the - security level and the algorithm primitive (authenticated encryption) is only defined by the - definition of the algorithm variant. The presence of a weak cryptographic algorithm like SHA1 - vs. HMAC-SHA1 also makes a difference. - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - Specifies the type of component. For software components, classify as application if no more - specific appropriate classification is available or cannot be determined for the component. - - - - - - - The OPTIONAL mime-type of the component. When used on file components, the mime-type - can provide additional context about the kind of file being represented such as an image, - font, or executable. Some library or framework components may also have an associated mime-type. - - - - - - - An optional identifier which can be used to reference the component elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - A valid SPDX license ID - - - - - If SPDX does not define the license used, this field may be used to provide the license name - - - - - - Specifies the optional full text of the attachment - - - - - The URL to the attachment file. If the attachment is a license or BOM, - an externalReference should also be specified for completeness. - - - - - Licensing details describing the licensor/licensee, license type, renewal and - expiration dates, and other important metadata - - - - - - License identifiers that may be used to manage licenses and - their lifecycle - - - - - - - - - - The individual or organization that grants a license to another - individual or organization - - - - - - - The organization that granted the license - - - - - The individual, not associated with an organization, - that granted the license - - - - - - - - - The individual or organization for which a license was granted to - - - - - - - The organization that was granted the license - - - - - The individual, not associated with an organization, - that was granted the license - - - - - - - - - The individual or organization that purchased the license - - - - - - - The organization that purchased the license - - - - - The individual, not associated with an organization, - that purchased the license - - - - - - - - - The purchase order identifier the purchaser sent to a supplier or - vendor to authorize a purchase - - - - - The type of license(s) that was granted to the licensee - - - - - - - - - - The timestamp indicating when the license was last - renewed. For new purchases, this is often the purchase or acquisition date. - For non-perpetual licenses or subscriptions, this is the timestamp of when the - license was last renewed. - - - - - The timestamp indicating when the current license - expires (if applicable). - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the license elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - - Declared licenses and concluded licenses represent two different stages in the - licensing process within software development. Declared licenses refer to the - initial intention of the software authors regarding the licensing terms under - which their code is released. On the other hand, concluded licenses are the - result of a comprehensive analysis of the project's codebase to identify and - confirm the actual licenses of the components used, which may differ from the - initially declared licenses. While declared licenses provide an upfront indication - of the licensing intentions, concluded licenses offer a more thorough understanding - of the actual licensing within a project, facilitating proper compliance and risk - management. Observed licenses are defined in `evidence.licenses`. Observed licenses - form the evidence necessary to substantiate a concluded license. - - - - - - - - - - The attachment data. Proactive controls such as input validation and sanitization should be employed to prevent misuse of attachment text. - - - - Specifies the content type of the text. Defaults to text/plain - if not specified. - - - - - - Specifies the optional encoding the text is represented in - - - - - - - - - - Specifies the file hash of the component - - - - - - Specifies the algorithm used to create the hash - - - - - - - - - - - The component is required for runtime - - - - - The component is optional at runtime. Optional components are components that - are not capable of being called due to them not be installed or otherwise accessible by any means. - Components that are installed but due to configuration or other restrictions are prohibited from - being called must be scoped as 'required'. - - - - - Components that are excluded provide the ability to document component usage - for test and other non-runtime purposes. Excluded components are not reachable within a call - graph at runtime. - - - - - - - - - - A software application. Refer to https://en.wikipedia.org/wiki/Application_software - for information about applications. - - - - - A software framework. Refer to https://en.wikipedia.org/wiki/Software_framework - for information on how frameworks vary slightly from libraries. - - - - - A software library. Refer to https://en.wikipedia.org/wiki/Library_(computing) - for information about libraries. All third-party and open source reusable components will likely - be a library. If the library also has key features of a framework, then it should be classified - as a framework. If not, or is unknown, then specifying library is recommended. - - - - - A packaging and/or runtime format, not specific to any particular technology, - which isolates software inside the container from software outside of a container through - virtualization technology. Refer to https://en.wikipedia.org/wiki/OS-level_virtualization - - - - - A runtime environment which interprets or executes software. This may include - runtimes such as those that execute bytecode or low-code/no-code application platforms. - - - - - A software operating system without regard to deployment model - (i.e. installed on physical hardware, virtual machine, image, etc) Refer to - https://en.wikipedia.org/wiki/Operating_system - - - - - A hardware device such as a processor, or chip-set. A hardware device - containing firmware SHOULD include a component for the physical hardware itself, and another - component of type 'firmware' or 'operating-system' (whichever is relevant), describing - information about the software running on the device. - See also the list of known device properties: https://github.com/CycloneDX/cyclonedx-property-taxonomy/blob/main/cdx/device.md - - - - - - A special type of software that operates or controls a particular type of device. - Refer to https://en.wikipedia.org/wiki/Device_driver - - - - - A special type of software that provides low-level control over a devices - hardware. Refer to https://en.wikipedia.org/wiki/Firmware - - - - - A computer file. Refer to https://en.wikipedia.org/wiki/Computer_file - for information about files. - - - - - A model based on training data that can make predictions or decisions without - being explicitly programmed to do so. - - - - - A collection of discrete values that convey information. - - - - - A cryptographic asset including algorithms, protocols, certificates, keys, tokens, and secrets. - - - - - - - - - - - - - - - - - - - - - - - - - - - A license that grants use of software solely for the purpose - of education or research. - - - - - A license covering use of software embedded in a specific - piece of hardware. - - - - - A Client Access License (CAL) allows client computers to access - services provided by server software. - - - - - A Concurrent User license (aka floating license) limits the - number of licenses for a software application and licenses are shared among - a larger number of users. - - - - - A license where the core of a computer's processor is assigned - a specific number of points. - - - - - A license for which consumption is measured by non-standard - metrics. - - - - - A license that covers a defined number of installations on - computers and other types of devices. - - - - - A license that grants permission to install and use software - for trial purposes. - - - - - A license that grants access to the software to one or more - pre-defined users. - - - - - A license that grants access to the software on one or more - pre-defined computers or devices. - - - - - An Original Equipment Manufacturer license that is delivered - with hardware, cannot be transferred to other hardware, and is valid for the - life of the hardware. - - - - - A license where the software is sold on a one-time basis and - the licensee can use a copy of the software indefinitely. - - - - - A license where each installation consumes points per - processor. - - - - - A license where the licensee pays a fee to use the software - or service. - - - - - A license that grants access to the software or service by a - specified number of users. - - - - - Another license type. - - - - - - - - - - - - - - - - - - - - - - - - - - - Define the format for acceptable CPE URIs. Supports CPE 2.2 and CPE 2.3 formats. - Refer to https://nvd.nist.gov/products/cpe for official specification. - - - - - - - - - - - - Specifies the full content of the SWID tag. - - - - - The URL to the SWID file. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - Maps to the tagId of a SoftwareIdentity. - - - - - Maps to the name of a SoftwareIdentity. - - - - - Maps to the version of a SoftwareIdentity. - - - - - Maps to the tagVersion of a SoftwareIdentity. - - - - - Maps to the patch of a SoftwareIdentity. - - - - - - - - Defines a string representation of a UUID conforming to RFC 4122. - - - - - - - - - - - - Version Control System - - - - - Issue or defect tracking system, or an Application Lifecycle Management (ALM) system - - - - - Website - - - - - Security advisories - - - - - Bill-of-materials (SBOM, OBOM, HBOM, SaaSBOM, etc) - - - - - Mailing list or discussion group - - - - - Social media account - - - - - Real-time chat platform - - - - - Documentation, guides, or how-to instructions - - - - - Community or commercial support - - - - - The location where the source code distributable can be obtained. This is often an archive format such as zip or tgz. The source-distribution type complements use of the version control (vcs) type. - - - - - Direct or repository download location - - - - - The location where a component was published to. This is often the same as "distribution" but may also include specialized publishing processes that act as an intermediary - - - - - The URL to the license file. If a license URL has been defined in the license - node, it should also be defined as an external reference for completeness - - - - - Build-system specific meta file (i.e. pom.xml, package.json, .nuspec, etc) - - - - - URL to an automated build system - - - - - URL to release notes - - - - - Specifies a way to contact the maintainer, supplier, or provider in the event of a security incident. Common URIs include links to a disclosure procedure, a mailto (RFC-2368) that specifies an email address, a tel (RFC-3966) that specifies a phone number, or dns (RFC-4501) that specifies the records containing DNS Security TXT. - - - - - A model card describes the intended uses of a machine learning model, potential - limitations, biases, ethical considerations, training parameters, datasets used to train the - model, performance metrics, and other relevant data useful for ML transparency. - - - - - A record of events that occurred in a computer system or application, such as problems, errors, or information on current operations. - - - - - Parameters or settings that may be used by other components or services. - - - - - Information used to substantiate a claim. - - - - - Describes how a component or service was manufactured or deployed. - - - - - Human or machine-readable statements containing facts, evidence, or testimony - - - - - An enumeration of identified weaknesses, threats, and countermeasures, dataflow diagram (DFD), attack tree, and other supporting documentation in human-readable or machine-readable format - - - - - The defined assumptions, goals, and capabilities of an adversary. - - - - - Identifies and analyzes the potential of future events that may negatively impact individuals, assets, and/or the environment. Risk assessments may also include judgments on the tolerability of each risk. - - - - - A Vulnerability Disclosure Report (VDR) which asserts the known and previously unknown vulnerabilities that affect a component, service, or product including the analysis and findings describing the impact (or lack of impact) that the reported vulnerability has on a component, service, or product. - - - - - A Vulnerability Exploitability eXchange (VEX) which asserts the known vulnerabilities that do not affect a product, product family, or organization, and optionally the ones that do. The VEX should include the analysis and findings describing the impact (or lack of impact) that the reported vulnerability has on the product, product family, or organization. - - - - - Results from an authorized simulated cyberattack on a component or service, otherwise known as a penetration test - - - - - SARIF or proprietary machine or human-readable report for which static analysis has identified code quality, security, and other potential issues with the source code - - - - - Dynamic analysis report that has identified issues such as vulnerabilities and misconfigurations - - - - - Report generated by analyzing the call stack of a running application - - - - - Report generated by Software Composition Analysis (SCA), container analysis, or other forms of component analysis - - - - - Report containing a formal assessment of an organization, business unit, or team against a maturity model - - - - - Industry, regulatory, or other certification from an accredited (if applicable) certification body - - - - - Report or system in which quality metrics can be obtained - - - - - Code or configuration that defines and provisions virtualized infrastructure, commonly referred to as Infrastructure as Code (IaC) - - - - - Plans of Action and Milestones (POAM) complement an "attestation" external reference. POAM is defined by NIST as a "document that identifies tasks needing to be accomplished. It details resources required to accomplish the elements of the plan, any milestones in meeting the tasks and scheduled completion dates for the milestones". - - - - - An e-signature is commonly a scanned representation of a written signature or a stylized script of the persons name. - - - - - A signature that leverages cryptography, typically public/private key pairs, which provides strong authenticity verification. - - - - - Document that complies with RFC-9116 (A File Format to Aid in Security Vulnerability Disclosure) - - - - - Use this if no other types accurately describe the purpose of the external reference - - - - - - - - - External references provide a way to document systems, sites, and information that may be - relevant, but are not included with the BOM. They may also establish specific relationships - within or external to the BOM. - - - - - - Zero or more external references can be defined - - - - - - - - - - The URI (URL or URN) to the external reference. External references - are URIs and therefore can accept any URL scheme including https, mailto, tel, and dns. - External references may also include formally registered URNs such as CycloneDX BOM-Link to - reference CycloneDX BOMs or any object within a BOM. BOM-Link transforms applicable external - references into relationships that can be expressed in a BOM or across BOMs. Refer to: - https://cyclonedx.org/capabilities/bomlink/ - - - - - - - - An optional comment describing the external reference - - - - - - - - - - - - - Specifies the type of external reference. There are built-in types to describe common - references. If a type does not exist for the reference being referred to, use the "other" type. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - Zero or more commits can be specified. - - - - - Specifies an individual commit. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - - A unique identifier of the commit. This may be version control - specific. For example, Subversion uses revision numbers whereas git uses commit hashes. - - - - - - The URL to the commit. This URL will typically point to a commit - in a version control system. - - - - - - The author who created the changes in the commit - - - - - The person who committed or pushed the commit - - - - - The text description of the contents of the commit - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - Zero or more patches can be specified. - - - - - Specifies an individual patch. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - - The patch file (or diff) that show changes. - Refer to https://en.wikipedia.org/wiki/Diff - - - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - Specifies the purpose for the patch including the resolution of defects, - security issues, or new behavior or functionality - - - - - - - - - A patch which is not developed by the creators or maintainers of the software - being patched. Refer to https://en.wikipedia.org/wiki/Unofficial_patch - - - - - A patch which dynamically modifies runtime behavior. - Refer to https://en.wikipedia.org/wiki/Monkey_patch - - - - - A patch which takes code from a newer version of software and applies - it to older versions of the same software. Refer to https://en.wikipedia.org/wiki/Backporting - - - - - A patch created by selectively applying commits from other versions or - branches of the same software. - - - - - - - - - - A fault, flaw, or bug in software - - - - - A new feature or behavior in software - - - - - A special type of defect which impacts security - - - - - - - - - - Specifies the optional text of the diff - - - - - Specifies the URL to the diff - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - An individual issue that has been resolved. - - - - - - The identifier of the issue assigned by the source of the issue - - - - - The name of the issue - - - - - A description of the issue - - - - - - - The source of the issue where it is documented. - - - - - - - The name of the source. For example "National Vulnerability Database", - "NVD", and "Apache" - - - - - - - The url of the issue documentation as provided by the source - - - - - - - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - Specifies the type of issue - - - - - - - - - The timestamp in which the action occurred - - - - - The name of the individual who performed the action - - - - - The email address of the individual who performed the action - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - Component pedigree is a way to document complex supply chain scenarios where components are created, - distributed, modified, redistributed, combined with other components, etc. Pedigree supports viewing - this complex chain from the beginning, the end, or anywhere in the middle. It also provides a way to - document variants where the exact relation may not be known. - - - - - - Describes zero or more components in which a component is derived - from. This is commonly used to describe forks from existing projects where the forked version - contains a ancestor node containing the original component it was forked from. For example, - Component A is the original component. Component B is the component being used and documented - in the BOM. However, Component B contains a pedigree node with a single ancestor documenting - Component A - the original component from which Component B is derived from. - - - - - - Descendants are the exact opposite of ancestors. This provides a - way to document all forks (and their forks) of an original or root component. - - - - - - Variants describe relations where the relationship between the - components are not known. For example, if Component A contains nearly identical code to - Component B. They are both related, but it is unclear if one is derived from the other, - or if they share a common ancestor. - - - - - - A list of zero or more commits which provide a trail describing - how the component deviates from an ancestor, descendant, or variant. - - - - - A list of zero or more patches describing how the component - deviates from an ancestor, descendant, or variant. Patches may be complementary to commits - or may be used in place of commits. - - - - - Notes, observations, and other non-structured commentary - describing the components pedigree. - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - - The component or service that is a dependency of this dependency object. - - - - - - The component or service that define a given specification or standard, which is provided or implemented by this dependency object. - For example, a cryptographic library which implements a cryptographic algorithm. A component which implements another component does not imply that the implementation is in use. - - - - - - References a component or service by its bom-ref attribute - - - - - - - - References a component or service by its bom-ref attribute - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - Defines the direct dependencies of a component or service. Components or services - that do not have their own dependencies MUST be declared as empty elements within the graph. - Components or services that are not represented in the dependency graph MAY have unknown - dependencies. It is RECOMMENDED that implementations assume this to be opaque and not an - indicator of a object being dependency-free. It is RECOMMENDED to leverage compositions to - indicate unknown dependency graphs. - - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - The organization that provides the service. - - - - - The grouping name, namespace, or identifier. This will often be a shortened, - single name of the company or project that produced the service or domain name. - Whitespace and special characters should be avoided. - - - - - The name of the service. This will often be a shortened, single name - of the service. - - - - - The service version. - - - - - Specifies a description for the service. - - - - - - - - A service endpoint URI. - - - - - - - - A boolean value indicating if the service requires authentication. - A value of true indicates the service requires authentication prior to use. - A value of false indicates the service does not require authentication. - - - - - A boolean value indicating if use of the service crosses a trust zone or boundary. - A value of true indicates that by using the service, a trust boundary is crossed. - A value of false indicates that by using the service, a trust boundary is not crossed. - - - - - The name of the trust zone the service resides in. - - - - - - - - - DEPRECATED: Specifies the data classification. THIS FIELD IS DEPRECATED AS OF v1.5. Use dataflow\classification instead - - - - - - Specifies the data classification. - - - - - - Specifies the data classification. - - - - - - The URI, URL, or BOM-Link of the components or services the data came in from. - - - - - - - - - - - - - - The URI, URL, or BOM-Link of the components or services the data is sent to. - - - - - - - - - - - - - - - - Name for the defined data. - - - - - - - Short description of the data content and usage. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - Provides the ability to document external references related to the service. - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - A list of services included or deployed behind the parent service. This is not a dependency - tree. It provides a way to specify a hierarchical representation of service assemblies. - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - Specifies optional release notes. - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the service elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - Specifies the data classification. - - - - - - Specifies the flow direction of the data. - - - - - - - - - Specifies the flow direction of the data. Valid values are: - inbound, outbound, bi-directional, and unknown. Direction is relative to the service. - Inbound flow states that data enters the service. Outbound flow states that data - leaves the service. Bi-directional states that data flows both ways, and unknown - states that the direction is not known. - - - - - - - - - - - - - - - A valid SPDX license expression. - Refer to https://spdx.org/specifications for syntax requirements - - Example values: - - Apache-2.0 AND (MIT OR GPL-2.0-only) - - GPL-3.0-only WITH Classpath-exception-2.0 - - - - - - - - - An optional identifier which can be used to reference the license elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - - Declared licenses and concluded licenses represent two different stages in the - licensing process within software development. Declared licenses refer to the - initial intention of the software authors regarding the licensing terms under - which their code is released. On the other hand, concluded licenses are the - result of a comprehensive analysis of the project's codebase to identify and - confirm the actual licenses of the components used, which may differ from the - initially declared licenses. While declared licenses provide an upfront indication - of the licensing intentions, concluded licenses offer a more thorough understanding - of the actual licensing within a project, facilitating proper compliance and risk - management. Observed licenses are defined in `evidence.licenses`. Observed licenses - form the evidence necessary to substantiate a concluded license. - - - - - - - - - - - - - - - - Declared licenses represent the initial intentions of authors regarding - the licensing terms of their code. - - - - - - - Concluded licenses are verified and confirmed. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Examines the source code without executing it. - - - - - - - Examines a compiled binary through reverse engineering, typically via disassembly or bytecode reversal. - - - - - - - Examines a package management system such as those used for building software or installing software. - - - - - - - Examines the Abstract Syntax Tree (AST) of source code or a compiled binary. - - - - - - - Evaluates the cryptographic hash of a component against a set of pre-computed hashes of identified software. - - - - - - - Examines the call stack of running applications by intercepting and monitoring application logic without the need to modify the application. - - - - - - - Evaluates a running application. - - - - - - - Evaluates file name of a component against a set of known file names of identified software. - - - - - - - A testimony to the accuracy of the identify of a component made by an individual or entity. - - - - - - - Any other technique. - - - - - - - - - - - Evidence that substantiates the identity of a component. The identify may be an - object or an array of identity objects. Support for specifying identify as a single object was - introduced in CycloneDX v1.5. "unbounded" was introduced in v1.6. It is RECOMMENDED that all - implementations are aware of "unbounded". - - - - - - The identity field of the component which the evidence describes. - - - - - The overall confidence of the evidence from 0 - 1, where 1 is 100% confidence. - - - - - The value of the field (cpe, purl, etc) that has been concluded based on the aggregate of all methods (if available). - - - - - The methods used to extract and/or analyze the evidence. - - - - - - - - - The technique used in this method of analysis. - - - - - The confidence of the evidence from 0 - 1, where 1 is 100% confidence. Confidence is specific to the technique used. Each technique of analysis can have independent confidence. - - - - - The value or contents of the evidence. - - - - - - - - - - - - The object in the BOM identified by its bom-ref. This is often a component or service, - but may be any object type supporting bom-refs. Tools used for analysis should already - be defined in the BOM, either in the metadata/tools, components, or formulation. - - - - - - - - - - - - - - Evidence of individual instances of a component spread across multiple locations. - - - - - - - - - The location or path to where the component was found. - - - - - The line number where the component was found. - - - - - The offset where the component was found. - - - - - The symbol name that was found associated with the component. - - - - - Any additional context of the detected component (e.g. a code snippet). - - - - - - - - An optional identifier which can be used to reference the occurrence elsewhere - in the BOM. Every bom-ref MUST be unique within the BOM. - - - - - - - - - - - Evidence of the components use through the callstack. - - - - - - - - - - - - A package organizes modules into namespaces, providing a unique namespace for each type it contains. - - - - - A module or class that encloses functions/methods and other code. - - - - - A block of code designed to perform a particular task. - - - - - Optional arguments that are passed to the module or function. - - - - - - - - - - The line number the code that is called resides on. - - - - - The column the code that is called resides. - - - - - The full path and filename of the module. - - - - - - - - - - - - The object in the BOM identified by its bom-ref. This is often a component or service, - but may be any object type supporting bom-refs. Tools used for analysis should already - be defined in the BOM, either in the metadata/tools, components, or formulation. - - - - - - - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - Specifies an aggregate type that describe how complete a relationship is. - - - - - - The bom-ref identifiers of the components or services being described. Assemblies refer to - nested relationships whereby a constituent part may include other constituent parts. References - do not cascade to child parts. References are explicit for the specified constituent part only. - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - The bom-ref identifiers of the components or services being described. Dependencies refer to a - relationship whereby an independent constituent part requires another independent constituent - part. References do not cascade to transitive dependencies. References are explicit for the - specified dependency only. - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - The bom-ref identifiers of the vulnerabilities being described. - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - - An optional identifier which can be used to reference the composition elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - - - - - The relationship is complete. No further relationships including constituent components, services, or dependencies are known to exist. - - - - - The relationship is incomplete. Additional relationships exist and may include constituent components, services, or dependencies. - - - - - The relationship is incomplete. Only relationships for first-party components, services, or their dependencies are represented. - - - - - The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are represented, limited specifically to those that are proprietary. - - - - - The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are represented, limited specifically to those that are opensource. - - - - - The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are represented. - - - - - The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are represented, limited specifically to those that are proprietary. - - - - - The relationship is incomplete. Only relationships for third-party components, services, or their dependencies are represented, limited specifically to those that are opensource. - - - - - The relationship may be complete or incomplete. This usually signifies a 'best-effort' to obtain constituent components, services, or dependencies but the completeness is inconclusive. - - - - - The relationship completeness is not specified. - - - - - - - - - Defines a syntax for representing two character language code (ISO-639) followed by an optional two - character country code. The language code MUST be lower case. If the country code is specified, the - country code MUST be upper case. The language code and country code MUST be separated by a minus sign. - Examples: en, en-US, fr, fr-CA - - - - - - - - - - - - The software versioning type. It is RECOMMENDED that the release type use one - of 'major', 'minor', 'patch', 'pre-release', or 'internal'. Representing all possible software - release types is not practical, so standardizing on the recommended values, whenever possible, - is strongly encouraged. - * major = A major release may contain significant changes or may introduce breaking changes. - * minor = A minor release, also known as an update, may contain a smaller number of changes than major releases. - * patch = Patch releases are typically unplanned and may resolve defects or important security issues. - * pre-release = A pre-release may include alpha, beta, or release candidates and typically have - limited support. They provide the ability to preview a release prior to its general availability. - * internal = Internal releases are not for public consumption and are intended to be used exclusively - by the project or manufacturer that produced it. - - - - - - The title of the release. - - - - - The URL to an image that may be prominently displayed with the release note. - - - - - The URL to an image that may be used in messaging on social media platforms. - - - - - A short description of the release. - - - - - The date and time (timestamp) when the release note was created. - - - - - - - - One or more alternate names the release may be referred to. This may - include unofficial terms used by development and marketing teams (e.g. code names). - - - - - - - - - A collection of issues that have been resolved. - - - - - - - - - - - - - Zero or more release notes containing the locale and content. Multiple - note elements may be specified to support release notes in a wide variety of languages. - - - - - - The ISO-639 (or higher) language code and optional ISO-3166 - (or higher) country code. Examples include: "en", "en-US", "fr" and "fr-CA". - - - - - Specifies the full content of the release note. - - - - - - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - A model card describes the intended uses of a machine learning model and potential limitations, including - biases and ethical considerations. Model cards typically contain the training parameters, which datasets - were used to train the model, performance metrics, and other relevant data useful for ML transparency. - This object SHOULD be specified for any component of type `machine-learning-model` and MUST NOT be specified - for other component types. - - - - - - - Hyper-parameters for construction of the model. - - - - - - - - The overall approach to learning used by the model for problem solving. - - - - - - - - Learning types describing the learning problem or hybrid learning problem. - - - - - - - - - - Directly influences the input and/or output. Examples include classification, - regression, clustering, etc. - - - - - - - The model architecture family such as transformer network, convolutional neural - network, residual neural network, LSTM neural network, etc. - - - - - - - The specific architecture of the model such as GPT-1, ResNet-50, YOLOv3, etc. - - - - - - - The datasets used to train and evaluate the model. - - - - - - - References a data component by the components bom-ref attribute - - - - - - - - - - - - - The input format(s) of the model - - - - - - - - - - - The data format for input to the model. Example formats include string, image, time-series - - - - - - - - - - - - - The output format(s) from the model - - - - - - - - - - - The data format for output from the model. Example formats include string, image, time-series - - - - - - - - - - - - - - - - A quantitative analysis of the model - - - - - - - - - - - - - - The type of performance metric. - - - - - - - The value of the performance metric. - - - - - - - The name of the slice this metric was computed on. By default, assume - this metric is not sliced. - - - - - - - The confidence interval of the metric. - - - - - - - - The lower bound of the confidence interval. - - - - - - - The upper bound of the confidence interval. - - - - - - - - - - - - - - - - A collection of graphics that represent various measurements - - - - - - - - A description of this collection of graphics. - - - - - - - A collection of graphics. - - - - - - - - - - - The name of the graphic. - - - - - - - The graphic (vector or raster). Base64 encoding MUST be specified for binary images. - - - - - - - - - - - - - - - - - - - What considerations should be taken into account regarding the model's construction, training, - and application? - - - - - - - - Who are the intended users of the model? - - - - - - - - - - - - What are the intended use cases of the model? - - - - - - - - - - - - What are the known technical limitations of the model? E.g. What kind(s) of data - should the model be expected not to perform well on? What are the factors that might - degrade model performance? - - - - - - - - - - - - What are the known tradeoffs in accuracy/performance of the model? - - - - - - - - - - - - What are the ethical risks involved in the application of this model? - - - - - - - - - - - The name of the risk - - - - - - - Strategy used to address this risk - - - - - - - - - - - - - What are the various environmental impacts the corresponding machine learning model has exhibited across its lifecycle? - - - - - - - How does the model affect groups at risk of being systematically disadvantaged? - What are the harms and benefits to the various affected groups? - - - - - - - - - - - The groups or individuals at risk of being systematically disadvantaged by the model. - - - - - - - Expected benefits to the identified groups. - - - - - - - Expected harms to the identified groups. - - - - - - - With respect to the benefits and harms outlined, please - describe any mitigation strategy implemented. - - - - - - - - - - - - - - - - - An optional identifier which can be used to reference the model card elsewhere in the BOM. - Every bom-ref MUST be unique within the BOM. - - - - - - - - - Describes various environmental impact metrics. - - - - - - - Describes energy consumption information incurred for one or more component lifecycle activities. - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - Describes energy consumption information incurred for the specified lifecycle activity. - - - - - - - The type of activity that is part of a machine learning model development or operational lifecycle. - - - - - - - - model design including problem framing, goal definition and algorithm selection. - - - - - - - model data acquisition including search, selection and transfer. - - - - - - - model data preparation including data cleaning, labeling and conversion. - - - - - - - model building, training and generalized tuning. - - - - - - - refining a trained model to produce desired outputs for a given problem space. - - - - - - - model validation including model output evaluation and testing. - - - - - - - explicit model deployment to a target hosting infrastructure. - - - - - - - generating an output response from a hosted model from a set of inputs. - - - - - - - a lifecycle activity type whose description does not match currently defined values. - - - - - - - - - - The provider(s) of the energy consumed by the associated model development lifecycle activity. - - - - - - - The total energy cost associated with the model lifecycle activity. - - - - - - - The CO2 cost (debit) equivalent to the total energy cost. - - - - - - - The CO2 offset (credit) for the CO2 equivalent cost. - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - - - - - A measure of energy. - - - - - - - Quantity of energy. - - - - - - - Unit of energy. - - - - - - - - kilowatt-hour (kWh) is the energy delivered by one kilowatt (kW) of power for one hour (h). - - - - - - - - - - - - - A measure of carbon dioxide (CO2). - - - - - - - Quantity of carbon dioxide (CO2). - - - - - - - Unit of carbon dioxide (CO2). - - - - - - - - Tonnes (t) of carbon dioxide (CO2) equivalent (eq). - - - - - - - - - - - - - Describes the physical provider of energy used for model development or operations. - - - - - - - A description of the energy provider. - - - - - - - The organization of the energy provider. - - - - - - - The energy source for the energy provider. - - - - - - - - Energy produced by types of coal. - - - - - - - Petroleum products (primarily crude oil and its derivative fuel oils). - - - - - - - Hydrocarbon gas liquids (HGL) that occur as gases at atmospheric pressure and as liquids under higher pressures including Natural gas (C5H12 and heavier), Ethane (C2H6), Propane (C3H8), etc. - - - - - - - Energy produced from the cores of atoms (i.e., through nuclear fission or fusion). - - - - - - - Energy produced from moving air. - - - - - - - Energy produced from the sun (i.e., solar radiation). - - - - - - - Energy produced from heat within the earth. - - - - - - - Energy produced from flowing water. - - - - - - - Liquid fuels produced from biomass feedstocks (i.e., organic materials such as plants or animals). - - - - - - - The energy source is unknown. - - - - - - - An energy source that is not listed. - - - - - - - - - - The energy provided by the energy source for an associated activity. - - - - - - External references provide a way to document systems, sites, and information that may be relevant but are not included with the BOM. They may also establish specific relationships within or external to the BOM. - - - - - - - An optional identifier which can be used to reference the energy provider elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - - - - An address used to identify a contactable location. - - - - - - - The country name or the two-letter ISO 3166-1 country code. - - - - - - - The region or state in the country. For example, Texas. - - - - - - - The locality or city within the country. For example, Austin. - - - - - - - The post office box number. For example, 901. - - - - - - - The postal code. For example, 78758. - - - - - - - The street address. For example, 100 Main Street. - - - - - - - - An optional identifier which can be used to reference the address elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - - - - - - Supervised machine learning involves training an algorithm on labeled - data to predict or classify new data based on the patterns learned from - the labeled examples. - - - - - - - Unsupervised machine learning involves training algorithms on unlabeled - data to discover patterns, structures, or relationships without explicit - guidance, allowing the model to identify inherent structures or clusters - within the data. - - - - - - - Reinforcement learning is a type of machine learning where an agent learns - to make decisions by interacting with an environment to maximize cumulative - rewards, through trial and error. - - - - - - - Semi-supervised machine learning utilizes a combination of labeled and - unlabeled data during training to improve model performance, leveraging - the benefits of both supervised and unsupervised learning techniques. - - - - - - - Self-supervised machine learning involves training models to predict parts - of the input data from other parts of the same data, without requiring - external labels, enabling learning from large amounts of unlabeled data. - - - - - - - - - - - - The general theme or subject matter of the data being specified. - - - - - - - The name of the dataset. - - - - - - - The contents or references to the contents of the data being described. - - - - - - - An optional way to include textual or encoded data. - - - - - The URL to where the data can be retrieved. - - - - - Provides the ability to document name-value parameters used for configuration. - - - - - - - - - Data classification tags data according to its type, sensitivity, and value if altered, stolen, or destroyed. - - - - - - - A description of any sensitive data in a dataset. - - - - - - - A collection of graphics that represent various measurements. - - - - - - - A description of the dataset. Can describe size of dataset, whether it's used for source code, - training, testing, or validation, etc. - - - - - - - - - An optional identifier which can be used to reference the dataset elsewhere in the BOM. - Every bom-ref MUST be unique within the BOM. - - - - - - - - - - - Data custodians are responsible for the safe custody, transport, and storage of data. - - - - - - - - - - - - Data stewards are responsible for data content, context, and associated business rules. - - - - - - - - - - - - Data owners are concerned with risk and appropriate access to data. - - - - - - - - - - - - - - - - - - - - - - A collection of graphics that represent various measurements. - - - - - - - A description of this collection of graphics. - - - - - - - A collection of graphics. - - - - - - - - - - - The name of the graphic. - - - - - - - The graphic (vector or raster). Base64 encoding MUST be specified for binary images. - - - - - - - - - - - - - - - - - Any type of code, code snippet, or data-as-code. - - - - - Parameters or settings that may be used by other components. - - - - - A collection of data. - - - - - Data that can be used to create new instances of what the definition defines. - - - - - Any other type of data that does not fit into existing definitions. - - - - - - - - - References a component or service by its bom-ref attribute - - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - Specifies an individual property with a name and value. - - - - - - The name of the property. Duplicate names are allowed, each potentially having a different value. - - - - - - - - - - - Defines a weakness in a component or service that could be exploited or triggered by a threat source. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - The identifier that uniquely identifies the vulnerability. For example: - CVE-2021-39182, GHSA-35m5-8cvj-8783, and SNYK-PYTHON-ENROCRYPT-1912876. - - - - - The source that published the vulnerability. - - - - - Zero or more pointers to vulnerabilities that are the equivalent of the - vulnerability specified. Often times, the same vulnerability may exist in multiple sources of - vulnerability intelligence, but have different identifiers. References provide a way to - correlate vulnerabilities across multiple sources of vulnerability intelligence. - - - - - - A pointer to a vulnerability that is the equivalent of the - vulnerability specified. - - - - - - The identifier that uniquely identifies the vulnerability. For example: - CVE-2021-39182, GHSA-35m5-8cvj-8783, and SNYK-PYTHON-ENROCRYPT-1912876. - - - - - The source that published the vulnerability. - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - List of vulnerability ratings. - - - - - - - - - - - - List of Common Weaknesses Enumerations (CWEs) codes that describes this vulnerability. - For example 399 (of https://cwe.mitre.org/data/definitions/399.html) - - - - - - - - - - A description of the vulnerability as provided by the source. - - - - - If available, an in-depth description of the vulnerability as provided by the - source organization. Details often include information useful in understanding root cause. - - - - - Recommendations of how the vulnerability can be remediated or mitigated. - - - - - A bypass, usually temporary, of the vulnerability that reduces its likelihood and/or impact. Workarounds often involve changes to configuration or deployments. - - - - - - - Evidence used to reproduce the vulnerability. - - - - - - Precise steps to reproduce the vulnerability. - - - - - A description of the environment in which reproduction was possible. - - - - - Supporting material that helps in reproducing or understanding how reproduction is possible. This may include screenshots, payloads, and PoC exploit code. - - - - - - - - - - - - - - - Published advisories of the vulnerability if provided. - - - - - - - - - - The date and time (timestamp) when the vulnerability record was created in the vulnerability database. - - - - - The date and time (timestamp) when the vulnerability record was first published. - - - - - The date and time (timestamp) when the vulnerability record was last updated. - - - - - The date and time (timestamp) when the vulnerability record was rejected (if applicable). - - - - - Individuals or organizations credited with the discovery of the vulnerability. - - - - - - The organizations credited with vulnerability discovery. - - - - - - - - - - The individuals, not associated with organizations, that are credited with vulnerability discovery. - - - - - - - - - - - - - The tool(s) used to identify, confirm, or score the vulnerability. - - - - - - - DEPRECATED. Use tools\components or tools\services instead. - - - - - - - A list of software and hardware components used as tools. - - - - - A list of services used as tools. - - - - - - - - - - - An assessment of the impact and exploitability of the vulnerability. - - - - - - - Declares the current state of an occurrence of a vulnerability, after automated or manual analysis. - - - - - - - The rationale of why the impact analysis state was asserted. - - - - - - A response to the vulnerability by the manufacturer, supplier, or - project responsible for the affected component or service. More than one response - is allowed. Responses are strongly encouraged for vulnerabilities where the analysis - state is exploitable. - - - - - - - - - - - Detailed description of the impact including methods used during assessment. - If a vulnerability is not exploitable, this field should include specific details - on why the component or service is not impacted by this vulnerability. - - - - - - - The date and time (timestamp) when the analysis was first issued. - - - - - - - The date and time (timestamp) when the analysis was last updated. - - - - - - - - - The components or services that are affected by the vulnerability. - - - - - - - - - References a component or service by the objects bom-ref. - - - - - - - - Zero or more individual versions or range of versions. - - - - - - - - - - A single version of a component or service. - - - - - A version range specified in Package URL Version Range syntax (vers) which is defined at https://github.com/package-url/purl-spec/VERSION-RANGE-SPEC.rst - - - - - - - The vulnerability status for the version or range of versions. - - - - - - - - - - - - - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - - An optional identifier which can be used to reference the vulnerability elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - - - - - The name of the source. - For example: NVD, National Vulnerability Database, OSS Index, VulnDB, and GitHub Advisories - - - - - - The url of the vulnerability documentation as provided by the source. - For example: https://nvd.nist.gov/vuln/detail/CVE-2021-39182 - - - - - - - - - - The source that calculated the severity or risk rating of the vulnerability. - - - - - The numerical score of the rating. - - - - - Textual representation of the severity that corresponds to the numerical score of the rating. - - - - - The risk scoring methodology/standard used. - - - - - Textual representation of the metric values used to score the vulnerability. - - - - - An optional reason for rating the vulnerability as it was. - - - - - - - - - - An optional name of the advisory. - - - - - Location where the advisory can be obtained. - - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - The organization that created the annotation - - - - - The person that created the annotation - - - - - The tool or component that created the annotation - - - - - The service that created the annotation - - - - - - - - - - - The objects in the BOM identified by their bom-ref's. This is often components or services, but may be any object type supporting bom-refs. - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - The organization, individual, component, or service which created the textual content - of the annotation. - - - - - The date and time (timestamp) when the annotation was created. - - - - - The textual content of the annotation. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the annotation elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - Textual representation of the severity of the vulnerability adopted by the analysis method. If the - analysis method uses values other than what is provided, the user is expected to translate appropriately. - - - - - - - - - - - - - - - - - Declares the current state of an occurrence of a vulnerability, after automated or manual analysis. - - - - - - - The vulnerability has been remediated. - - - - - - - The vulnerability has been remediated and evidence of the changes are provided in the affected - components pedigree containing verifiable commit history and/or diff(s). - - - - - - - The vulnerability may be directly or indirectly exploitable. - - - - - - - The vulnerability is being investigated. - - - - - - - The vulnerability is not specific to the component or service and was falsely identified or associated. - - - - - - - The component or service is not affected by the vulnerability. Justification should be specified - for all not_affected cases. - - - - - - - - - - The rationale of why the impact analysis state was asserted. - - - - - - - The code has been removed or tree-shaked. - - - - - - - The vulnerable code is not invoked at runtime. - - - - - - - Exploitability requires a configurable option to be set/unset. - - - - - - - Exploitability requires a dependency that is not present. - - - - - - - Exploitability requires a certain environment which is not present. - - - - - - - Exploitability requires a compiler flag to be set/unset. - - - - - - - Exploits are prevented at runtime. - - - - - - - Attacks are blocked at physical, logical, or network perimeter. - - - - - - - Preventative measures have been implemented that reduce the likelihood and/or impact of the vulnerability. - - - - - - - - - - Specifies the severity or risk scoring methodology or standard used. - - - - - - - Common Vulnerability Scoring System v2.0 standard as defined at https://www.first.org/cvss/v2/ - - - - - - - Common Vulnerability Scoring System v3.0 standard as defined at https://www.first.org/cvss/v3-0/ - - - - - - - Common Vulnerability Scoring System v3.1 standard as defined at https://www.first.org/cvss/v3-1/ - - - - - - - Common Vulnerability Scoring System v4.0 standard as defined at https://www.first.org/cvss/v4-0/ - - - - - - - OWASP Risk Rating as defined at https://owasp.org/www-community/OWASP_Risk_Rating_Methodology - - - - - - - Stakeholder Specific Vulnerability Categorization as defined at https://github.com/CERTCC/SSVC - - - - - - - Another severity or risk scoring methodology - - - - - - - - - - The rationale of why the impact analysis state was asserted. - - - - - - - - - - - - - - - The vulnerability status of a given version or range of versions of a product. The statuses - 'affected' and 'unaffected' indicate that the version is affected or unaffected by the vulnerability. - The status 'unknown' indicates that it is unknown or unspecified whether the given version is affected. - There can be many reasons for an 'unknown' status, including that an investigation has not been - undertaken or that a vendor has not disclosed the status. - - - - - - - - - - - - - Describes how a component or service was manufactured or deployed. This is achieved through the use - of formulas, workflows, tasks, and steps, which declare the precise steps to reproduce along with the - observed formulas describing the steps which transpired in the manufacturing process. - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - Describes workflows and resources that captures rules and other aspects of how the associated - BOM component or service was formed. - - - - - - Transient components that are used in tasks that constitute one or more of - this formula's workflows - - - - - Transient services that are used in tasks that constitute one or more of - this formula's workflows - - - - - List of workflows that can be declared to accomplish specific orchestrated goals - and independently triggered. - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - - An optional identifier which can be used to reference the formula elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - The unique identifier for the resource instance within its deployment context. - - - - - - - The name of the resource instance. - - - - - - - The description of the resource instance. - - - - - - References to component or service resources that are used to realize - the resource instance. - - - - - The tasks that comprise the workflow. - - - - - The graph of dependencies between tasks within the workflow. - - - - - Indicates the types of activities performed by the set of workflow tasks. - - - - - - - - - - The trigger that initiated the task. - - - - - - The sequence of steps for the task. - - - - - - - - - - - Represents resources and data brought into a task at runtime by executor - or task commands - - - - - - - - - - Represents resources and data output from a task at runtime by executor - or task commands - - - - - - - - - - - The date and time (timestamp) when the task started. - - - - - - - The date and time (timestamp) when the task ended. - - - - - - A set of named filesystem or data resource shareable by workflow tasks. - - - - - A graph of the component runtime topology for workflow's instance. - A description of the runtime component and service topology. This can describe a partial or - complete topology used to host and execute the task (e.g., hardware, operating systems, - configurations, etc.) - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the workflow elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - References an object by its bom-ref attribute - - - - - - - - - - Reference to an externally accessible resource. - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - The unique identifier for the resource instance within its deployment context. - - - - - - - The name of the resource instance. - - - - - - - The description of the resource instance. - - - - - - - References to component or service resources that are used to realize the resource instance. - - - - - - - Indicates the types of activities performed by the set of workflow tasks. - - - - - - - - - - - - The trigger that initiated the task. - - - - - - - The sequence of steps for the task. - - - - - - - - - - - - Represents resources and data brought into a task at runtime by executor or task commands. - - - - - - - - - - - - Represents resources and data output from a task at runtime by executor or task commands - - - - - - - - - - - - The date and time (timestamp) when the task started. - - - - - - - The date and time (timestamp) when the task ended. - - - - - - - A set of named filesystem or data resource shareable by workflow tasks. - - - - - - - A graph of the component runtime topology for task's instance. - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the task elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - A task that copies software or data used to accomplish other tasks in the workflow. - - - - - A task that clones a software repository into the workflow in order to retrieve its source code or data for use in a build step. - - - - - A task that checks source code for programmatic and stylistic errors. - - - - - A task that performs a scan against source code, or built or deployed components and services. Scans are typically run to gather or test for security vulnerabilities or policy compliance. - - - - - A task that merges changes or fixes into source code prior to a build step in the workflow. - - - - - A task that builds the source code, dependencies and/or data into an artifact that can be deployed to and executed on target systems. - - - - - A task that verifies the functionality of a component or service. - - - - - A task that delivers a built artifact to one or more target repositories or storage systems. - - - - - A task that deploys a built artifact for execution on one or more target systems. - - - - - A task that releases a built, versioned artifact to a target repository or distribution system. - - - - - A task that cleans unnecessary tools, build artifacts and/or data from workflow storage. - - - - - A workflow task that does not match current task type definitions. - - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - A named filesystem or data resource shareable by workflow tasks. - - - - - - - The unique identifier for the resource instance within its deployment context. - - - - - - - The name of the resource instance. - - - - - - - The names for the workspace as referenced by other workflow tasks. Effectively, a name mapping - so other tasks can use their own local name in their steps. - - - - - - - - - - - - The description of the resource instance. - - - - - - - References to component or service resources that are used to realize the resource instance. - - - - - - - Describes the read-write access control for the workspace relative to the owning resource instance. - - - - - - - A path to a location on disk where the workspace will be available to the associated task's steps. - - - - - - - The name of a domain-specific data type the workspace represents. This property is for CI/CD - frameworks that are able to provide access to structured, managed data at a more granular level - than a filesystem. - - - - - - - Identifies the reference to the request for a specific volume type and parameters. - - - - - - - Information about the actual volume instance allocated to the workspace. - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the workflow elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - - - - - - - - An identifiable, logical unit of data storage tied to a physical device. - - - - - - - The unique identifier for the volume instance within its deployment context. - - - - - - - The name of the volume instance - - - - - - - The mode for the volume instance. - - - - - - - The underlying path created from the actual volume. - - - - - - - The allocated size of the volume accessible to the associated workspace. This should include - the scalar size as well as IEC standard unit in either decimal or binary form. - - - - - - - Indicates if the volume persists beyond the life of the resource it is associated with. - - - - - - - Indicates if the volume is remotely (i.e., network) attached. - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - - - - - - - - - - - Executes specific commands or tools in order to accomplish its owning task as part of a sequence. - - - - - - - A name for the step. - - - - - - - A description of the step. - - - - - - - Ordered list of commands or directives for the step - - - - - - - - - - - A text representation of the executed command. - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - The unique identifier for the resource instance within its deployment context. - - - - - - - The name of the resource instance. - - - - - - - The description of the resource instance. - - - - - - - References to component or service resources that are used to realize the resource instance. - - - - - - - The source type of event which caused the trigger to fire. - - - - - - - The event data that caused the associated trigger to activate. - - - - - - - - - - A condition that was used to determine a trigger should be activated. - - - - - - - - Describes the set of conditions which cause the trigger to activate. - - - - - - - The logical expression that was evaluated that determined the trigger should be fired. - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - - - - - - - The date and time (timestamp) when the trigger was activated. - - - - - - - Represents resources and data brought into a task at runtime by executor or task commands - - - - - - - - - - - - Represents resources and data output from a task at runtime by executor or task commands - - - - - - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the trigger elsewhere in the BOM. - Uniqueness is enforced within all elements and children of the root-level bom element. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - - - - - - - - - The unique identifier of the event. - - - - - - - A description of the event. - - - - - - - The date and time (timestamp) when the event was received. - - - - - - - Encoding of the raw event data. - - - - - - - References the component or service that was the source of the event - - - - - - - References the component or service that was the target of the event - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - Type that represents various input data types and formats. - - - - - - - - A reference to an independent resource provided as an input to a task by the workflow runtime. - - - - - - - Inputs that have the form of parameters with names and values. - - - - - - - Inputs that have the form of parameters with names and values. - - - - - - - - - - - - - - - - Inputs that have the form of data. - - - - - - - - A references to the component or service that provided the input to the task - (e.g., reference to a service with data flow value of inbound) - - - - - - - A reference to the component or service that received or stored the input if not the task - itself (e.g., a local, named storage workspace) - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - Represents resources and data output from a task at runtime by executor or task commands - - - - - - - - A reference to an independent resource generated as output by the task. - - - - - - - Outputs that have the form of environment variables. - - - - - - - - - - - - - - - - Outputs that have the form of data. - - - - - - - - Describes the type of data output. - - - - - - - Component or service that generated or provided the output from the task (e.g., a build tool) - - - - - - - Component or service that received the output from the task - (e.g., reference to an artifactory service with data flow value of outbound) - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - - - - - - - - - - - - - - - A representation of a functional parameter. - - - - - - - The name of the parameter. - - - - - - - The value of the parameter. - - - - - - - The data type of the parameter. - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - Cryptographic assets have properties that uniquely define them and that make them actionable for - further reasoning. As an example, it makes a difference if one knows the algorithm family (e.g. AES) - or the specific variant or instantiation (e.g. AES-128-GCM). This is because the security level and the - algorithm primitive (authenticated encryption) is only defined by the definition of the algorithm variant. - The presence of a weak cryptographic algorithm like SHA1 vs. HMAC-SHA1 also makes a difference. - - - - - - - Cryptographic assets occur in several forms. Algorithms and protocols are most commonly - implemented in specialized cryptographic libraries. They may however also be 'hardcoded' - in software components. Certificates and related cryptographic material like keys, tokens, - secrets or passwords are other cryptographic assets to be modelled. - - - - - - - - Mathematical function commonly used for data encryption, authentication, and - digital signatures. - - - - - - - An electronic document that is used to provide the identity or validate a public key. - - - - - - - A set of rules and guidelines that govern the behavior and communication with each other. - - - - - - - Other cryptographic assets that are related to algorithms, certificate, and protocols - such as keys and tokens. - - - - - - - - - - Additional properties specific to a cryptographic algorithm. - - - - - - - - Cryptographic building blocks used in higher-level cryptographic systems and - protocols. Primitives represent different cryptographic routines: deterministic - random bit generators (drbg, e.g. CTR_DRBG from NIST SP800-90A-r1), message - authentication codes (mac, e.g. HMAC-SHA-256), blockciphers (e.g. AES), - streamciphers (e.g. Salsa20), signatures (e.g. ECDSA), hash functions (e.g. SHA-256), - public-key encryption schemes (pke, e.g. RSA), extended output functions - (xof, e.g. SHAKE256), key derivation functions (e.g. pbkdf2), key agreement - algorithms (e.g. ECDH), key encapsulation mechanisms (e.g. ML-KEM), authenticated - encryption (ae, e.g. AES-GCM) and the combination of multiple algorithms - (combiner, e.g. SP800-56Cr2). - - - - - - - - Deterministic Random Bit Generator (DRBG) is a type of pseudorandom - number generator designed to produce a sequence of bits from an initial - seed value. DRBGs are commonly used in cryptographic applications where - reproducibility of random values is important. - - - - - - - In cryptography, a Message Authentication Code (MAC) is information - used for authenticating and integrity-checking a message. - - - - - - - A block cipher is a symmetric key algorithm that operates on fixed-size - blocks of data. It encrypts or decrypts the data in block units, - providing confidentiality. Block ciphers are widely used in various - cryptographic modes and protocols for secure data transmission. - - - - - - - A stream cipher is a symmetric key cipher where plaintext digits are - combined with a pseudorandom cipher digit stream (keystream). - - - - - - - In cryptography, a signature is a digital representation of a message - or data that proves its origin, identity, and integrity. Digital - signatures are generated using cryptographic algorithms and are widely - used for authentication and verification in secure communication. - - - - - - - A hash function is a mathematical algorithm that takes an input - (or 'message') and produces a fixed-size string of characters, which is - typically a hash value. Hash functions are commonly used in various - cryptographic applications, including data integrity verification and - password hashing. - - - - - - - Public Key Encryption (PKE) is a type of encryption that uses a pair of - public and private keys for secure communication. The public key is used - for encryption, while the private key is used for decryption. PKE is a - fundamental component of public-key cryptography. - - - - - - - An XOF is an extendable output function that can take arbitrary input - and creates a stream of output, up to a limit determined by the size of - the internal state of the hash function that underlies the XOF. - - - - - - - A Key Derivation Function (KDF) derives key material from another source - of entropy while preserving the entropy of the input. - - - - - - - In cryptography, a key-agreement is a protocol whereby two or more - parties agree on a cryptographic key in such a way that both influence - the outcome. - - - - - - - A Key Encapsulation Mechanism (KEM) algorithm is a mechanism for - transporting random keying material to a recipient using the recipient's - public key. - - - - - - - Authenticated Encryption (AE) is a cryptographic process that provides - both confidentiality and data integrity. It ensures that the encrypted - data has not been tampered with and comes from a legitimate source. - AE is commonly used in secure communication protocols. - - - - - - - A combiner aggregates many candidates for a cryptographic primitive and - generates a new candidate for the same primitive. - - - - - - - Another primitive type. - - - - - - - The primitive is not known. - - - - - - - - - - An identifier for the parameter set of the cryptographic algorithm. Examples: in - AES128, '128' identifies the key length in bits, in SHA256, '256' identifies the - digest length, '128' in SHAKE128 identifies its maximum security level in bits, and - 'SHA2-128s' identifies a parameter set used in SLH-DSA (FIPS205). - - - - - - - The specific underlying Elliptic Curve (EC) definition employed which is an indicator - of the level of security strength, performance and complexity. Absent an - authoritative source of curve names, CycloneDX recommends use of curve names as - defined at https://neuromancer.sk/std/, the source from which can be found at - https://github.com/J08nY/std-curves. - - - - - - - The target and execution environment in which the algorithm is implemented in. - - - - - - - - A software implementation running in plain unencrypted RAM. - - - - - - - A software implementation running in encrypted RAM. - - - - - - A software implementation running in a trusted execution environment. - - - - - - A hardware implementation. - - - - - - Another implementation environment. - - - - - - The execution environment is not known. - - - - - - - - - - The target platform for which the algorithm is implemented. The implementation can - be 'generic', running on any platform or for a specific platform. - - - - - - - - - - - - - - - - - - - - - - - - - The certification that the implementation of the cryptographic algorithm has - received, if any. Certifications include revisions and levels of FIPS 140 or - Common Criteria of different Extended Assurance Levels (CC-EAL). - - - - - - - - No certification obtained - - - - - - - FIPS 140-1 Level 1 - - - - - - - FIPS 140-1 Level 2 - - - - - - - FIPS 140-1 Level 3 - - - - - - - FIPS 140-1 Level 4 - - - - - - - FIPS 140-2 Level 1 - - - - - - - FIPS 140-2 Level 2 - - - - - - - FIPS 140-2 Level 3 - - - - - - - FIPS 140-2 Level 4 - - - - - - - FIPS 140-3 Level 1 - - - - - - - FIPS 140-3 Level 2 - - - - - - - FIPS 140-3 Level 3 - - - - - - - FIPS 140-3 Level 4 - - - - - - - Common Criteria - Evaluation Assurance Level 1 - - - - - - - Common Criteria - Evaluation Assurance Level 1 (Augmented) - - - - - - - Common Criteria - Evaluation Assurance Level 2 - - - - - - - Common Criteria - Evaluation Assurance Level 2 (Augmented) - - - - - - - Common Criteria - Evaluation Assurance Level 3 - - - - - - - Common Criteria - Evaluation Assurance Level 3 (Augmented) - - - - - - - Common Criteria - Evaluation Assurance Level 4 - - - - - - - Common Criteria - Evaluation Assurance Level 4 (Augmented) - - - - - - - Common Criteria - Evaluation Assurance Level 5 - - - - - - - Common Criteria - Evaluation Assurance Level 5 (Augmented) - - - - - - - Common Criteria - Evaluation Assurance Level 6 - - - - - - - Common Criteria - Evaluation Assurance Level 6 (Augmented) - - - - - - - Common Criteria - Evaluation Assurance Level 7 - - - - - - - Common Criteria - Evaluation Assurance Level 7 (Augmented) - - - - - - - Another certification - - - - - - - The certification level is not known - - - - - - - - - - The mode of operation in which the cryptographic algorithm (block cipher) is used. - - - - - - - - Cipher block chaining - - - - - - - Electronic codebook - - - - - - - Counter with cipher block chaining message authentication code - - - - - - - Galois/counter - - - - - - - Cipher feedback - - - - - - - Output feedback - - - - - - - Counter - - - - - - - Another mode of operation - - - - - - - The mode of operation is not known - - - - - - - - - - The padding scheme that is used for the cryptographic algorithm. - - - - - - - - Password-Based Cryptography Specification #5 - - - - - - - Public Key Cryptography Standard: Cryptographic Message Syntax - - - - - - - Public Key Cryptography Standard: RSA Cryptography v1.5 - - - - - - - Optimal asymmetric encryption padding - - - - - - - Raw - - - - - - - Another padding scheme - - - - - - - The padding scheme is not known - - - - - - - - - - The cryptographic functions implemented by the cryptographic algorithm. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The classical security level that a cryptographic algorithm provides (in bits). - - - - - - - - - - - - The NIST security strength category as defined in - https://csrc.nist.gov/projects/post-quantum-cryptography/post-quantum-cryptography-standardization/evaluation-criteria/security-(evaluation-criteria). - A value of 0 indicates that none of the categories are met. - - - - - - - - - - - - - - - - Properties for cryptographic assets of asset type 'certificate' - - - - - - - - The subject name for the certificate - - - - - - - The issuer name for the certificate - - - - - - - The date and time according to ISO-8601 standard from which the certificate is valid - - - - - - - The date and time according to ISO-8601 standard from which the certificate is not valid anymore - - - - - - - The bom-ref to signature algorithm used by the certificate - - - - - - - The bom-ref to the public key of the subject - - - - - - - The format of the certificate. Examples include X.509, PEM, DER, and CVC - - - - - - - The file extension of the certificate. Examples include crt, pem, cer, der, and p12. - - - - - - - - - - Properties for cryptographic assets of asset type 'relatedCryptoMaterial' - - - - - - - - The type for the related cryptographic material - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The optional unique identifier for the related cryptographic material. - - - - - - - The key state as defined by NIST SP 800-57. - - - - - - - - - - - - - - - - - The bom-ref to the algorithm used to generate the related cryptographic material. - - - - - - - The date and time (timestamp) when the related cryptographic material was created. - - - - - - - The date and time (timestamp) when the related cryptographic material was activated. - - - - - - - The date and time (timestamp) when the related cryptographic material was updated. - - - - - - - The date and time (timestamp) when the related cryptographic material expires. - - - - - - - The associated value of the cryptographic material. - - - - - - - The size of the cryptographic asset (in bits). - - - - - - - The format of the related cryptographic material (e.g. P8, PEM, DER). - - - - - - - The mechanism by which the cryptographic asset is secured by. - - - - - - - - Specifies the mechanism by which the cryptographic asset is secured by. - Examples include HSM, TPM, XGX, Software, and None. - - - - - - - The bom-ref to the algorithm. - - - - - - - - - - - - - Properties specific to cryptographic assets of type: 'protocol'. - - - - - - - - The concrete protocol type. - - - - - - - - Transport Layer Security - - - - - - - Secure Shell - - - - - - - Internet Protocol Security - - - - - - - Internet Key Exchange - - - - - - - Secure Socket Tunneling Protocol - - - - - - - Wi-Fi Protected Access - - - - - - - Another protocol type - - - - - - - The protocol type is not known - - - - - - - - - - The version of the protocol. Examples include 1.0, 1.2, and 1.99. - - - - - - - A list of cipher suites related to the protocol. - - - - - - - - - - - A common name for the cipher suite. For example: TLS_DHE_RSA_WITH_AES_128_CCM - - - - - - - A list of algorithms related to the cipher suite. - - - - - - - - The bom-ref to algorithm cryptographic asset. - - - - - - - - - - A list of common identifiers for the cipher suite. - - - - - - - - Cipher suite identifier. Examples include 0xC0 and 0x9E. - - - - - - - - - - - - - - - - The IKEv2 transform types supported (types 1-4), defined in RFC7296 section 3.3.2, - and additional properties. - - - - - - - - Transform Type 1: encryption algorithms - - - - - - - Transform Type 2: pseudorandom functions - - - - - - - Transform Type 3: integrity algorithms - - - - - - - Transform Type 4: Key Exchange Method (KE) per RFC9370, formerly called Diffie-Hellman Group (D-H) - - - - - - - Specifies if an Extended Sequence Number (ESN) is used. - - - - - - - IKEv2 Authentication method - - - - - - - - - - - - - The object identifier (OID) of the cryptographic asset. - - - - - - - - - - - - The list of assessors evaluating claims and determining conformance to requirements and confidence in that assessment. - - - - - - - - The assessor who evaluates claims and determines conformance to requirements and confidence in that assessment. - - - - - - - - The boolean indicating if the assessor is outside the organization generating claims. A value of false indicates a self assessor. - - - - - - - The entity issuing the assessment. - - - - - - - - An optional identifier which can be used to reference the object elsewhere in the BOM. - Every bom-ref MUST be unique within the BOM. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - The list of attestations asserted by an assessor that maps requirements to claims. - - - - - - - - An attestation asserted by an assessor that maps requirements to claims. - - - - - - - - The short description explaining the main points of the attestation. - - - - - - - The `bom-ref` to the assessor asserting the attestation. - - - - - - - The grouping of requirements to claims and the attestors declared conformance and confidence thereof. - - - - - - - - The `bom-ref` to the requirement being attested to. - - - - - - - The list of `bom-ref` to the claims being attested to. - - - - - - - - The `bom-ref` to the claim being attested to. - - - - - - - - - - The list of `bom-ref` to the counter claims being attested to. - - - - - - - - The `bom-ref` to the counter claim being attested to. - - - - - - - - - - The conformance of the claim meeting a requirement. - - - - - - - - The conformance of the claim between and inclusive of 0 and 1, where 1 is 100% conformance. - - - - - - - - - - - - - The rationale for the score of conformance. - - - - - - - The list of `bom-ref` to the evidence provided describing the - mitigation strategies. Each mitigation strategy should include an - explanation of how any weaknesses in the evidence will be mitigated. - - - - - - - - - - - - - - - The confidence of the claim meeting the requirement. - - - - - - - - The confidence of the claim between and inclusive of 0 and 1, where 1 is 100% confidence. - - - - - - - - - - - - - The rationale for the confidence score. - - - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - - - - The list of claims. - - - - - - - - - - - The `bom-ref` to a target representing a specific system, application, - API, module, team, person, process, business unit, company, etc... - that this claim is being applied to. - - - - - - - The specific statement or assertion about the target. - - - - - - - The list of `bom-ref` to the evidence provided describing the - mitigation strategies. Each mitigation strategy should include an - explanation of how any weaknesses in the evidence will be mitigated. - - - - - - - - - - - - The written explanation of why the evidence provided substantiates the claim. - - - - - - - The list of `bom-ref` to evidence that supports this claim. - - - - - - - The list of `bom-ref` to counterEvidence that supports this claim. - - - - - - Provides the ability to document external references related to the claim the BOM describes. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the object elsewhere - in the BOM. Every bom-ref MUST be unique within the BOM. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - The list of evidence - - - - - - - - The list of evidence - - - - - - - - The reference to the property name as defined in the [CycloneDX Property Taxonomy](https://github.com/CycloneDX/cyclonedx-property-taxonomy/). - - - - - - - The written description of what this evidence is and how it was created. - - - - - - - The output or analysis that supports claims. - - - - - - - - The name of the data. - - - - - - - The contents or references to the contents of the data being described. - - - - - - - An optional way to include textual or encoded data. - - - - - The URL to where the data can be retrieved. - - - - - - - - - Data classification tags data according to its type, sensitivity, and value if altered, stolen, or destroyed. - - - - - - - A description of any sensitive data. - - - - - - - - - - The date and time (timestamp) when the evidence was created. - - - - - The optional date and time (timestamp) when the evidence is no longer valid. - - - - - The author of the evidence. - - - - - The reviewer of the evidence. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the object elsewhere - in the BOM. Every bom-ref MUST be unique within the BOM. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - The list of targets which claims are made against. - - - - - - - - The list of organizations which claims are made against. - - - - - - - - - - - - The list of components which claims are made against. - - - - - - - - - - - - The list of services which claims are made against. - - - - - - - - - - - - - - - - - - The brief statement affirmed by an individual regarding all declarations. - This could be an affirmation of acceptance by a third-party auditor or receiving - individual of a file. For example: "I certify, to the best of my knowledge, that all information is correct." - - - - - - - The list of signatories authorized on behalf of an organization to assert validity of this document. - - - - - - - - - - - The signatory's name. - - - - - - - The signatory's role within an organization. - - - - - - - The signatory's organization. - - - - - - - An External reference provide a way to document systems, sites, and information that may be relevant, but are not included with the BOM. They may also establish specific relationships within or external to the BOM. - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - - - A collection of reusable objects that are defined and may be used elsewhere in the BOM. - - - - - - - - - - - The list of standards which may consist of regulations, industry or organizational-specific standards, maturity models, best practices, or any other requirements which can be evaluated against or attested to. - - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - A standard may consist of regulations, industry or organizational-specific standards, maturity models, best practices, or any other requirements which can be evaluated against or attested to. - - - - - - - The name of the standard. This will often be a shortened, single name of the standard. - - - - - - - The version of the standard. - - - - - - - The description of the standard. - - - - - - - The owner of the standard, often the entity responsible for its release. - - - - - - - The list of requirements comprising the standard. - - - - - - - - - - - The unique identifier used in the standard to identify a specific requirement. This should match what is in the standard and should not be the requirements bom-ref. - - - - - - - The title of the requirement. - - - - - - - The textual content of the requirement. - - - - - - - The supplemental text that provides additional guidance or context to the requirement, but is not directly part of the requirement. - - - - - - - - - - - - The Common Requirements Enumeration (CRE) identifier(s). CRE is a structured and standardized framework for uniting security standards and guidelines. CRE links each section of a resource to a shared topic identifier (a Common Requirement). Through this shared topic link, all resources map to each other. Use of CRE promotes clear and unambiguous communication among stakeholders. - - - - - - - - - - - - The optional `bom-ref` to a parent requirement. This establishes a hierarchy of requirements. Top-level requirements must not define a parent. Only child requirements should define parents. - - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - Provides the ability to document external references related to the BOM or - to the project the BOM describes. - - - - - - - An optional identifier which can be used to reference the object elsewhere - in the BOM. Every bom-ref MUST be unique within the BOM. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - - The list of levels associated with the standard. Some standards have different levels of compliance. - - - - - - - - - - - The identifier used in the standard to identify a specific level. - - - - - - - The title of the level. - - - - - - - The description of the level. - - - - - - - The list of requirement `bom-ref`s that comprise the level. - - - - - - - - - - - - - An optional identifier which can be used to reference the object elsewhere - in the BOM. Every bom-ref MUST be unique within the BOM. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - - Provides the ability to document external references related to the BOM or - to the project the BOM describes. - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - - An optional identifier which can be used to reference the object elsewhere - in the BOM. Every bom-ref MUST be unique within the BOM. - - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - Textual strings that aid in discovery, search, and retrieval of the associated - object. Tags often serve as a way to group or categorize similar or related objects by various - attributes. - - Examples include: - "json-parser", "object-persistence", "text-to-image", "translation", and "object-detection" - - - - - - - - - - - - Provides additional information about a BOM. - - - - - A list of software and hardware components. - - - - - A list of services. This may include microservices, function-as-a-service, and other types of network or intra-process services. - - - - - Provides the ability to document external references related to the BOM or - to the project the BOM describes. - - - - - Provides the ability to document dependency relationships. - - - - - Compositions describe constituent parts (including components, services, and dependency relationships) and their completeness. The completeness of vulnerabilities expressed in a BOM may also be described. - - - - - Provides the ability to document properties in a name/value store. - This provides flexibility to include data not officially supported in the standard - without having to use additional namespaces or create extensions. Property names - of interest to the general public are encouraged to be registered in the - CycloneDX Property Taxonomy - https://github.com/CycloneDX/cyclonedx-property-taxonomy. - Formal registration is OPTIONAL. - - - - - Vulnerabilities identified in components or services. - - - - - Comments made by people, organizations, or tools about any object with - a bom-ref, such as components, services, vulnerabilities, or the BOM itself. Unlike - inventory information, annotations may contain opinion or commentary from various - stakeholders. Annotations may be inline (with inventory) or externalized via BOM-Link, - and may optionally be signed. - - - - - Describes how a component or service was manufactured or deployed. This is - achieved through the use of formulas, workflows, tasks, and steps, which declare the precise - steps to reproduce along with the observed formulas describing the steps which transpired - in the manufacturing process. - - - - - - The list of declarations which describe the conformance to standards. Each declaration may - include attestations, claims, and evidence. - - - - - - - A collection of reusable objects that are defined and may be used elsewhere in the BOM. - - - - - - - Allows any undeclared elements as long as the elements are placed in a different namespace. - - - - - - - Whenever an existing BOM is modified, either manually or through automated - processes, the version of the BOM SHOULD be incremented by 1. When a system is presented with - multiple BOMs with identical serial numbers, the system SHOULD use the most recent version of the BOM. - The default version is '1'. - - - - - Every BOM generated SHOULD have a unique serial number, even if the contents of - the BOM have not changed over time. If specified, the serial number MUST conform to RFC-4122. - Use of serial numbers are RECOMMENDED. - - - - - User-defined attributes may be used on this element as long as they - do not have the same name as an existing attribute used by the schema. - - - - - - - - - diff --git a/schema/cyclonedx/spdx.xsd b/schema/cyclonedx/spdx.xsd deleted file mode 100644 index c2126601965..00000000000 --- a/schema/cyclonedx/spdx.xsd +++ /dev/null @@ -1,3079 +0,0 @@ - - - - - - - - - BSD Zero Clause License - - - - - Attribution Assurance License - - - - - Abstyles License - - - - - AdaCore Doc License - - - - - Adobe Systems Incorporated Source Code License Agreement - - - - - Adobe Glyph List License - - - - - Amazon Digital Services License - - - - - Academic Free License v1.1 - - - - - Academic Free License v1.2 - - - - - Academic Free License v2.0 - - - - - Academic Free License v2.1 - - - - - Academic Free License v3.0 - - - - - Afmparse License - - - - - Affero General Public License v1.0 - - - - - Affero General Public License v1.0 only - - - - - Affero General Public License v1.0 or later - - - - - GNU Affero General Public License v3.0 - - - - - GNU Affero General Public License v3.0 only - - - - - GNU Affero General Public License v3.0 or later - - - - - Aladdin Free Public License - - - - - AMD's plpa_map.c License - - - - - Apple MIT License - - - - - Academy of Motion Picture Arts and Sciences BSD - - - - - ANTLR Software Rights Notice - - - - - ANTLR Software Rights Notice with license fallback - - - - - Apache License 1.0 - - - - - Apache License 1.1 - - - - - Apache License 2.0 - - - - - Adobe Postscript AFM License - - - - - Adaptive Public License 1.0 - - - - - App::s2p License - - - - - Apple Public Source License 1.0 - - - - - Apple Public Source License 1.1 - - - - - Apple Public Source License 1.2 - - - - - Apple Public Source License 2.0 - - - - - Arphic Public License - - - - - Artistic License 1.0 - - - - - Artistic License 1.0 w/clause 8 - - - - - Artistic License 1.0 (Perl) - - - - - Artistic License 2.0 - - - - - ASWF Digital Assets License version 1.0 - - - - - ASWF Digital Assets License 1.1 - - - - - Baekmuk License - - - - - Bahyph License - - - - - Barr License - - - - - Beerware License - - - - - Bitstream Charter Font License - - - - - Bitstream Vera Font License - - - - - BitTorrent Open Source License v1.0 - - - - - BitTorrent Open Source License v1.1 - - - - - SQLite Blessing - - - - - Blue Oak Model License 1.0.0 - - - - - Boehm-Demers-Weiser GC License - - - - - Borceux license - - - - - Brian Gladman 3-Clause License - - - - - BSD 1-Clause License - - - - - BSD 2-Clause "Simplified" License - - - - - BSD 2-Clause FreeBSD License - - - - - BSD 2-Clause NetBSD License - - - - - BSD-2-Clause Plus Patent License - - - - - BSD 2-Clause with views sentence - - - - - BSD 3-Clause "New" or "Revised" License - - - - - BSD with attribution - - - - - BSD 3-Clause Clear License - - - - - Lawrence Berkeley National Labs BSD variant license - - - - - BSD 3-Clause Modification - - - - - BSD 3-Clause No Military License - - - - - BSD 3-Clause No Nuclear License - - - - - BSD 3-Clause No Nuclear License 2014 - - - - - BSD 3-Clause No Nuclear Warranty - - - - - BSD 3-Clause Open MPI variant - - - - - BSD 4-Clause "Original" or "Old" License - - - - - BSD 4 Clause Shortened - - - - - BSD-4-Clause (University of California-Specific) - - - - - BSD 4.3 RENO License - - - - - BSD 4.3 TAHOE License - - - - - BSD Advertising Acknowledgement License - - - - - BSD with Attribution and HPND disclaimer - - - - - BSD Protection License - - - - - BSD Source Code Attribution - - - - - Boost Software License 1.0 - - - - - Business Source License 1.1 - - - - - bzip2 and libbzip2 License v1.0.5 - - - - - bzip2 and libbzip2 License v1.0.6 - - - - - Computational Use of Data Agreement v1.0 - - - - - Cryptographic Autonomy License 1.0 - - - - - Cryptographic Autonomy License 1.0 (Combined Work Exception) - - - - - Caldera License - - - - - Computer Associates Trusted Open Source License 1.1 - - - - - Creative Commons Attribution 1.0 Generic - - - - - Creative Commons Attribution 2.0 Generic - - - - - Creative Commons Attribution 2.5 Generic - - - - - Creative Commons Attribution 2.5 Australia - - - - - Creative Commons Attribution 3.0 Unported - - - - - Creative Commons Attribution 3.0 Austria - - - - - Creative Commons Attribution 3.0 Germany - - - - - Creative Commons Attribution 3.0 IGO - - - - - Creative Commons Attribution 3.0 Netherlands - - - - - Creative Commons Attribution 3.0 United States - - - - - Creative Commons Attribution 4.0 International - - - - - Creative Commons Attribution Non Commercial 1.0 Generic - - - - - Creative Commons Attribution Non Commercial 2.0 Generic - - - - - Creative Commons Attribution Non Commercial 2.5 Generic - - - - - Creative Commons Attribution Non Commercial 3.0 Unported - - - - - Creative Commons Attribution Non Commercial 3.0 Germany - - - - - Creative Commons Attribution Non Commercial 4.0 International - - - - - Creative Commons Attribution Non Commercial No Derivatives 1.0 Generic - - - - - Creative Commons Attribution Non Commercial No Derivatives 2.0 Generic - - - - - Creative Commons Attribution Non Commercial No Derivatives 2.5 Generic - - - - - Creative Commons Attribution Non Commercial No Derivatives 3.0 Unported - - - - - Creative Commons Attribution Non Commercial No Derivatives 3.0 Germany - - - - - Creative Commons Attribution Non Commercial No Derivatives 3.0 IGO - - - - - Creative Commons Attribution Non Commercial No Derivatives 4.0 International - - - - - Creative Commons Attribution Non Commercial Share Alike 1.0 Generic - - - - - Creative Commons Attribution Non Commercial Share Alike 2.0 Generic - - - - - Creative Commons Attribution Non Commercial Share Alike 2.0 Germany - - - - - Creative Commons Attribution-NonCommercial-ShareAlike 2.0 France - - - - - Creative Commons Attribution Non Commercial Share Alike 2.0 England and Wales - - - - - Creative Commons Attribution Non Commercial Share Alike 2.5 Generic - - - - - Creative Commons Attribution Non Commercial Share Alike 3.0 Unported - - - - - Creative Commons Attribution Non Commercial Share Alike 3.0 Germany - - - - - Creative Commons Attribution Non Commercial Share Alike 3.0 IGO - - - - - Creative Commons Attribution Non Commercial Share Alike 4.0 International - - - - - Creative Commons Attribution No Derivatives 1.0 Generic - - - - - Creative Commons Attribution No Derivatives 2.0 Generic - - - - - Creative Commons Attribution No Derivatives 2.5 Generic - - - - - Creative Commons Attribution No Derivatives 3.0 Unported - - - - - Creative Commons Attribution No Derivatives 3.0 Germany - - - - - Creative Commons Attribution No Derivatives 4.0 International - - - - - Creative Commons Attribution Share Alike 1.0 Generic - - - - - Creative Commons Attribution Share Alike 2.0 Generic - - - - - Creative Commons Attribution Share Alike 2.0 England and Wales - - - - - Creative Commons Attribution Share Alike 2.1 Japan - - - - - Creative Commons Attribution Share Alike 2.5 Generic - - - - - Creative Commons Attribution Share Alike 3.0 Unported - - - - - Creative Commons Attribution Share Alike 3.0 Austria - - - - - Creative Commons Attribution Share Alike 3.0 Germany - - - - - Creative Commons Attribution-ShareAlike 3.0 IGO - - - - - Creative Commons Attribution Share Alike 4.0 International - - - - - Creative Commons Public Domain Dedication and Certification - - - - - Creative Commons Zero v1.0 Universal - - - - - Common Development and Distribution License 1.0 - - - - - Common Development and Distribution License 1.1 - - - - - Common Documentation License 1.0 - - - - - Community Data License Agreement Permissive 1.0 - - - - - Community Data License Agreement Permissive 2.0 - - - - - Community Data License Agreement Sharing 1.0 - - - - - CeCILL Free Software License Agreement v1.0 - - - - - CeCILL Free Software License Agreement v1.1 - - - - - CeCILL Free Software License Agreement v2.0 - - - - - CeCILL Free Software License Agreement v2.1 - - - - - CeCILL-B Free Software License Agreement - - - - - CeCILL-C Free Software License Agreement - - - - - CERN Open Hardware Licence v1.1 - - - - - CERN Open Hardware Licence v1.2 - - - - - CERN Open Hardware Licence Version 2 - Permissive - - - - - CERN Open Hardware Licence Version 2 - Strongly Reciprocal - - - - - CERN Open Hardware Licence Version 2 - Weakly Reciprocal - - - - - CFITSIO License - - - - - Checkmk License - - - - - Clarified Artistic License - - - - - Clips License - - - - - CMU Mach License - - - - - CNRI Jython License - - - - - CNRI Python License - - - - - CNRI Python Open Source GPL Compatible License Agreement - - - - - Copyfree Open Innovation License - - - - - Community Specification License 1.0 - - - - - Condor Public License v1.1 - - - - - copyleft-next 0.3.0 - - - - - copyleft-next 0.3.1 - - - - - Cornell Lossless JPEG License - - - - - Common Public Attribution License 1.0 - - - - - Common Public License 1.0 - - - - - Code Project Open License 1.02 - - - - - Crossword License - - - - - CrystalStacker License - - - - - CUA Office Public License v1.0 - - - - - Cube License - - - - - curl License - - - - - Deutsche Freie Software Lizenz - - - - - diffmark license - - - - - Data licence Germany – attribution – version 2.0 - - - - - DOC License - - - - - Dotseqn License - - - - - Detection Rule License 1.0 - - - - - DSDP License - - - - - David M. Gay dtoa License - - - - - dvipdfm License - - - - - Educational Community License v1.0 - - - - - Educational Community License v2.0 - - - - - eCos license version 2.0 - - - - - Eiffel Forum License v1.0 - - - - - Eiffel Forum License v2.0 - - - - - eGenix.com Public License 1.1.0 - - - - - Elastic License 2.0 - - - - - Entessa Public License v1.0 - - - - - EPICS Open License - - - - - Eclipse Public License 1.0 - - - - - Eclipse Public License 2.0 - - - - - Erlang Public License v1.1 - - - - - Etalab Open License 2.0 - - - - - EU DataGrid Software License - - - - - European Union Public License 1.0 - - - - - European Union Public License 1.1 - - - - - European Union Public License 1.2 - - - - - Eurosym License - - - - - Fair License - - - - - Fraunhofer FDK AAC Codec Library - - - - - Frameworx Open License 1.0 - - - - - FreeBSD Documentation License - - - - - FreeImage Public License v1.0 - - - - - FSF All Permissive License - - - - - FSF Unlimited License - - - - - FSF Unlimited License (with License Retention) - - - - - FSF Unlimited License (With License Retention and Warranty Disclaimer) - - - - - Freetype Project License - - - - - GD License - - - - - GNU Free Documentation License v1.1 - - - - - GNU Free Documentation License v1.1 only - invariants - - - - - GNU Free Documentation License v1.1 or later - invariants - - - - - GNU Free Documentation License v1.1 only - no invariants - - - - - GNU Free Documentation License v1.1 or later - no invariants - - - - - GNU Free Documentation License v1.1 only - - - - - GNU Free Documentation License v1.1 or later - - - - - GNU Free Documentation License v1.2 - - - - - GNU Free Documentation License v1.2 only - invariants - - - - - GNU Free Documentation License v1.2 or later - invariants - - - - - GNU Free Documentation License v1.2 only - no invariants - - - - - GNU Free Documentation License v1.2 or later - no invariants - - - - - GNU Free Documentation License v1.2 only - - - - - GNU Free Documentation License v1.2 or later - - - - - GNU Free Documentation License v1.3 - - - - - GNU Free Documentation License v1.3 only - invariants - - - - - GNU Free Documentation License v1.3 or later - invariants - - - - - GNU Free Documentation License v1.3 only - no invariants - - - - - GNU Free Documentation License v1.3 or later - no invariants - - - - - GNU Free Documentation License v1.3 only - - - - - GNU Free Documentation License v1.3 or later - - - - - Giftware License - - - - - GL2PS License - - - - - 3dfx Glide License - - - - - Glulxe License - - - - - Good Luck With That Public License - - - - - gnuplot License - - - - - GNU General Public License v1.0 only - - - - - GNU General Public License v1.0 or later - - - - - GNU General Public License v1.0 only - - - - - GNU General Public License v1.0 or later - - - - - GNU General Public License v2.0 only - - - - - GNU General Public License v2.0 or later - - - - - GNU General Public License v2.0 only - - - - - GNU General Public License v2.0 or later - - - - - GNU General Public License v2.0 w/Autoconf exception - - - - - GNU General Public License v2.0 w/Bison exception - - - - - GNU General Public License v2.0 w/Classpath exception - - - - - GNU General Public License v2.0 w/Font exception - - - - - GNU General Public License v2.0 w/GCC Runtime Library exception - - - - - GNU General Public License v3.0 only - - - - - GNU General Public License v3.0 or later - - - - - GNU General Public License v3.0 only - - - - - GNU General Public License v3.0 or later - - - - - GNU General Public License v3.0 w/Autoconf exception - - - - - GNU General Public License v3.0 w/GCC Runtime Library exception - - - - - Graphics Gems License - - - - - gSOAP Public License v1.3b - - - - - Haskell Language Report License - - - - - Hippocratic License 2.1 - - - - - Hewlett-Packard 1986 License - - - - - Historical Permission Notice and Disclaimer - - - - - HPND with US Government export control warning - - - - - Historical Permission Notice and Disclaimer - Markus Kuhn variant - - - - - Historical Permission Notice and Disclaimer - sell variant - - - - - HPND sell variant with MIT disclaimer - - - - - HTML Tidy License - - - - - IBM PowerPC Initialization and Boot Software - - - - - ICU License - - - - - IEC Code Components End-user licence agreement - - - - - Independent JPEG Group License - - - - - Independent JPEG Group License - short - - - - - ImageMagick License - - - - - iMatix Standard Function Library Agreement - - - - - Imlib2 License - - - - - Info-ZIP License - - - - - Inner Net License v2.0 - - - - - Intel Open Source License - - - - - Intel ACPI Software License Agreement - - - - - Interbase Public License v1.0 - - - - - IPA Font License - - - - - IBM Public License v1.0 - - - - - ISC License - - - - - Jam License - - - - - JasPer License - - - - - JPL Image Use Policy - - - - - Japan Network Information Center License - - - - - JSON License - - - - - Kazlib License - - - - - Knuth CTAN License - - - - - Licence Art Libre 1.2 - - - - - Licence Art Libre 1.3 - - - - - Latex2e License - - - - - Latex2e with translated notice permission - - - - - Leptonica License - - - - - GNU Library General Public License v2 only - - - - - GNU Library General Public License v2 or later - - - - - GNU Library General Public License v2 only - - - - - GNU Library General Public License v2 or later - - - - - GNU Lesser General Public License v2.1 only - - - - - GNU Lesser General Public License v2.1 or later - - - - - GNU Lesser General Public License v2.1 only - - - - - GNU Lesser General Public License v2.1 or later - - - - - GNU Lesser General Public License v3.0 only - - - - - GNU Lesser General Public License v3.0 or later - - - - - GNU Lesser General Public License v3.0 only - - - - - GNU Lesser General Public License v3.0 or later - - - - - Lesser General Public License For Linguistic Resources - - - - - libpng License - - - - - PNG Reference Library version 2 - - - - - libselinux public domain notice - - - - - libtiff License - - - - - libutil David Nugent License - - - - - Licence Libre du Québec – Permissive version 1.1 - - - - - Licence Libre du Québec – Réciprocité version 1.1 - - - - - Licence Libre du Québec – Réciprocité forte version 1.1 - - - - - Linux man-pages - 1 paragraph - - - - - Linux man-pages Copyleft - - - - - Linux man-pages Copyleft - 2 paragraphs - - - - - Linux man-pages Copyleft Variant - - - - - Linux Kernel Variant of OpenIB.org license - - - - - Common Lisp LOOP License - - - - - Lucent Public License Version 1.0 - - - - - Lucent Public License v1.02 - - - - - LaTeX Project Public License v1.0 - - - - - LaTeX Project Public License v1.1 - - - - - LaTeX Project Public License v1.2 - - - - - LaTeX Project Public License v1.3a - - - - - LaTeX Project Public License v1.3c - - - - - LZMA SDK License (versions 9.11 to 9.20) - - - - - LZMA SDK License (versions 9.22 and beyond) - - - - - MakeIndex License - - - - - Martin Birgmeier License - - - - - metamail License - - - - - Minpack License - - - - - The MirOS Licence - - - - - MIT License - - - - - MIT No Attribution - - - - - Enlightenment License (e16) - - - - - CMU License - - - - - enna License - - - - - feh License - - - - - MIT Festival Variant - - - - - MIT License Modern Variant - - - - - MIT Open Group variant - - - - - MIT Tom Wu Variant - - - - - MIT +no-false-attribs license - - - - - Motosoto License - - - - - mpi Permissive License - - - - - mpich2 License - - - - - Mozilla Public License 1.0 - - - - - Mozilla Public License 1.1 - - - - - Mozilla Public License 2.0 - - - - - Mozilla Public License 2.0 (no copyleft exception) - - - - - mplus Font License - - - - - Microsoft Limited Public License - - - - - Microsoft Public License - - - - - Microsoft Reciprocal License - - - - - Matrix Template Library License - - - - - Mulan Permissive Software License, Version 1 - - - - - Mulan Permissive Software License, Version 2 - - - - - Multics License - - - - - Mup License - - - - - Nara Institute of Science and Technology License (2003) - - - - - NASA Open Source Agreement 1.3 - - - - - Naumen Public License - - - - - Net Boolean Public License v1 - - - - - Non-Commercial Government Licence - - - - - University of Illinois/NCSA Open Source License - - - - - Net-SNMP License - - - - - NetCDF license - - - - - Newsletr License - - - - - Nethack General Public License - - - - - NICTA Public Software License, Version 1.0 - - - - - NIST Public Domain Notice - - - - - NIST Public Domain Notice with license fallback - - - - - NIST Software License - - - - - Norwegian Licence for Open Government Data (NLOD) 1.0 - - - - - Norwegian Licence for Open Government Data (NLOD) 2.0 - - - - - No Limit Public License - - - - - Nokia Open Source License - - - - - Netizen Open Source License - - - - - Noweb License - - - - - Netscape Public License v1.0 - - - - - Netscape Public License v1.1 - - - - - Non-Profit Open Software License 3.0 - - - - - NRL License - - - - - NTP License - - - - - NTP No Attribution - - - - - Nunit License - - - - - Open Use of Data Agreement v1.0 - - - - - Open CASCADE Technology Public License - - - - - OCLC Research Public License 2.0 - - - - - Open Data Commons Open Database License v1.0 - - - - - Open Data Commons Attribution License v1.0 - - - - - OFFIS License - - - - - SIL Open Font License 1.0 - - - - - SIL Open Font License 1.0 with no Reserved Font Name - - - - - SIL Open Font License 1.0 with Reserved Font Name - - - - - SIL Open Font License 1.1 - - - - - SIL Open Font License 1.1 with no Reserved Font Name - - - - - SIL Open Font License 1.1 with Reserved Font Name - - - - - OGC Software License, Version 1.0 - - - - - Taiwan Open Government Data License, version 1.0 - - - - - Open Government Licence - Canada - - - - - Open Government Licence v1.0 - - - - - Open Government Licence v2.0 - - - - - Open Government Licence v3.0 - - - - - Open Group Test Suite License - - - - - Open LDAP Public License v1.1 - - - - - Open LDAP Public License v1.2 - - - - - Open LDAP Public License v1.3 - - - - - Open LDAP Public License v1.4 - - - - - Open LDAP Public License v2.0 (or possibly 2.0A and 2.0B) - - - - - Open LDAP Public License v2.0.1 - - - - - Open LDAP Public License v2.1 - - - - - Open LDAP Public License v2.2 - - - - - Open LDAP Public License v2.2.1 - - - - - Open LDAP Public License 2.2.2 - - - - - Open LDAP Public License v2.3 - - - - - Open LDAP Public License v2.4 - - - - - Open LDAP Public License v2.5 - - - - - Open LDAP Public License v2.6 - - - - - Open LDAP Public License v2.7 - - - - - Open LDAP Public License v2.8 - - - - - Open Logistics Foundation License Version 1.3 - - - - - Open Market License - - - - - OpenPBS v2.3 Software License - - - - - OpenSSL License - - - - - Open Public License v1.0 - - - - - United Kingdom Open Parliament Licence v3.0 - - - - - Open Publication License v1.0 - - - - - OSET Public License version 2.1 - - - - - Open Software License 1.0 - - - - - Open Software License 1.1 - - - - - Open Software License 2.0 - - - - - Open Software License 2.1 - - - - - Open Software License 3.0 - - - - - The Parity Public License 6.0.0 - - - - - The Parity Public License 7.0.0 - - - - - Open Data Commons Public Domain Dedication & License 1.0 - - - - - PHP License v3.0 - - - - - PHP License v3.01 - - - - - Plexus Classworlds License - - - - - PolyForm Noncommercial License 1.0.0 - - - - - PolyForm Small Business License 1.0.0 - - - - - PostgreSQL License - - - - - Python Software Foundation License 2.0 - - - - - psfrag License - - - - - psutils License - - - - - Python License 2.0 - - - - - Python License 2.0.1 - - - - - Qhull License - - - - - Q Public License 1.0 - - - - - Q Public License 1.0 - INRIA 2004 variant - - - - - Rdisc License - - - - - Red Hat eCos Public License v1.1 - - - - - Reciprocal Public License 1.1 - - - - - Reciprocal Public License 1.5 - - - - - RealNetworks Public Source License v1.0 - - - - - RSA Message-Digest License - - - - - Ricoh Source Code Public License - - - - - Ruby License - - - - - Sax Public Domain Notice - - - - - Saxpath License - - - - - SCEA Shared Source License - - - - - Scheme Language Report License - - - - - Sendmail License - - - - - Sendmail License 8.23 - - - - - SGI Free Software License B v1.0 - - - - - SGI Free Software License B v1.1 - - - - - SGI Free Software License B v2.0 - - - - - SGP4 Permission Notice - - - - - Solderpad Hardware License v0.5 - - - - - Solderpad Hardware License, Version 0.51 - - - - - Simple Public License 2.0 - - - - - Sun Industry Standards Source License v1.1 - - - - - Sun Industry Standards Source License v1.2 - - - - - Sleepycat License - - - - - Standard ML of New Jersey License - - - - - Secure Messaging Protocol Public License - - - - - SNIA Public License 1.1 - - - - - snprintf License - - - - - Spencer License 86 - - - - - Spencer License 94 - - - - - Spencer License 99 - - - - - Sun Public License v1.0 - - - - - SSH OpenSSH license - - - - - SSH short notice - - - - - Server Side Public License, v 1 - - - - - Standard ML of New Jersey License - - - - - SugarCRM Public License v1.1.3 - - - - - SunPro License - - - - - Scheme Widget Library (SWL) Software License Agreement - - - - - Symlinks License - - - - - TAPR Open Hardware License v1.0 - - - - - TCL/TK License - - - - - TCP Wrappers License - - - - - TermReadKey License - - - - - TMate Open Source License - - - - - TORQUE v2.5+ Software License v1.1 - - - - - Trusster Open Source License - - - - - Time::ParseDate License - - - - - THOR Public License 1.0 - - - - - Text-Tabs+Wrap License - - - - - Technische Universitaet Berlin License 1.0 - - - - - Technische Universitaet Berlin License 2.0 - - - - - UCAR License - - - - - Upstream Compatibility License v1.0 - - - - - Unicode License Agreement - Data Files and Software (2015) - - - - - Unicode License Agreement - Data Files and Software (2016) - - - - - Unicode Terms of Use - - - - - UnixCrypt License - - - - - The Unlicense - - - - - Universal Permissive License v1.0 - - - - - Vim License - - - - - VOSTROM Public License for Open Source - - - - - Vovida Software License v1.0 - - - - - W3C Software Notice and License (2002-12-31) - - - - - W3C Software Notice and License (1998-07-20) - - - - - W3C Software Notice and Document License (2015-05-13) - - - - - w3m License - - - - - Sybase Open Watcom Public License 1.0 - - - - - Widget Workshop License - - - - - Wsuipa License - - - - - Do What The F*ck You Want To Public License - - - - - wxWindows Library License - - - - - X11 License - - - - - X11 License Distribution Modification Variant - - - - - Xdebug License v 1.03 - - - - - Xerox License - - - - - Xfig License - - - - - XFree86 License 1.1 - - - - - xinetd License - - - - - xlock License - - - - - X.Net License - - - - - XPP License - - - - - XSkat License - - - - - Yahoo! Public License v1.0 - - - - - Yahoo! Public License v1.1 - - - - - Zed License - - - - - Zend License v2.0 - - - - - Zimbra Public License v1.3 - - - - - Zimbra Public License v1.4 - - - - - zlib License - - - - - zlib/libpng License with Acknowledgement - - - - - Zope Public License 1.1 - - - - - Zope Public License 2.0 - - - - - Zope Public License 2.1 - - - - - - 389 Directory Server Exception - - - - - Asterisk exception - - - - - Autoconf exception 2.0 - - - - - Autoconf exception 3.0 - - - - - Autoconf generic exception - - - - - Autoconf macro exception - - - - - Bison exception 2.2 - - - - - Bootloader Distribution Exception - - - - - Classpath exception 2.0 - - - - - CLISP exception 2.0 - - - - - cryptsetup OpenSSL exception - - - - - DigiRule FOSS License Exception - - - - - eCos exception 2.0 - - - - - Fawkes Runtime Exception - - - - - FLTK exception - - - - - Font exception 2.0 - - - - - FreeRTOS Exception 2.0 - - - - - GCC Runtime Library exception 2.0 - - - - - GCC Runtime Library exception 3.1 - - - - - GNAT exception - - - - - GNU JavaMail exception - - - - - GPL-3.0 Interface Exception - - - - - GPL-3.0 Linking Exception - - - - - GPL-3.0 Linking Exception (with Corresponding Source) - - - - - GPL Cooperation Commitment 1.0 - - - - - GStreamer Exception (2005) - - - - - GStreamer Exception (2008) - - - - - i2p GPL+Java Exception - - - - - KiCad Libraries Exception - - - - - LGPL-3.0 Linking Exception - - - - - libpri OpenH323 exception - - - - - Libtool Exception - - - - - Linux Syscall Note - - - - - LLGPL Preamble - - - - - LLVM Exception - - - - - LZMA exception - - - - - Macros and Inline Functions Exception - - - - - Nokia Qt LGPL exception 1.1 - - - - - OCaml LGPL Linking Exception - - - - - Open CASCADE Exception 1.0 - - - - - OpenJDK Assembly exception 1.0 - - - - - OpenVPN OpenSSL Exception - - - - - PS/PDF font exception (2017-08-17) - - - - - INRIA QPL 1.0 2004 variant exception - - - - - Qt GPL exception 1.0 - - - - - Qt LGPL exception 1.1 - - - - - Qwt exception 1.0 - - - - - Solderpad Hardware License v2.0 - - - - - Solderpad Hardware License v2.1 - - - - - SWI exception - - - - - Swift Exception - - - - - U-Boot exception 2.0 - - - - - Universal FOSS Exception, Version 1.0 - - - - - vsftpd OpenSSL exception - - - - - WxWindows Library Exception 3.1 - - - - - x11vnc OpenSSL Exception - - - - - - From 337e4405e0ef72e53625cadb5baa893be8f094dc Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 12:46:47 -0400 Subject: [PATCH 106/109] chore(deps): bump gorm.io/gorm from 1.26.1 to 1.30.0 (#2687) Bumps [gorm.io/gorm](https://github.com/go-gorm/gorm) from 1.26.1 to 1.30.0. - [Release notes](https://github.com/go-gorm/gorm/releases) - [Commits](https://github.com/go-gorm/gorm/compare/v1.26.1...v1.30.0) --- updated-dependencies: - dependency-name: gorm.io/gorm dependency-version: 1.30.0 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 fc0230ed17d..987fc593b9b 100644 --- a/go.mod +++ b/go.mod @@ -69,7 +69,7 @@ require ( golang.org/x/time v0.11.0 golang.org/x/tools v0.33.0 gopkg.in/yaml.v3 v3.0.1 - gorm.io/gorm v1.26.1 + gorm.io/gorm v1.30.0 ) require ( diff --git a/go.sum b/go.sum index 796b2571be6..db26e1429fd 100644 --- a/go.sum +++ b/go.sum @@ -2402,8 +2402,8 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/gorm v1.26.1 h1:ghB2gUI9FkS46luZtn6DLZ0f6ooBJ5IbVej2ENFDjRw= -gorm.io/gorm v1.26.1/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= +gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= +gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= 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= From 112a383a49e960725719ed984bc65c6da540053d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 28 May 2025 12:49:59 -0400 Subject: [PATCH 107/109] chore(deps): bump github.com/google/go-containerregistry (#2681) Bumps [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) from 0.20.4 to 0.20.5. - [Release notes](https://github.com/google/go-containerregistry/releases) - [Changelog](https://github.com/google/go-containerregistry/blob/main/.goreleaser.yml) - [Commits](https://github.com/google/go-containerregistry/compare/v0.20.4...v0.20.5) --- updated-dependencies: - dependency-name: github.com/google/go-containerregistry dependency-version: 0.20.5 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 987fc593b9b..eb8461a985e 100644 --- a/go.mod +++ b/go.mod @@ -36,7 +36,7 @@ require ( github.com/go-viper/mapstructure/v2 v2.2.1 github.com/gohugoio/hashstructure v0.5.0 github.com/google/go-cmp v0.7.0 - github.com/google/go-containerregistry v0.20.4 + github.com/google/go-containerregistry v0.20.5 github.com/google/uuid v1.6.0 github.com/gookit/color v1.5.4 github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b diff --git a/go.sum b/go.sum index db26e1429fd..2436125b128 100644 --- a/go.sum +++ b/go.sum @@ -1070,8 +1070,8 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/go-containerregistry v0.20.4 h1:w/Fdj3ef046SdV/GJU69cCnreaLpqbTo1X9XPyHbkd4= -github.com/google/go-containerregistry v0.20.4/go.mod h1:Q14vdOOzug02bwnhMkZKD4e30pDaD9W65qzXpyzF49E= +github.com/google/go-containerregistry v0.20.5 h1:4RnlYcDs5hoA++CeFjlbZ/U9Yp1EuWr+UhhTyYQjOP0= +github.com/google/go-containerregistry v0.20.5/go.mod h1:Q14vdOOzug02bwnhMkZKD4e30pDaD9W65qzXpyzF49E= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs= github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY= From ecd8ed7122075b5f9a9f1e4bd5f11cebf4eb66d3 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, 28 May 2025 12:50:15 -0400 Subject: [PATCH 108/109] chore(deps): update tools to latest versions (#2679) Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: westonsteimel <1593939+westonsteimel@users.noreply.github.com> --- .binny.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.binny.yaml b/.binny.yaml index f62b8c74dc2..f194c0b9699 100644 --- a/.binny.yaml +++ b/.binny.yaml @@ -98,7 +98,7 @@ tools: # used for integration tests - name: skopeo version: - want: v1.18.0 + want: v1.19.0 method: go-install with: module: github.com/containers/skopeo From f19c39c84655d077ccc9133ca6b9faec1b069ee5 Mon Sep 17 00:00:00 2001 From: Patrick Burke <24pburke@gmail.com> Date: Wed, 28 May 2025 12:12:31 -0500 Subject: [PATCH 109/109] docs: updated Readme (CBL-Mariner is now Azure Linux) (#2684) * updated Readme (CBL-Mariner is now Azure Linux) --------- Signed-off-by: Patrick Burke <24pburke@gmail.com> Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com> --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4a6453e191b..c7ce525c21d 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ A vulnerability scanner for container images and filesystems. Easily [install th - Agenda: https://docs.google.com/document/d/1ZtSAa6fj2a6KRWviTn3WoJm09edvrNUp4Iz_dOjjyY8/edit?usp=sharing (join [this group](https://groups.google.com/g/anchore-oss-community) for write access) - All are welcome! -For commercial support options with Syft or Grype, please [contact Anchore](https://get.anchore.com/contact/) +For commercial support options with Syft or Grype, please [contact Anchore](https://get.anchore.com/contact/). ![grype-demo](https://user-images.githubusercontent.com/590471/90276236-9868f300-de31-11ea-8068-4268b6b68529.gif) @@ -34,9 +34,9 @@ For commercial support options with Syft or Grype, please [contact Anchore](http - Find vulnerabilities for major operating system packages: - Alpine - Amazon Linux + - Azure Linux (previously CBL-Mariner) - BusyBox - CentOS - - CBL-Mariner - Debian - Distroless - Oracle Linux