Compare commits

..
23 Commits
Author SHA1 Message Date
Alex Goodman 01de99b253 Add JVM cataloger (#3217)
* add jvm cataloger

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* simplify version selection

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* CPEs from JVM cataloger should be declared

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* ensure package overlap is enabled for sensitive use cases

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* more permissive glob

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-09-23 17:21:38 -04:00
Laurent Goderre 7815d8e4d9 feat: classifier for Dart lang binaries (#3265)
Signed-off-by: Laurent Goderre <laurent.goderre@docker.com>
2024-09-23 14:21:31 -04:00
Alex Goodman 963ea594c8 Add compliance policy for empty name and version (#3257)
* add policy for empty name and version

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* default stub version

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* modifying ids requires augmenting relationships

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-09-20 12:50:47 -04:00
dependabot[bot] 60bbd24031 chore(deps): bump github.com/github/go-spdx/v2 from 2.3.1 to 2.3.2 (#3254)
Bumps [github.com/github/go-spdx/v2](https://github.com/github/go-spdx) from 2.3.1 to 2.3.2.
- [Release notes](https://github.com/github/go-spdx/releases)
- [Commits](https://github.com/github/go-spdx/compare/v2.3.1...v2.3.2)

---
updated-dependencies:
- dependency-name: github.com/github/go-spdx/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 10:50:16 -04:00
dependabot[bot] 7c12e3f3b3 chore(deps): bump peter-evans/create-pull-request from 7.0.3 to 7.0.5 (#3255)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.3 to 7.0.5.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/6cd32fd93684475c31847837f87bb135d40a2b79...5e914681df9dc83aa4e4905692ca88beb2f9e91f)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 10:50:03 -04:00
dependabot[bot] 9b5cf1db51 chore(deps): bump github/codeql-action from 3.26.7 to 3.26.8 (#3256)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.7 to 3.26.8.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/8214744c546c1e5c8f03dde8fab3a7353211988d...294a9d92911152fe08befb9ec03e240add280cb3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 10:49:55 -04:00
anchore-actions-token-generator[bot]andspiffcs a08ea86aa6 chore(deps): update tools to latest versions (#3259)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2024-09-20 10:49:37 -04:00
dependabot[bot] 98c96ce361 chore(deps): bump github.com/docker/docker (#3260)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 27.2.1+incompatible to 27.3.0+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v27.2.1...v27.3.0)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 10:49:22 -04:00
6a95a5f2ed feat: add binary classifiers for lighttp, proftpd, zstd, xz, gzip, jq, and sqlcipher (#3252)
* feat: detect lighttpd binaries

Signed-off-by: Krystian Gorny <krystian.gorny@wipotec.com>

* feat: detect proftpd binaries

Signed-off-by: Krystian Gorny <krystian.gorny@wipotec.com>

* feat: detect zstd binaries

Signed-off-by: Krystian Gorny <krystian.gorny@wipotec.com>

* feat: detect xz utils binarie

Signed-off-by: Krystian Gorny <krystian.gorny@wipotec.com>

* feat: detect gzip binaries

Signed-off-by: Krystian Gorny <krystian.gorny@wipotec.com>

* feat: detect sqlcipher binaries

Signed-off-by: Krystian Gorny <krystian.gorny@wipotec.com>

* feat: detect jq binaries

Signed-off-by: Krystian Gorny <krystian.gorny@wipotec.com>

* add tests + snippets

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Krystian Gorny <krystian.gorny@wipotec.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Krystian Gorny <krystian.gorny@wipotec.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-09-19 13:21:02 +00:00
Krystian G.andGorny Krystian cb0de97bc3 fix: capture-snippet.sh can handle leading whitespaces now (#3249) (#3250)
Signed-off-by: Gorny Krystian <krystian.gorny@wipotec.com>
Co-authored-by: Gorny Krystian <krystian.gorny@wipotec.com>
2024-09-19 09:15:54 -04:00
anchore-actions-token-generator[bot]andspiffcs 50016c3172 chore(deps): update tools to latest versions (#3251)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2024-09-19 09:15:12 -04:00
anchore-actions-token-generator[bot]andspiffcs a2f12fef0c chore(deps): update tools to latest versions (#3247)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2024-09-18 13:13:24 -04:00
anchore-actions-token-generator[bot]andspiffcs 7934696463 chore(deps): update tools to latest versions (#3243)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2024-09-17 12:30:07 -04:00
dependabot[bot] b9efac4d78 chore(deps): bump github.com/CycloneDX/cyclonedx-go from 0.9.0 to 0.9.1 (#3242)
Bumps [github.com/CycloneDX/cyclonedx-go](https://github.com/CycloneDX/cyclonedx-go) from 0.9.0 to 0.9.1.
- [Release notes](https://github.com/CycloneDX/cyclonedx-go/releases)
- [Changelog](https://github.com/CycloneDX/cyclonedx-go/blob/master/.goreleaser.yml)
- [Commits](https://github.com/CycloneDX/cyclonedx-go/compare/v0.9.0...v0.9.1)

---
updated-dependencies:
- dependency-name: github.com/CycloneDX/cyclonedx-go
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 11:54:12 -04:00
dependabot[bot] 48c1c45d12 chore(deps): bump github/codeql-action from 3.26.6 to 3.26.7 (#3241)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.6 to 3.26.7.
- [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/4dd16135b69a43b6c8efb853346f8437d92d3c93...8214744c546c1e5c8f03dde8fab3a7353211988d)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 11:54:01 -04:00
dependabot[bot] 9cc3641ac6 chore(deps): bump peter-evans/create-pull-request from 7.0.2 to 7.0.3 (#3240)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.2 to 7.0.3.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/d121e62763d8cc35b5fb1710e887d6e69a52d3a4...6cd32fd93684475c31847837f87bb135d40a2b79)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-16 11:53:51 -04:00
anchore-actions-token-generator[bot]andspiffcs 7b4feb7c16 chore(deps): update tools to latest versions (#3231)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2024-09-16 09:09:11 -04:00
anchore-actions-token-generator[bot]andwagoodman 41e9630409 chore(deps): update CPE dictionary index (#3232)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
2024-09-16 09:08:50 -04:00
anchore-actions-token-generator[bot]andspiffcs 58100fec9f chore(deps): update tools to latest versions (#3205)
* chore(deps): update tools to latest versions

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* chore: disable gosec(G115)

A change to the rule gosec(G115) made a large amount of FP for gosec appear when updating to the
latest golang-ci linter.

https://github.com/securego/gosec/issues/1185
https://github.com/securego/gosec/pull/1149

We're going to ignore this rule for the time being while waiting for gosec to get updates so that
bound checking and example snippets of `valid` code is added for this rule

Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>

---------

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>
2024-09-13 15:05:50 -04:00
dependabot[bot] 834027e32d chore(deps): bump github.com/charmbracelet/bubbletea from 1.1.0 to 1.1.1 (#3225)
Bumps [github.com/charmbracelet/bubbletea](https://github.com/charmbracelet/bubbletea) from 1.1.0 to 1.1.1.
- [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.1.0...v1.1.1)

---
updated-dependencies:
- dependency-name: github.com/charmbracelet/bubbletea
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-13 13:51:17 -04:00
dependabot[bot] 2b4d5c275f chore(deps): bump peter-evans/create-pull-request from 7.0.1 to 7.0.2 (#3226)
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.1 to 7.0.2.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20...d121e62763d8cc35b5fb1710e887d6e69a52d3a4)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-13 11:31:09 -04:00
dependabot[bot] 38e51f16ec chore(deps): bump modernc.org/sqlite from 1.33.0 to 1.33.1 (#3229)
Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.33.0 to 1.33.1.
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.33.0...v1.33.1)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-13 11:30:58 -04:00
Keith Zantow 1b863268df feat: --enrich flag for data enrichment feature enablement (#3182)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2024-09-12 10:45:18 -04:00
65 changed files with 5393 additions and 242 deletions
+5 -5
View File
@@ -26,7 +26,7 @@ tools:
# used for linting
- name: golangci-lint
version:
want: v1.60.3
want: v1.61.0
method: github-release
with:
repo: golangci/golangci-lint
@@ -58,7 +58,7 @@ tools:
# used to release all artifacts
- name: goreleaser
version:
want: v2.2.0
want: v2.3.2
method: github-release
with:
repo: goreleaser/goreleaser
@@ -103,7 +103,7 @@ tools:
# used for running all local and CI tasks
- name: task
version:
want: v3.38.0
want: v3.39.2
method: github-release
with:
repo: go-task/task
@@ -111,7 +111,7 @@ tools:
# used for triggering a release
- name: gh
version:
want: v2.55.0
want: v2.57.0
method: github-release
with:
repo: cli/cli
@@ -130,4 +130,4 @@ tools:
want: v4.44.3
method: github-release
with:
repo: mikefarah/yq
repo: mikefarah/yq
+3 -3
View File
@@ -45,7 +45,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@4dd16135b69a43b6c8efb853346f8437d92d3c93 #v3.26.6
uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3 #v3.26.8
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@4dd16135b69a43b6c8efb853346f8437d92d3c93 #v3.26.6
uses: github/codeql-action/autobuild@294a9d92911152fe08befb9ec03e240add280cb3 #v3.26.8
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -70,4 +70,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@4dd16135b69a43b6c8efb853346f8437d92d3c93 #v3.26.6
uses: github/codeql-action/analyze@294a9d92911152fe08befb9ec03e240add280cb3 #v3.26.8
+1 -1
View File
@@ -49,7 +49,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 #v7.0.1
- uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f #v7.0.5
with:
signoff: true
delete-branch: true
@@ -33,7 +33,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 #v7.0.1
- uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f #v7.0.5
with:
signoff: true
delete-branch: true
@@ -44,7 +44,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@8867c4aba1b742c39f8d0ba35429c2dfa4b6cb20 #v7.0.1
- uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f #v7.0.5
with:
signoff: true
delete-branch: true
+3
View File
@@ -57,6 +57,9 @@ linters-settings:
settings:
ruleguard:
rules: "test/rules/rules.go"
gosec:
excludes:
- G115
output:
uniq-by-line: false
run:
+96 -18
View File
@@ -11,6 +11,7 @@ import (
"github.com/anchore/fangs"
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/task"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cataloging/filecataloging"
@@ -36,6 +37,8 @@ type Catalog struct {
Scope string `yaml:"scope" json:"scope" mapstructure:"scope"`
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
Relationships relationshipsConfig `yaml:"relationships" json:"relationships" mapstructure:"relationships"`
Compliance complianceConfig `yaml:"compliance" json:"compliance" mapstructure:"compliance"`
Enrich []string `yaml:"enrich" json:"enrich" mapstructure:"enrich"`
// ecosystem-specific cataloger configuration
Golang golangConfig `yaml:"golang" json:"golang" mapstructure:"golang"`
@@ -55,11 +58,12 @@ type Catalog struct {
var _ interface {
clio.FlagAdder
clio.PostLoader
fangs.FieldDescriber
clio.FieldDescriber
} = (*Catalog)(nil)
func DefaultCatalog() Catalog {
return Catalog{
Compliance: defaultComplianceConfig(),
Scope: source.SquashedScope.String(),
Package: defaultPackageConfig(),
LinuxKernel: defaultLinuxKernelConfig(),
@@ -77,6 +81,7 @@ func (cfg Catalog) ToSBOMConfig(id clio.Identification) *syft.CreateSBOMConfig {
WithTool(id.Name, id.Version).
WithParallelism(cfg.Parallelism).
WithRelationshipsConfig(cfg.ToRelationshipsConfig()).
WithComplianceConfig(cfg.ToComplianceConfig()).
WithSearchConfig(cfg.ToSearchConfig()).
WithPackagesConfig(cfg.ToPackagesConfig()).
WithFilesConfig(cfg.ToFilesConfig()).
@@ -102,6 +107,13 @@ func (cfg Catalog) ToRelationshipsConfig() cataloging.RelationshipsConfig {
}
}
func (cfg Catalog) ToComplianceConfig() cataloging.ComplianceConfig {
return cataloging.ComplianceConfig{
MissingName: cfg.Compliance.MissingName,
MissingVersion: cfg.Compliance.MissingVersion,
}
}
func (cfg Catalog) ToFilesConfig() filecataloging.Config {
hashers, err := intFile.Hashers(cfg.File.Metadata.Digests...)
if err != nil {
@@ -130,9 +142,9 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
return pkgcataloging.Config{
Binary: binary.DefaultClassifierCatalogerConfig(),
Golang: golang.DefaultCatalogerConfig().
WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses).
WithSearchLocalModCacheLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchLocalModCacheLicenses)).
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses).
WithSearchRemoteLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchRemoteLicenses)).
WithProxy(cfg.Golang.Proxy).
WithNoProxy(cfg.Golang.NoProxy).
WithMainModuleVersion(
@@ -142,7 +154,7 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
WithFromLDFlags(cfg.Golang.MainModuleVersion.FromLDFlags),
),
JavaScript: javascript.DefaultCatalogerConfig().
WithSearchRemoteLicenses(cfg.JavaScript.SearchRemoteLicenses).
WithSearchRemoteLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.JavaScript, task.Node, task.NPM), cfg.JavaScript.SearchRemoteLicenses)).
WithNpmBaseURL(cfg.JavaScript.NpmBaseURL),
LinuxKernel: kernel.LinuxKernelCatalogerConfig{
CatalogModules: cfg.LinuxKernel.CatalogModules,
@@ -151,9 +163,9 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
},
JavaArchive: java.DefaultArchiveCatalogerConfig().
WithUseMavenLocalRepository(cfg.Java.UseMavenLocalRepository).
WithUseMavenLocalRepository(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Java, task.Maven), cfg.Java.UseMavenLocalRepository)).
WithMavenLocalRepositoryDir(cfg.Java.MavenLocalRepositoryDir).
WithUseNetwork(cfg.Java.UseNetwork).
WithUseNetwork(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Java, task.Maven), cfg.Java.UseNetwork)).
WithMavenBaseURL(cfg.Java.MavenURL).
WithArchiveTraversal(archiveSearch, cfg.Java.MaxParentRecursiveDepth),
}
@@ -193,6 +205,9 @@ func (cfg *Catalog) AddFlags(flags clio.FlagSet) {
flags.StringArrayVarP(&cfg.SelectCatalogers, "select-catalogers", "",
"add, remove, and filter the catalogers to be used")
flags.StringArrayVarP(&cfg.Enrich, "enrich", "",
fmt.Sprintf("enable package data enrichment from local and online sources (options: %s)", strings.Join(publicisedEnrichmentOptions, ", ")))
flags.StringVarP(&cfg.Source.Name, "source-name", "",
"set the name of the target being analyzed")
@@ -205,6 +220,10 @@ func (cfg *Catalog) AddFlags(flags clio.FlagSet) {
func (cfg *Catalog) DescribeFields(descriptions fangs.FieldDescriptionSet) {
descriptions.Add(&cfg.Parallelism, "number of cataloger workers to run in parallel")
descriptions.Add(&cfg.Enrich, fmt.Sprintf(`Enable data enrichment operations, which can utilize services such as Maven Central and NPM.
By default all enrichment is disabled, use: all to enable everything.
Available options are: %s`, strings.Join(publicisedEnrichmentOptions, ", ")))
}
func (cfg *Catalog) PostLoad() error {
@@ -215,23 +234,12 @@ func (cfg *Catalog) PostLoad() error {
return fmt.Errorf("cannot use both 'catalogers' and 'select-catalogers'/'default-catalogers' flags")
}
flatten := func(l []string) []string {
var out []string
for _, v := range l {
for _, s := range strings.Split(v, ",") {
out = append(out, strings.TrimSpace(s))
}
}
sort.Strings(out)
return out
}
cfg.From = flatten(cfg.From)
cfg.Catalogers = flatten(cfg.Catalogers)
cfg.DefaultCatalogers = flatten(cfg.DefaultCatalogers)
cfg.SelectCatalogers = flatten(cfg.SelectCatalogers)
cfg.Enrich = flatten(cfg.Enrich)
// for backwards compatibility
cfg.DefaultCatalogers = append(cfg.DefaultCatalogers, cfg.Catalogers...)
@@ -241,5 +249,75 @@ func (cfg *Catalog) PostLoad() error {
return fmt.Errorf("bad scope value %q", cfg.Scope)
}
// the binary package exclusion code depends on the file overlap relationships being created upstream in processing
if !cfg.Relationships.PackageFileOwnershipOverlap && cfg.Package.ExcludeBinaryOverlapByOwnership {
return fmt.Errorf("cannot enable exclude-binary-overlap-by-ownership without enabling package-file-ownership-overlap")
}
return nil
}
func flatten(commaSeparatedEntries []string) []string {
var out []string
for _, v := range commaSeparatedEntries {
for _, s := range strings.Split(v, ",") {
out = append(out, strings.TrimSpace(s))
}
}
sort.Strings(out)
return out
}
var publicisedEnrichmentOptions = []string{
"all",
task.Golang,
task.Java,
task.JavaScript,
}
func enrichmentEnabled(enrichDirectives []string, features ...string) *bool {
if len(enrichDirectives) == 0 {
return nil
}
enabled := func(features ...string) *bool {
for _, directive := range enrichDirectives {
enable := true
directive = strings.TrimPrefix(directive, "+") // +java and java are equivalent
if strings.HasPrefix(directive, "-") {
directive = directive[1:]
enable = false
}
for _, feature := range features {
if directive == feature {
return &enable
}
}
}
return nil
}
enableAll := enabled("all")
disableAll := enabled("none")
if disableAll != nil && *disableAll {
if enableAll != nil {
log.Warn("you have specified to both enable and disable all enrichment functionality, defaulting to disabled")
}
enableAll = ptr(false)
}
// check for explicit enable/disable of feature names
for _, feat := range features {
enableFeature := enabled(feat)
if enableFeature != nil {
return enableFeature
}
}
return enableAll
}
func ptr[T any](val T) *T {
return &val
}
+74
View File
@@ -57,6 +57,14 @@ func TestCatalog_PostLoad(t *testing.T) {
assert.Empty(t, options.Catalogers)
},
},
{
name: "must have package overlap flag when pruning binaries by overlap",
options: Catalog{
Package: packageConfig{ExcludeBinaryOverlapByOwnership: true},
Relationships: relationshipsConfig{PackageFileOwnershipOverlap: false},
},
wantErr: assert.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -70,3 +78,69 @@ func TestCatalog_PostLoad(t *testing.T) {
})
}
}
func Test_enrichmentEnabled(t *testing.T) {
tests := []struct {
directives string
test string
expected *bool
}{
{
directives: "",
test: "java",
expected: nil,
},
{
directives: "none",
test: "java",
expected: ptr(false),
},
{
directives: "none,+java",
test: "java",
expected: ptr(true),
},
{
directives: "all,none",
test: "java",
expected: ptr(false),
},
{
directives: "all",
test: "java",
expected: ptr(true),
},
{
directives: "golang,js",
test: "java",
expected: nil,
},
{
directives: "golang,-js,java",
test: "java",
expected: ptr(true),
},
{
directives: "golang,js,-java",
test: "java",
expected: ptr(false),
},
{
directives: "all",
test: "java",
expected: ptr(true),
},
{
directives: "all,-java",
test: "java",
expected: ptr(false),
},
}
for _, test := range tests {
t.Run(test.directives, func(t *testing.T) {
got := enrichmentEnabled(flatten([]string{test.directives}), test.test)
assert.Equal(t, test.expected, got)
})
}
}
+35
View File
@@ -0,0 +1,35 @@
package options
import (
"github.com/anchore/fangs"
"github.com/anchore/syft/syft/cataloging"
)
var (
_ fangs.FieldDescriber = (*complianceConfig)(nil)
_ fangs.PostLoader = (*complianceConfig)(nil)
)
type complianceConfig struct {
MissingName cataloging.ComplianceAction `mapstructure:"missing-name" json:"missing-name" yaml:"missing-name"`
MissingVersion cataloging.ComplianceAction `mapstructure:"missing-version" json:"missing-version" yaml:"missing-version"`
}
func defaultComplianceConfig() complianceConfig {
def := cataloging.DefaultComplianceConfig()
return complianceConfig{
MissingName: def.MissingName,
MissingVersion: def.MissingVersion,
}
}
func (r *complianceConfig) DescribeFields(descriptions fangs.FieldDescriptionSet) {
descriptions.Add(&r.MissingName, "action to take when a package is missing a name")
descriptions.Add(&r.MissingVersion, "action to take when a package is missing a version")
}
func (r *complianceConfig) PostLoad() error {
r.MissingName = r.MissingName.Parse()
r.MissingVersion = r.MissingVersion.Parse()
return nil
}
+4 -4
View File
@@ -8,9 +8,9 @@ import (
)
type golangConfig struct {
SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
SearchLocalModCacheLicenses *bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
SearchRemoteLicenses *bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
MainModuleVersion golangMainModuleVersionConfig `json:"main-module-version" yaml:"main-module-version" mapstructure:"main-module-version"`
@@ -47,9 +47,9 @@ type golangMainModuleVersionConfig struct {
func defaultGolangConfig() golangConfig {
def := golang.DefaultCatalogerConfig()
return golangConfig{
SearchLocalModCacheLicenses: def.SearchLocalModCacheLicenses,
SearchLocalModCacheLicenses: nil, // this defaults to false, which is the API default
LocalModCacheDir: def.LocalModCacheDir,
SearchRemoteLicenses: def.SearchRemoteLicenses,
SearchRemoteLicenses: nil, // this defaults to false, which is the API default
Proxy: strings.Join(def.Proxies, ","),
NoProxy: strings.Join(def.NoProxy, ","),
MainModuleVersion: golangMainModuleVersionConfig{
+4 -4
View File
@@ -6,8 +6,8 @@ import (
)
type javaConfig struct {
UseNetwork bool `yaml:"use-network" json:"use-network" mapstructure:"use-network"`
UseMavenLocalRepository bool `yaml:"use-maven-local-repository" json:"use-maven-local-repository" mapstructure:"use-maven-local-repository"`
UseNetwork *bool `yaml:"use-network" json:"use-network" mapstructure:"use-network"`
UseMavenLocalRepository *bool `yaml:"use-maven-local-repository" json:"use-maven-local-repository" mapstructure:"use-maven-local-repository"`
MavenLocalRepositoryDir string `yaml:"maven-local-repository-dir" json:"maven-local-repository-dir" mapstructure:"maven-local-repository-dir"`
MavenURL string `yaml:"maven-url" json:"maven-url" mapstructure:"maven-url"`
MaxParentRecursiveDepth int `yaml:"max-parent-recursive-depth" json:"max-parent-recursive-depth" mapstructure:"max-parent-recursive-depth"`
@@ -17,9 +17,9 @@ func defaultJavaConfig() javaConfig {
def := java.DefaultArchiveCatalogerConfig()
return javaConfig{
UseNetwork: def.UseNetwork,
UseNetwork: nil, // this defaults to false, which is the API default
MaxParentRecursiveDepth: def.MaxParentRecursiveDepth,
UseMavenLocalRepository: def.UseMavenLocalRepository,
UseMavenLocalRepository: nil, // this defaults to false, which is the API default
MavenLocalRepositoryDir: def.MavenLocalRepositoryDir,
MavenURL: def.MavenBaseURL,
}
+1 -1
View File
@@ -3,7 +3,7 @@ package options
import "github.com/anchore/clio"
type javaScriptConfig struct {
SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
SearchRemoteLicenses *bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
NpmBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"`
}
+17 -17
View File
@@ -3,14 +3,14 @@ module github.com/anchore/syft
go 1.22.0
require (
github.com/CycloneDX/cyclonedx-go v0.9.0
github.com/CycloneDX/cyclonedx-go v0.9.1
github.com/Masterminds/semver v1.5.0
github.com/Masterminds/sprig/v3 v3.3.0
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/acobaugh/osrelease v0.1.0
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9
github.com/anchore/clio v0.0.0-20240522144804-d81e109008aa
github.com/anchore/fangs v0.0.0-20240508143433-f016b099950f
github.com/anchore/fangs v0.0.0-20240903175602-e716ef12c23d
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
@@ -24,16 +24,16 @@ require (
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.1.0
github.com/charmbracelet/bubbletea v1.1.1
github.com/charmbracelet/lipgloss v0.13.0
github.com/dave/jennifer v1.7.1
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/distribution/reference v0.6.0
github.com/docker/docker v27.2.1+incompatible
github.com/docker/docker v27.3.0+incompatible
github.com/dustin/go-humanize v1.0.1
github.com/elliotchance/phpserialize v1.4.0
github.com/facebookincubator/nvdtools v0.1.5
github.com/github/go-spdx/v2 v2.3.1
github.com/github/go-spdx/v2 v2.3.2
github.com/gkampitakis/go-snaps v0.5.7
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.12.0
@@ -81,10 +81,10 @@ require (
golang.org/x/mod v0.21.0
golang.org/x/net v0.29.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.33.0
modernc.org/sqlite v1.33.1
)
require google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
require google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
require (
github.com/BurntSushi/toml v1.4.0
@@ -135,13 +135,13 @@ require (
github.com/emirpasic/gods v1.18.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
github.com/gkampitakis/ciinfo v0.3.0 // indirect
github.com/gkampitakis/go-diff v1.3.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-restruct/restruct v1.2.0-alpha // indirect
github.com/gogo/protobuf v1.3.2 // indirect
@@ -185,7 +185,7 @@ require (
github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pierrec/lz4/v4 v4.1.19 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
@@ -204,7 +204,7 @@ require (
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/sylabs/sif/v2 v2.17.1 // indirect
github.com/sylabs/squashfs v1.0.0 // indirect
@@ -221,10 +221,10 @@ require (
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect
go.opentelemetry.io/otel v1.19.0 // indirect
go.opentelemetry.io/otel/metric v1.19.0 // indirect
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
@@ -234,8 +234,8 @@ require (
golang.org/x/text v0.18.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/grpc v1.59.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
+46 -36
View File
@@ -60,8 +60,8 @@ github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
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/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CycloneDX/cyclonedx-go v0.9.0 h1:inaif7qD8bivyxp7XLgxUYtOXWtDez7+j72qKTMQTb8=
github.com/CycloneDX/cyclonedx-go v0.9.0/go.mod h1:NE/EWvzELOFlG6+ljX/QeMlVt9VKcTwu8u0ccsACEsw=
github.com/CycloneDX/cyclonedx-go v0.9.1 h1:yffaWOZsv77oTJa/SdVZYdgAgFioCeycBUKkqS2qzQM=
github.com/CycloneDX/cyclonedx-go v0.9.1/go.mod h1:NE/EWvzELOFlG6+ljX/QeMlVt9VKcTwu8u0ccsACEsw=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
@@ -99,8 +99,8 @@ github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 h1:p0ZIe0htYOX284Y4
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9/go.mod h1:3ZsFB9tzW3vl4gEiUeuSOMDnwroWxIxJelOOHUp8dSw=
github.com/anchore/clio v0.0.0-20240522144804-d81e109008aa h1:pwlAn4O9SBUnlgfa69YcqIynbUyobLVFYu8HxSoCffA=
github.com/anchore/clio v0.0.0-20240522144804-d81e109008aa/go.mod h1:nD3H5uIvjxlfmakOBgtyFQbk5Zjp3l538kxfpHPslzI=
github.com/anchore/fangs v0.0.0-20240508143433-f016b099950f h1:NOhzafCyNYFi88qxkBFjMzQo4dRa1vDhBzx+0Uovx8Q=
github.com/anchore/fangs v0.0.0-20240508143433-f016b099950f/go.mod h1:sVpRS2yNCw6tLVpvA1QSDVWTJVpCuAm8JNZgn4Sjz/k=
github.com/anchore/fangs v0.0.0-20240903175602-e716ef12c23d h1:ZD4wdCBgJJzJybjTUIEiiupLF7B9H3WLuBTjspBO2Mc=
github.com/anchore/fangs v0.0.0-20240903175602-e716ef12c23d/go.mod h1:Xh4ObY3fmoMzOEVXwDtS1uK44JC7+nRD0n29/1KYFYg=
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=
@@ -159,8 +159,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2/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/bubbletea v1.1.0 h1:FjAl9eAL3HBCHenhz/ZPjkKdScmaS5SK69JAK2YJK9c=
github.com/charmbracelet/bubbletea v1.1.0/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY=
github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
@@ -229,8 +229,8 @@ github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2
github.com/docker/cli v27.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 v27.2.1+incompatible h1:fQdiLfW7VLscyoeYEBz7/J8soYFDZV1u6VW6gJEjNMI=
github.com/docker/docker v27.2.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.3.0+incompatible h1:BNb1QY6o4JdKpqwi9IB+HUYcRRrVN4aGFUTvDmWYK1A=
github.com/docker/docker v27.3.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -275,8 +275,8 @@ github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
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=
@@ -285,8 +285,8 @@ github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyT
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.3.1 h1:ffGuHTbHuHzWPt53n8f9o8clGutuLPObo3zB4JAjxU8=
github.com/github/go-spdx/v2 v2.3.1/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
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/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8=
github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
@@ -313,8 +313,8 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc=
@@ -622,8 +622,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.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
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/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
@@ -731,8 +731,8 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
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/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=
@@ -824,20 +824,20 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q=
go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
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.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
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=
@@ -1261,12 +1261,12 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1294,8 +1294,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1344,8 +1344,14 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
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.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
@@ -1354,8 +1360,12 @@ modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/sqlite v1.33.0 h1:WWkA/T2G17okiLGgKAj4/RMIvgyMT19yQ038160IeYk=
modernc.org/sqlite v1.33.0/go.mod h1:9uQ9hF/pCZoYZK73D/ud5Z7cIRIILSZI8NdIemVMTX8=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM=
modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+1 -1
View File
@@ -3,5 +3,5 @@ package internal
const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "16.0.16"
JSONSchemaVersion = "16.0.17"
)
+2 -2
View File
@@ -56,7 +56,7 @@ func OpenZip(filepath string) (*ZipReadCloser, error) {
if offset > math.MaxInt64 {
return nil, fmt.Errorf("archive start offset too large: %v", offset)
}
offset64 := int64(offset) //nolint:gosec // lint bug, checked above: https://github.com/securego/gosec/issues/1187
offset64 := int64(offset)
size := fi.Size() - offset64
@@ -183,7 +183,7 @@ func findDirectory64End(r io.ReaderAt, directoryEndOffset int64) (int64, error)
if b.uint32() != 1 { // total number of disks
return -1, nil // the file is not a valid zip64-file
}
return int64(p), nil //nolint:gosec
return int64(p), nil
}
// readDirectory64End reads the zip64 directory end and updates the
@@ -25,55 +25,102 @@ var (
binaryMetadataTypes = []string{
reflect.TypeOf(pkg.ELFBinaryPackageNoteJSONPayload{}).Name(),
reflect.TypeOf(pkg.BinarySignature{}).Name(),
reflect.TypeOf(pkg.JavaVMInstallation{}).Name(),
}
)
func ExcludeBinariesByFileOwnershipOverlap(accessor sbomsync.Accessor) {
accessor.WriteToSBOM(func(s *sbom.SBOM) {
for _, r := range s.Relationships {
if excludeBinaryByFileOwnershipOverlap(r, s.Artifacts.Packages) {
s.Artifacts.Packages.Delete(r.To.ID())
s.Relationships = RemoveRelationshipsByID(s.Relationships, r.To.ID())
if idToRemove := excludeByFileOwnershipOverlap(r, s.Artifacts.Packages); idToRemove != "" {
s.Artifacts.Packages.Delete(idToRemove)
s.Relationships = RemoveRelationshipsByID(s.Relationships, idToRemove)
}
}
})
}
// excludeBinaryByFileOwnershipOverlap will remove packages from a collection given the following properties are true
// 1) the relationship between packages is OwnershipByFileOverlap
// 2) the parent is an "os" package
// 3) the child is a synthetic package generated by the binary cataloger
// 4) the package names are identical
// This was implemented as a way to help resolve: https://github.com/anchore/syft/issues/931
func excludeBinaryByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) bool {
// excludeByFileOwnershipOverlap will remove packages that should be overridden by a more authoritative package,
// such as an OS package or a package from a cataloger with more specific information being raised up.
func excludeByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) artifact.ID {
if artifact.OwnershipByFileOverlapRelationship != r.Type {
return false
return ""
}
parent := c.Package(r.From.ID())
if parent == nil {
return false
}
parentInExclusion := slices.Contains(osCatalogerTypes, parent.Type)
if !parentInExclusion {
return false
return ""
}
child := c.Package(r.To.ID())
if child == nil {
return false
return ""
}
if slices.Contains(binaryCatalogerTypes, child.Type) {
return true
if idToRemove := identifyOverlappingOSRelationship(parent, child); idToRemove != "" {
return idToRemove
}
if idToRemove := identifyOverlappingJVMRelationship(parent, child); idToRemove != "" {
return idToRemove
}
return ""
}
// identifyOverlappingJVMRelationship indicates the package to remove if this is a binary -> binary pkg relationship
// with a java binary signature package and a more authoritative JVM release package.
func identifyOverlappingJVMRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID {
if !slices.Contains(binaryCatalogerTypes, parent.Type) {
return ""
}
if !slices.Contains(binaryCatalogerTypes, child.Type) {
return ""
}
if child.Metadata == nil {
return false
return ""
}
childMetadataType := reflect.TypeOf(child.Metadata)
var (
foundJVM bool
idToRemove artifact.ID
)
for _, p := range []*pkg.Package{parent, child} {
switch p.Metadata.(type) {
case pkg.JavaVMInstallation:
foundJVM = true
default:
idToRemove = p.ID()
}
}
return slices.Contains(binaryMetadataTypes, childMetadataType.Name())
if foundJVM {
return idToRemove
}
return ""
}
// identifyOverlappingOSRelationship indicates the package ID to remove if this is an OS pkg -> bin pkg relationship.
// This was implemented as a way to help resolve: https://github.com/anchore/syft/issues/931
func identifyOverlappingOSRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID {
if !slices.Contains(osCatalogerTypes, parent.Type) {
return ""
}
if slices.Contains(binaryCatalogerTypes, child.Type) {
return child.ID()
}
if child.Metadata == nil {
return ""
}
if !slices.Contains(binaryMetadataTypes, reflect.TypeOf(child.Metadata).Name()) {
return ""
}
return child.ID()
}
@@ -3,18 +3,17 @@ package relationship
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/pkg"
)
func TestExclude(t *testing.T) {
func TestExcludeByFileOwnershipOverlap(t *testing.T) {
packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg}
packageB := pkg.Package{Name: "package-a", Type: pkg.PythonPkg}
packageC := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg}
packageD := pkg.Package{Name: "package-d", Type: pkg.BinaryPkg}
packageE := pkg.Package{Name: "package-e", Type: pkg.RpmPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{Type: "rpm"}}
packageF := pkg.Package{Name: "package-f", Type: pkg.RpmPkg, Metadata: pkg.BinarySignature{}}
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE, &packageF} {
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg, Metadata: pkg.JavaVMInstallation{}}
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{Type: "rpm"}}
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC} {
p := p
p.SetID()
}
@@ -26,73 +25,152 @@ func TestExclude(t *testing.T) {
shouldExclude bool
}{
{
name: "no exclusions from os -> python",
// prove that OS -> bin exclusions are wired
name: "exclusions from os -> elf binary (as RPM)",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageB,
},
packages: pkg.NewCollection(packageA, packageB),
shouldExclude: false,
},
{
name: "exclusions from os -> binary",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageC,
From: packageA, // OS
To: packageC, // ELF binary
},
packages: pkg.NewCollection(packageA, packageC),
shouldExclude: true,
},
{
name: "exclusions from os -> elf binary (as RPM)",
// prove that bin -> JVM exclusions are wired
name: "exclusions from binary -> binary with JVM metadata",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageE,
From: packageB, // binary with JVM metadata
To: packageC, // binary
},
packages: pkg.NewCollection(packageA, packageE),
packages: pkg.NewCollection(packageC, packageB),
shouldExclude: true,
},
{
name: "exclusions from os -> binary (masquerading as RPM)",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageF,
},
packages: pkg.NewCollection(packageA, packageF),
shouldExclude: true,
},
{
name: "no exclusions from python -> binary",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageB,
To: packageC,
},
packages: pkg.NewCollection(packageB, packageC),
shouldExclude: false,
},
{
name: "no exclusions for different package names",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageD,
},
packages: pkg.NewCollection(packageA, packageD),
shouldExclude: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if !excludeBinaryByFileOwnershipOverlap(test.relationship, test.packages) && test.shouldExclude {
actualExclude := excludeByFileOwnershipOverlap(test.relationship, test.packages)
didExclude := actualExclude != ""
if !didExclude && test.shouldExclude {
t.Errorf("expected to exclude relationship %+v", test.relationship)
}
})
}
}
func TestIdentifyOverlappingOSRelationship(t *testing.T) {
packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg} // OS package
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg}
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}}
packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg} // Language package
packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{}}
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} {
p.SetID()
}
tests := []struct {
name string
parent *pkg.Package
child *pkg.Package
expectedID artifact.ID
}{
{
name: "OS -> binary without metadata",
parent: &packageA,
child: &packageB,
expectedID: packageB.ID(), // OS package to binary package, should return child ID
},
{
name: "OS -> binary with binary metadata",
parent: &packageA,
child: &packageC,
expectedID: packageC.ID(), // OS package to binary package with binary metadata, should return child ID
},
{
name: "OS -> non-binary package",
parent: &packageA,
child: &packageD,
expectedID: "", // OS package to non-binary package, no exclusion
},
{
name: "OS -> binary with ELF metadata",
parent: &packageA,
child: &packageE,
expectedID: packageE.ID(), // OS package to binary package with ELF metadata, should return child ID
},
{
name: "non-OS parent",
parent: &packageD, // non-OS package
child: &packageC,
expectedID: "", // non-OS parent, no exclusion
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resultID := identifyOverlappingOSRelationship(tt.parent, tt.child)
assert.Equal(t, tt.expectedID, resultID)
})
}
}
func TestIdentifyOverlappingJVMRelationship(t *testing.T) {
packageA := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg}
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}}
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.JavaVMInstallation{}}
packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg}
packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg}
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} {
p.SetID()
}
tests := []struct {
name string
parent *pkg.Package
child *pkg.Package
expectedID artifact.ID
}{
{
name: "binary -> binary with JVM installation",
parent: &packageA,
child: &packageC,
expectedID: packageA.ID(), // JVM found, return BinaryPkg ID
},
{
name: "binary -> binary with binary signature",
parent: &packageA,
child: &packageB,
expectedID: "", // binary signatures only found, no exclusion
},
{
name: "binary -> python (non-binary child)",
parent: &packageA,
child: &packageD,
expectedID: "", // non-binary child, no exclusion
},
{
name: "no JVM or signature in binary -> binary",
parent: &packageA,
child: &packageE,
expectedID: "", // no JVM or binary signature, no exclusion
},
{
name: "non-binary parent",
parent: &packageD,
child: &packageC,
expectedID: "", // non-binary parent, no exclusion
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resultID := identifyOverlappingJVMRelationship(tt.parent, tt.child)
assert.Equal(t, tt.expectedID, resultID)
})
}
}
+46 -10
View File
@@ -17,20 +17,16 @@ type Index struct {
// NewIndex returns a new relationship Index
func NewIndex(relationships ...artifact.Relationship) *Index {
out := Index{}
out := Index{
fromID: make(map[artifact.ID]*mappedRelationships),
toID: make(map[artifact.ID]*mappedRelationships),
}
out.Add(relationships...)
return &out
}
// Add adds all the given relationships to the index, without adding duplicates
func (i *Index) Add(relationships ...artifact.Relationship) {
if i.fromID == nil {
i.fromID = map[artifact.ID]*mappedRelationships{}
}
if i.toID == nil {
i.toID = map[artifact.ID]*mappedRelationships{}
}
// store appropriate indexes for stable ordering to minimize ID() calls
for _, r := range relationships {
// prevent duplicates
@@ -68,6 +64,39 @@ func (i *Index) Add(relationships ...artifact.Relationship) {
}
}
func (i *Index) Remove(id artifact.ID) {
delete(i.fromID, id)
delete(i.toID, id)
for idx := 0; idx < len(i.all); {
if i.all[idx].from == id || i.all[idx].to == id {
i.all = append(i.all[:idx], i.all[idx+1:]...)
} else {
idx++
}
}
}
func (i *Index) Replace(ogID artifact.ID, replacement artifact.Identifiable) {
for _, mapped := range fromMappedByID(i.fromID, ogID) {
i.Add(artifact.Relationship{
From: replacement,
To: mapped.relationship.To,
Type: mapped.relationship.Type,
})
}
for _, mapped := range fromMappedByID(i.toID, ogID) {
i.Add(artifact.Relationship{
From: mapped.relationship.From,
To: replacement,
Type: mapped.relationship.Type,
})
}
i.Remove(ogID)
}
// From returns all relationships from the given identifiable, with specified types
func (i *Index) From(identifiable artifact.Identifiable, types ...artifact.RelationshipType) []artifact.Relationship {
return toSortedSlice(fromMapped(i.fromID, identifiable), types)
@@ -110,10 +139,17 @@ func (i *Index) All(types ...artifact.RelationshipType) []artifact.Relationship
}
func fromMapped(idMap map[artifact.ID]*mappedRelationships, identifiable artifact.Identifiable) []*sortableRelationship {
if identifiable == nil || idMap == nil {
if identifiable == nil {
return nil
}
mapped := idMap[identifiable.ID()]
return fromMappedByID(idMap, identifiable.ID())
}
func fromMappedByID(idMap map[artifact.ID]*mappedRelationships, id artifact.ID) []*sortableRelationship {
if idMap == nil {
return nil
}
mapped := idMap[id]
if mapped == nil {
return nil
}
+177
View File
@@ -3,6 +3,7 @@ package relationship
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
@@ -231,3 +232,179 @@ func (i id) ID() artifact.ID {
func slice[T any](values ...T) []T {
return values
}
func TestRemove(t *testing.T) {
p1 := pkg.Package{Name: "pkg-1"}
p2 := pkg.Package{Name: "pkg-2"}
p3 := pkg.Package{Name: "pkg-3"}
c1 := file.Coordinates{RealPath: "/coords/1"}
c2 := file.Coordinates{RealPath: "/coords/2"}
c3 := file.Coordinates{RealPath: "/coords/3"}
c4 := file.Coordinates{RealPath: "/coords/4"}
for _, p := range []*pkg.Package{&p1, &p2, &p3} {
p.SetID()
}
r1 := artifact.Relationship{
From: p1,
To: p2,
Type: artifact.DependencyOfRelationship,
}
r2 := artifact.Relationship{
From: p1,
To: p3,
Type: artifact.DependencyOfRelationship,
}
r3 := artifact.Relationship{
From: p1,
To: c1,
Type: artifact.ContainsRelationship,
}
r4 := artifact.Relationship{
From: p2,
To: c2,
Type: artifact.ContainsRelationship,
}
r5 := artifact.Relationship{
From: p3,
To: c1,
Type: artifact.ContainsRelationship,
}
r6 := artifact.Relationship{
From: p3,
To: c2,
Type: artifact.ContainsRelationship,
}
r7 := artifact.Relationship{
From: c1,
To: c3,
Type: artifact.ContainsRelationship,
}
r8 := artifact.Relationship{
From: c3,
To: c4,
Type: artifact.ContainsRelationship,
}
index := NewIndex(r1, r2, r3, r4, r5, r6, r7, r8)
assert.Equal(t, 8, len(index.All()))
// removal of p1 should remove r1, r2, and r3
index.Remove(p1.ID())
remaining := index.All()
assert.Equal(t, 5, len(remaining))
assert.NotContains(t, remaining, r1)
assert.NotContains(t, remaining, r2)
assert.NotContains(t, remaining, r3)
assert.Empty(t, index.From(p1))
assert.Empty(t, index.To(p1))
// removal of c1 should remove r5 and r7
index.Remove(c1.ID())
remaining = index.All()
// r8 remains since c3->c4 should still exist
assert.Equal(t, 3, len(remaining))
assert.NotContains(t, remaining, r5)
assert.NotContains(t, remaining, r7)
assert.Contains(t, remaining, r8)
assert.Empty(t, index.From(c1))
assert.Empty(t, index.To(c1))
// removal of c3 should remove r8
index.Remove(c3.ID())
remaining = index.All()
assert.Equal(t, 2, len(remaining))
assert.Contains(t, remaining, r4)
assert.Contains(t, remaining, r6)
assert.Empty(t, index.From(c3))
assert.Empty(t, index.To(c3))
}
func TestReplace(t *testing.T) {
p1 := pkg.Package{Name: "pkg-1"}
p2 := pkg.Package{Name: "pkg-2"}
p3 := pkg.Package{Name: "pkg-3"}
p4 := pkg.Package{Name: "pkg-4"}
for _, p := range []*pkg.Package{&p1, &p2, &p3, &p4} {
p.SetID()
}
r1 := artifact.Relationship{
From: p1,
To: p2,
Type: artifact.DependencyOfRelationship,
}
r2 := artifact.Relationship{
From: p3,
To: p1,
Type: artifact.DependencyOfRelationship,
}
r3 := artifact.Relationship{
From: p2,
To: p3,
Type: artifact.ContainsRelationship,
}
index := NewIndex(r1, r2, r3)
// replace p1 with p4 in the relationships
index.Replace(p1.ID(), &p4)
expectedRels := []artifact.Relationship{
{
From: p4, // replaced
To: p2,
Type: artifact.DependencyOfRelationship,
},
{
From: p3,
To: p4, // replaced
Type: artifact.DependencyOfRelationship,
},
{
From: p2,
To: p3,
Type: artifact.ContainsRelationship,
},
}
compareRelationships(t, expectedRels, index.All())
}
func compareRelationships(t testing.TB, expected, actual []artifact.Relationship) {
assert.Equal(t, len(expected), len(actual), "number of relationships should match")
for _, e := range expected {
found := false
for _, a := range actual {
if a.From.ID() == e.From.ID() && a.To.ID() == e.To.ID() && a.Type == e.Type {
found = true
break
}
}
assert.True(t, found, "expected relationship not found: %+v", e)
}
}
func TestReplace_NoExistingRelations(t *testing.T) {
p1 := pkg.Package{Name: "pkg-1"}
p2 := pkg.Package{Name: "pkg-2"}
p1.SetID()
p2.SetID()
index := NewIndex()
index.Replace(p1.ID(), &p2)
allRels := index.All()
assert.Len(t, allRels, 0)
}
+136 -36
View File
@@ -11,6 +11,7 @@ import (
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/relationship"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cataloging"
@@ -27,6 +28,7 @@ type packageTaskFactory func(cfg CatalogingFactoryConfig) Task
type PackageTaskFactories []packageTaskFactory
type CatalogingFactoryConfig struct {
ComplianceConfig cataloging.ComplianceConfig
SearchConfig cataloging.SearchConfig
RelationshipsConfig cataloging.RelationshipsConfig
DataGenerationConfig cataloging.DataGenerationConfig
@@ -35,6 +37,7 @@ type CatalogingFactoryConfig struct {
func DefaultCatalogingFactoryConfig() CatalogingFactoryConfig {
return CatalogingFactoryConfig{
ComplianceConfig: cataloging.DefaultComplianceConfig(),
SearchConfig: cataloging.DefaultSearchConfig(),
RelationshipsConfig: cataloging.DefaultRelationshipsConfig(),
DataGenerationConfig: cataloging.DefaultDataGenerationConfig(),
@@ -82,7 +85,7 @@ func (f PackageTaskFactories) Tasks(cfg CatalogingFactoryConfig) ([]Task, error)
}
// NewPackageTask creates a Task function for a generic pkg.Cataloger, honoring the common configuration options.
func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string) Task { //nolint: funlen
func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string) Task {
fn := func(ctx context.Context, resolver file.Resolver, sbom sbomsync.Builder) error {
catalogerName := c.Name()
log.WithFields("name", catalogerName).Trace("starting package cataloger")
@@ -106,42 +109,9 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
log.WithFields("cataloger", catalogerName).Debugf("discovered %d packages", len(pkgs))
for i, p := range pkgs {
if p.FoundBy == "" {
p.FoundBy = catalogerName
}
pkgs, relationships = finalizePkgCatalogerResults(cfg, resolver, catalogerName, pkgs, relationships)
if cfg.DataGenerationConfig.GenerateCPEs && !hasAuthoritativeCPE(p.CPEs) {
// generate CPEs (note: this is excluded from package ID, so is safe to mutate)
// we might have binary classified CPE already with the package so we want to append here
dictionaryCPEs, ok := cpeutils.DictionaryFind(p)
if ok {
log.Tracef("used CPE dictionary to find CPEs for %s package %q: %s", p.Type, p.Name, dictionaryCPEs)
p.CPEs = append(p.CPEs, dictionaryCPEs...)
} else {
p.CPEs = append(p.CPEs, cpeutils.Generate(p)...)
}
}
// if we were not able to identify the language we have an opportunity
// to try and get this value from the PURL. Worst case we assert that
// we could not identify the language at either stage and set UnknownLanguage
if p.Language == "" {
p.Language = pkg.LanguageFromPURL(p.PURL)
}
if cfg.RelationshipsConfig.PackageFileOwnership {
// create file-to-package relationships for files owned by the package
owningRelationships, err := packageFileOwnershipRelationships(p, resolver)
if err != nil {
log.Warnf("unable to create any package-file relationships for package name=%q type=%q: %w", p.Name, p.Type, err)
} else {
relationships = append(relationships, owningRelationships...)
}
}
pkgs[i] = p
}
pkgs, relationships = applyCompliance(cfg.ComplianceConfig, pkgs, relationships)
sbom.AddPackages(pkgs...)
sbom.AddRelationships(relationships...)
@@ -157,6 +127,136 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
return NewTask(c.Name(), fn, tags...)
}
func finalizePkgCatalogerResults(cfg CatalogingFactoryConfig, resolver file.PathResolver, catalogerName string, pkgs []pkg.Package, relationships []artifact.Relationship) ([]pkg.Package, []artifact.Relationship) {
for i, p := range pkgs {
if p.FoundBy == "" {
p.FoundBy = catalogerName
}
if cfg.DataGenerationConfig.GenerateCPEs && !hasAuthoritativeCPE(p.CPEs) {
// generate CPEs (note: this is excluded from package ID, so is safe to mutate)
// we might have binary classified CPE already with the package so we want to append here
dictionaryCPEs, ok := cpeutils.DictionaryFind(p)
if ok {
log.Tracef("used CPE dictionary to find CPEs for %s package %q: %s", p.Type, p.Name, dictionaryCPEs)
p.CPEs = append(p.CPEs, dictionaryCPEs...)
} else {
p.CPEs = append(p.CPEs, cpeutils.Generate(p)...)
}
}
// if we were not able to identify the language we have an opportunity
// to try and get this value from the PURL. Worst case we assert that
// we could not identify the language at either stage and set UnknownLanguage
if p.Language == "" {
p.Language = pkg.LanguageFromPURL(p.PURL)
}
if cfg.RelationshipsConfig.PackageFileOwnership {
// create file-to-package relationships for files owned by the package
owningRelationships, err := packageFileOwnershipRelationships(p, resolver)
if err != nil {
log.Warnf("unable to create any package-file relationships for package name=%q type=%q: %v", p.Name, p.Type, err)
} else {
relationships = append(relationships, owningRelationships...)
}
}
pkgs[i] = p
}
return pkgs, relationships
}
type packageReplacement struct {
original artifact.ID
pkg pkg.Package
}
func applyCompliance(cfg cataloging.ComplianceConfig, pkgs []pkg.Package, relationships []artifact.Relationship) ([]pkg.Package, []artifact.Relationship) {
remainingPkgs, droppedPkgs, replacements := filterNonCompliantPackages(pkgs, cfg)
relIdx := relationship.NewIndex(relationships...)
for _, p := range droppedPkgs {
relIdx.Remove(p.ID())
}
for _, replacement := range replacements {
relIdx.Replace(replacement.original, replacement.pkg)
}
return remainingPkgs, relIdx.All()
}
func filterNonCompliantPackages(pkgs []pkg.Package, cfg cataloging.ComplianceConfig) ([]pkg.Package, []pkg.Package, []packageReplacement) {
var remainingPkgs, droppedPkgs []pkg.Package
var replacements []packageReplacement
for _, p := range pkgs {
keep, replacement := applyComplianceRules(&p, cfg)
if keep {
remainingPkgs = append(remainingPkgs, p)
} else {
droppedPkgs = append(droppedPkgs, p)
}
if replacement != nil {
replacements = append(replacements, *replacement)
}
}
return remainingPkgs, droppedPkgs, replacements
}
func applyComplianceRules(p *pkg.Package, cfg cataloging.ComplianceConfig) (bool, *packageReplacement) {
var drop bool
var replacement *packageReplacement
applyComplianceRule := func(value, fieldName string, action cataloging.ComplianceAction) bool {
if strings.TrimSpace(value) != "" {
return false
}
loc := "unknown"
locs := p.Locations.ToSlice()
if len(locs) > 0 {
loc = locs[0].Path()
}
switch action {
case cataloging.ComplianceActionDrop:
log.WithFields("pkg", p.String(), "location", loc).Debugf("package with missing %s, dropping", fieldName)
drop = true
case cataloging.ComplianceActionStub:
log.WithFields("pkg", p.String(), "location", loc).Debugf("package with missing %s, stubbing with default value", fieldName)
return true
case cataloging.ComplianceActionKeep:
log.WithFields("pkg", p.String(), "location", loc).Tracef("package with missing %s, taking no action", fieldName)
}
return false
}
ogID := p.ID()
if applyComplianceRule(p.Name, "name", cfg.MissingName) {
p.Name = cataloging.UnknownStubValue
p.SetID()
}
if applyComplianceRule(p.Version, "version", cfg.MissingVersion) {
p.Version = cataloging.UnknownStubValue
p.SetID()
}
newID := p.ID()
if newID != ogID {
replacement = &packageReplacement{
original: ogID,
pkg: *p,
}
}
return !drop, replacement
}
func hasAuthoritativeCPE(cpes []cpe.CPE) bool {
for _, c := range cpes {
if c.Source != cpe.GeneratedSource {
@@ -4,8 +4,13 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
func Test_hasAuthoritativeCPE(t *testing.T) {
@@ -53,3 +58,83 @@ func Test_hasAuthoritativeCPE(t *testing.T) {
})
}
}
func TestApplyCompliance(t *testing.T) {
p1 := pkg.Package{Name: "pkg-1", Version: "1.0"}
p2 := pkg.Package{Name: "", Version: "1.0"} // missing name
p3 := pkg.Package{Name: "pkg-3", Version: ""} // missing version
p4 := pkg.Package{Name: "pkg-4", Version: ""} // missing version
c1 := file.Coordinates{RealPath: "/coords/1"}
c2 := file.Coordinates{RealPath: "/coords/2"}
for _, p := range []*pkg.Package{&p1, &p2, &p3, &p4} {
p.SetID()
}
r1 := artifact.Relationship{
From: p1,
To: c1,
Type: artifact.ContainsRelationship,
}
r2 := artifact.Relationship{
From: p2,
To: c2,
Type: artifact.ContainsRelationship,
}
cfg := cataloging.ComplianceConfig{
MissingName: cataloging.ComplianceActionDrop,
MissingVersion: cataloging.ComplianceActionStub,
}
remainingPkgs, remainingRels := applyCompliance(cfg, []pkg.Package{p1, p2, p3, p4}, []artifact.Relationship{r1, r2})
// p2 should be dropped because it has a missing name, p3 and p4 should pass with a warning for the missing version
assert.Len(t, remainingPkgs, 3) // p1, p3, p4 should remain
assert.Len(t, remainingRels, 1) // only r1 should remain (relationship involving p1)
}
func TestFilterNonCompliantPackages(t *testing.T) {
p1 := pkg.Package{Name: "pkg-1", Version: "1.0"}
p2 := pkg.Package{Name: "", Version: "1.0"} // missing name
p3 := pkg.Package{Name: "pkg-3", Version: ""} // missing version
for _, p := range []*pkg.Package{&p1, &p2, &p3} {
p.SetID()
}
cfg := cataloging.ComplianceConfig{
MissingName: cataloging.ComplianceActionDrop,
MissingVersion: cataloging.ComplianceActionKeep,
}
remainingPkgs, droppedPkgs, replacement := filterNonCompliantPackages([]pkg.Package{p1, p2, p3}, cfg)
require.Nil(t, replacement)
// p2 should be dropped because it has a missing name
assert.Len(t, remainingPkgs, 2)
assert.Len(t, droppedPkgs, 1)
assert.Equal(t, p2, droppedPkgs[0])
}
func TestApplyComplianceRules_DropAndStub(t *testing.T) {
p := pkg.Package{Name: "", Version: ""}
p.SetID()
ogID := p.ID()
cfg := cataloging.ComplianceConfig{
MissingName: cataloging.ComplianceActionDrop,
MissingVersion: cataloging.ComplianceActionStub,
}
isCompliant, replacement := applyComplianceRules(&p, cfg)
require.NotNil(t, replacement)
assert.Equal(t, packageReplacement{
original: ogID,
pkg: p,
}, *replacement)
// the package should be dropped due to missing name (drop action) and its version should be stubbed
assert.False(t, isCompliant)
assert.Equal(t, cataloging.UnknownStubValue, p.Version)
}
+24 -8
View File
@@ -34,6 +34,21 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/wordpress"
)
const (
// Java ecosystem labels
Java = "java"
Maven = "maven"
// Go ecosystem labels
Go = "go"
Golang = "golang"
// JavaScript ecosystem labels
JavaScript = "javascript"
Node = "node"
NPM = "npm"
)
//nolint:funlen
func DefaultPackageTaskFactories() PackageTaskFactories {
return []packageTaskFactory{
@@ -49,7 +64,7 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
// language-specific package installed catalogers ///////////////////////////////////////////////////////////////////////////
newSimplePackageTaskFactory(cpp.NewConanInfoCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "cpp", "conan"),
newSimplePackageTaskFactory(javascript.NewPackageCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "javascript", "node"),
newSimplePackageTaskFactory(javascript.NewPackageCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, JavaScript, Node),
newSimplePackageTaskFactory(php.NewComposerInstalledCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "php", "composer"),
newSimplePackageTaskFactory(r.NewPackageCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "r"),
newSimplePackageTaskFactory(ruby.NewInstalledGemSpecCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "ruby", "gem", "gemspec"),
@@ -67,20 +82,20 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return golang.NewGoModuleFileCataloger(cfg.PackagesConfig.Golang)
},
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "go", "golang", "gomod",
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, Go, Golang, "gomod",
),
newSimplePackageTaskFactory(java.NewGradleLockfileCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "java", "gradle"),
newSimplePackageTaskFactory(java.NewGradleLockfileCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, Java, "gradle"),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return java.NewPomCataloger(cfg.PackagesConfig.JavaArchive)
},
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "java", "maven",
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, Java, Maven,
),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return javascript.NewLockCataloger(cfg.PackagesConfig.JavaScript)
},
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "javascript", "node", "npm",
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, JavaScript, Node, NPM,
),
newSimplePackageTaskFactory(php.NewComposerLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "php", "composer"),
newSimplePackageTaskFactory(php.NewPeclCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, pkgcataloging.ImageTag, "php", "pecl"),
@@ -105,15 +120,15 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return golang.NewGoModuleBinaryCataloger(cfg.PackagesConfig.Golang)
},
pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "go", "golang", "gomod", "binary",
pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, Go, Golang, "gomod", "binary",
),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return java.NewArchiveCataloger(cfg.PackagesConfig.JavaArchive)
},
pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "java", "maven",
pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, Java, Maven,
),
newSimplePackageTaskFactory(java.NewNativeImageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "java"),
newSimplePackageTaskFactory(java.NewNativeImageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, Java),
newSimplePackageTaskFactory(nix.NewStoreCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "nix"),
newSimplePackageTaskFactory(lua.NewPackageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "lua"),
@@ -127,6 +142,7 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
newSimplePackageTaskFactory(binary.NewELFPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "elf-package"),
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
newSimplePackageTaskFactory(java.NewJvmDistributionCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "java", "jvm", "jdk", "jre"),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return kernel.NewLinuxKernelCataloger(cfg.PackagesConfig.LinuxKernel)
File diff suppressed because it is too large Load Diff
+93 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "anchore.io/schema/syft/json/16.0.16/document",
"$id": "anchore.io/schema/syft/json/16.0.17/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@@ -955,6 +955,24 @@
"virtualPath"
]
},
"JavaJvmInstallation": {
"properties": {
"release": {
"$ref": "#/$defs/JavaVMRelease"
},
"files": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
"required": [
"release",
"files"
]
},
"JavaManifest": {
"properties": {
"main": {
@@ -1062,6 +1080,77 @@
"version"
]
},
"JavaVMRelease": {
"properties": {
"implementor": {
"type": "string"
},
"implementorVersion": {
"type": "string"
},
"javaRuntimeVersion": {
"type": "string"
},
"javaVersion": {
"type": "string"
},
"javaVersionDate": {
"type": "string"
},
"libc": {
"type": "string"
},
"modules": {
"items": {
"type": "string"
},
"type": "array"
},
"osArch": {
"type": "string"
},
"osName": {
"type": "string"
},
"osVersion": {
"type": "string"
},
"source": {
"type": "string"
},
"buildSource": {
"type": "string"
},
"buildSourceRepo": {
"type": "string"
},
"sourceRepo": {
"type": "string"
},
"fullVersion": {
"type": "string"
},
"semanticVersion": {
"type": "string"
},
"buildInfo": {
"type": "string"
},
"jvmVariant": {
"type": "string"
},
"jvmVersion": {
"type": "string"
},
"imageType": {
"type": "string"
},
"buildType": {
"type": "string"
}
},
"type": "object"
},
"JavascriptNpmPackage": {
"properties": {
"name": {
@@ -1583,6 +1672,9 @@
{
"$ref": "#/$defs/JavaArchive"
},
{
"$ref": "#/$defs/JavaJvmInstallation"
},
{
"$ref": "#/$defs/JavascriptNpmPackage"
},
+47
View File
@@ -0,0 +1,47 @@
package cataloging
import (
"strings"
)
const (
ComplianceActionKeep ComplianceAction = "keep"
ComplianceActionDrop ComplianceAction = "drop"
ComplianceActionStub ComplianceAction = "stub"
)
const UnknownStubValue = "UNKNOWN"
type ComplianceAction string
type ComplianceConfig struct {
MissingName ComplianceAction `yaml:"missing-name" json:"missing-name" mapstructure:"missing-name"`
MissingVersion ComplianceAction `yaml:"missing-version" json:"missing-version" mapstructure:"missing-version"`
}
func DefaultComplianceConfig() ComplianceConfig {
// Note: name and version are required minimum SBOM elements by NTIA, thus should be the API default
return ComplianceConfig{
MissingName: ComplianceActionDrop,
MissingVersion: ComplianceActionStub,
}
}
func (c ComplianceConfig) Parse() ComplianceConfig {
return ComplianceConfig{
MissingName: c.MissingName.Parse(),
MissingVersion: c.MissingVersion.Parse(),
}
}
func (c ComplianceAction) Parse() ComplianceAction {
switch strings.ToLower(string(c)) {
case string(ComplianceActionKeep), "include":
return ComplianceActionKeep
case string(ComplianceActionDrop), "exclude":
return ComplianceActionDrop
case string(ComplianceActionStub), "replace":
return ComplianceActionStub
}
return ComplianceActionKeep
}
+9
View File
@@ -19,6 +19,7 @@ import (
// CreateSBOMConfig specifies all parameters needed for creating an SBOM.
type CreateSBOMConfig struct {
// required configuration input to specify how cataloging should be performed
Compliance cataloging.ComplianceConfig
Search cataloging.SearchConfig
Relationships cataloging.RelationshipsConfig
DataGeneration cataloging.DataGenerationConfig
@@ -38,6 +39,7 @@ type CreateSBOMConfig struct {
func DefaultCreateSBOMConfig() *CreateSBOMConfig {
return &CreateSBOMConfig{
Compliance: cataloging.DefaultComplianceConfig(),
Search: cataloging.DefaultSearchConfig(),
Relationships: cataloging.DefaultRelationshipsConfig(),
DataGeneration: cataloging.DefaultDataGenerationConfig(),
@@ -93,6 +95,12 @@ func (c *CreateSBOMConfig) WithParallelism(p int) *CreateSBOMConfig {
return c
}
// WithComplianceConfig allows for setting the specific compliance configuration for cataloging.
func (c *CreateSBOMConfig) WithComplianceConfig(cfg cataloging.ComplianceConfig) *CreateSBOMConfig {
c.Compliance = cfg
return c
}
// WithSearchConfig allows for setting the specific search configuration for cataloging.
func (c *CreateSBOMConfig) WithSearchConfig(cfg cataloging.SearchConfig) *CreateSBOMConfig {
c.Search = cfg
@@ -225,6 +233,7 @@ func (c *CreateSBOMConfig) packageTasks(src source.Description) ([]task.Task, *t
RelationshipsConfig: c.Relationships,
DataGenerationConfig: c.DataGeneration,
PackagesConfig: c.Packages,
ComplianceConfig: c.Compliance,
}
persistentTasks, selectableTasks, err := c.allPackageTasks(cfg)
+1 -1
View File
@@ -175,7 +175,7 @@ func hasElfDynTag(f *elf.File, tag elf.DynTag) bool {
t = elf.DynTag(f.ByteOrder.Uint32(d[0:4]))
d = d[8:]
case elf.ELFCLASS64:
t = elf.DynTag(f.ByteOrder.Uint64(d[0:8])) //nolint:gosec
t = elf.DynTag(f.ByteOrder.Uint64(d[0:8]))
d = d[16:]
}
if t == tag {
@@ -62,6 +62,10 @@ func Originator(p pkg.Package) (typ string, author string) { //nolint: funlen
}
}
case pkg.JavaVMInstallation:
typ = orgType
author = metadata.Release.Implementor
case pkg.LinuxKernelModule:
author = metadata.Author
@@ -178,6 +178,18 @@ func Test_OriginatorSupplier(t *testing.T) {
},
// note: empty!
},
{
name: "from java -- jvm installation",
input: pkg.Package{
Metadata: pkg.JavaVMInstallation{
Release: pkg.JavaVMRelease{
Implementor: "Oracle",
},
},
},
originator: "Organization: Oracle",
supplier: "Organization: Oracle",
},
{
name: "from linux kernel module",
input: pkg.Package{
+1 -1
View File
@@ -146,7 +146,7 @@ func safeFileModeConvert(val int) (fs.FileMode, error) {
if err != nil {
return 0, err
}
return os.FileMode(mode), nil //nolint:gosec
return os.FileMode(mode), nil
}
func toSyftLicenses(m []model.License) (p []pkg.License) {
@@ -140,6 +140,40 @@ func (r ChrootContext) ToNativePath(chrootPath string) (string, error) {
return responsePath, nil
}
func (r ChrootContext) ToNativeGlob(chrootPath string) (string, error) {
// split on any *
parts := strings.Split(chrootPath, "*")
if len(parts) == 0 || parts[0] == "" {
// either this is an empty string or a path that starts with * so there is nothing we can do
return chrootPath, nil
}
if len(parts) == 1 {
// this has no glob, treat it like a path
return r.ToNativePath(chrootPath)
}
responsePath := parts[0]
if filepath.IsAbs(responsePath) {
// don't allow input to potentially hop above root path
responsePath = path.Join(r.root, responsePath)
} else {
// ensure we take into account any relative difference between the root path and the CWD for relative requests
responsePath = path.Join(r.cwdRelativeToRoot, responsePath)
}
var err error
responsePath, err = filepath.Abs(responsePath)
if err != nil {
return "", err
}
parts[0] = strings.TrimRight(responsePath, "/") + "/"
return strings.Join(parts, "*"), nil
}
// ToChrootPath takes a path from the underlying fs domain and converts it to a path that is relative to the current root context.
func (r ChrootContext) ToChrootPath(nativePath string) string {
responsePath := nativePath
@@ -479,3 +479,98 @@ func Test_ChrootContext_RequestResponse(t *testing.T) {
})
}
}
func TestToNativeGlob(t *testing.T) {
tests := []struct {
name string
chrootContext ChrootContext
chrootPath string
expectedResult string
expectedError error
}{
{
name: "ignore empty path",
chrootContext: ChrootContext{
root: "/root",
cwdRelativeToRoot: "/cwd",
},
chrootPath: "",
expectedResult: "",
expectedError: nil,
},
{
name: "ignore if just a path",
chrootContext: ChrootContext{
root: "/root",
cwdRelativeToRoot: "/cwd",
},
chrootPath: "/some/path/file.txt",
expectedResult: "/root/some/path/file.txt",
expectedError: nil,
},
{
name: "ignore starting with glob",
chrootContext: ChrootContext{
root: "/root",
cwdRelativeToRoot: "/cwd",
},
chrootPath: "*/relative/path/*",
expectedResult: "*/relative/path/*",
expectedError: nil,
},
{
name: "absolute path with glob",
chrootContext: ChrootContext{
root: "/root",
cwdRelativeToRoot: "/cwd",
},
chrootPath: "/some/path/*",
expectedResult: "/root/some/path/*",
expectedError: nil,
},
{
name: "relative path with glob",
chrootContext: ChrootContext{
root: "/root",
cwdRelativeToRoot: "/cwd",
},
chrootPath: "relative/path/*",
expectedResult: "/cwd/relative/path/*",
expectedError: nil,
},
{
name: "relative path with no root",
chrootContext: ChrootContext{
root: "",
cwdRelativeToRoot: "/cwd",
},
chrootPath: "relative/path/*",
expectedResult: "/cwd/relative/path/*",
expectedError: nil,
},
{
name: "globs everywhere",
chrootContext: ChrootContext{
root: "/root",
cwdRelativeToRoot: "/cwd",
},
chrootPath: "relative/path/**/file*.txt",
expectedResult: "/cwd/relative/path/**/file*.txt",
expectedError: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.chrootContext.ToNativeGlob(tt.chrootPath)
if tt.expectedError != nil {
assert.Error(t, err)
assert.Equal(t, tt.expectedError, err)
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expectedResult, result)
}
})
}
}
+9 -1
View File
@@ -145,13 +145,21 @@ func (r Directory) FilesByPath(userPaths ...string) ([]file.Location, error) {
return references, nil
}
func (r Directory) requestGlob(pattern string) (string, error) {
return r.chroot.ToNativeGlob(pattern)
}
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
func (r Directory) FilesByGlob(patterns ...string) ([]file.Location, error) {
uniqueFileIDs := stereoscopeFile.NewFileReferenceSet()
uniqueLocations := make([]file.Location, 0)
for _, pattern := range patterns {
refVias, err := r.searchContext.SearchByGlob(pattern, filetree.FollowBasenameLinks)
requestGlob, err := r.requestGlob(pattern)
if err != nil {
return nil, err
}
refVias, err := r.searchContext.SearchByGlob(requestGlob, filetree.FollowBasenameLinks)
if err != nil {
return nil, err
}
@@ -27,6 +27,7 @@ func AllTypes() []any {
pkg.HackageStackYamlEntry{},
pkg.HackageStackYamlLockEntry{},
pkg.JavaArchive{},
pkg.JavaVMInstallation{},
pkg.LinuxKernel{},
pkg.LinuxKernelModule{},
pkg.LuaRocksPackage{},
+1
View File
@@ -81,6 +81,7 @@ var jsonTypes = makeJSONTypes(
jsonNames(pkg.HackageStackYamlLockEntry{}, "haskell-hackage-stack-lock-entry", "HackageMetadataType"),
jsonNamesWithoutLookup(pkg.HackageStackYamlEntry{}, "haskell-hackage-stack-entry", "HackageMetadataType"), // the legacy value is split into two types, where the other is preferred
jsonNames(pkg.JavaArchive{}, "java-archive", "JavaMetadata"),
jsonNames(pkg.JavaVMInstallation{}, "java-jvm-installation"),
jsonNames(pkg.MicrosoftKbPatch{}, "microsoft-kb-patch", "KbPatchMetadata"),
jsonNames(pkg.LinuxKernel{}, "linux-kernel-archive", "LinuxKernel"),
jsonNames(pkg.LinuxKernelModule{}, "linux-kernel-module", "LinuxKernelModule"),
@@ -961,6 +961,28 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("swipl-binary"),
},
},
{
logicalFixture: "dart/3.5.2/linux-amd64",
expected: pkg.Package{
Name: "dart",
Version: "3.5.2",
Type: "binary",
PURL: "pkg:generic/dart@3.5.2",
Locations: locations("dart"),
Metadata: metadata("dart-binary"),
},
},
{
logicalFixture: "dart/3.6.0-216.1.beta/linux-amd64",
expected: pkg.Package{
Name: "dart",
Version: "3.6.0-216.1.beta",
Type: "binary",
PURL: "pkg:generic/dart@3.6.0-216.1.beta",
Locations: locations("dart"),
Metadata: metadata("dart-binary"),
},
},
{
logicalFixture: "haskell-ghc/9.6.5/linux-amd64",
expected: pkg.Package{
@@ -1082,6 +1104,94 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("wordpress-cli-binary"),
},
},
{
logicalFixture: "lighttpd/1.4.76/linux-amd64",
expected: pkg.Package{
Name: "lighttpd",
Version: "1.4.76",
Type: "binary",
PURL: "pkg:generic/lighttpd@1.4.76",
Locations: locations("lighttpd"),
Metadata: metadata("lighttpd-binary"),
},
},
{
logicalFixture: "proftpd/1.3.8b/linux-amd64",
expected: pkg.Package{
Name: "proftpd",
Version: "1.3.8b",
Type: "binary",
PURL: "pkg:generic/proftpd@1.3.8b",
Locations: locations("proftpd"),
Metadata: metadata("proftpd-binary"),
},
},
{
logicalFixture: "zstd/1.5.6/linux-amd64",
expected: pkg.Package{
Name: "zstd",
Version: "1.5.6",
Type: "binary",
PURL: "pkg:generic/zstd@1.5.6",
Locations: locations("zstd"),
Metadata: metadata("zstd-binary"),
},
},
{
logicalFixture: "zstd/1.5.6/linux-amd64",
expected: pkg.Package{
Name: "zstd",
Version: "1.5.6",
Type: "binary",
PURL: "pkg:generic/zstd@1.5.6",
Locations: locations("zstd"),
Metadata: metadata("zstd-binary"),
},
},
{
logicalFixture: "xz/5.6.2/linux-amd64",
expected: pkg.Package{
Name: "xz",
Version: "5.6.2",
Type: "binary",
PURL: "pkg:generic/xz@5.6.2",
Locations: locations("xz"),
Metadata: metadata("xz-binary"),
},
},
{
logicalFixture: "gzip/1.12/linux-amd64",
expected: pkg.Package{
Name: "gzip",
Version: "1.12",
Type: "binary",
PURL: "pkg:generic/gzip@1.12",
Locations: locations("gzip"),
Metadata: metadata("gzip-binary"),
},
},
{
logicalFixture: "sqlcipher/4.5.5/linux-amd64",
expected: pkg.Package{
Name: "sqlcipher",
Version: "4.5.5",
Type: "binary",
PURL: "pkg:generic/sqlcipher@4.5.5",
Locations: locations("sqlcipher"),
Metadata: metadata("sqlcipher-binary"),
},
},
{
logicalFixture: "jq/1.7.1/linux-amd64",
expected: pkg.Package{
Name: "jq",
Version: "1.7.1",
Type: "binary",
PURL: "pkg:generic/jq@1.7.1",
Locations: locations("jq"),
Metadata: metadata("jq-binary"),
},
},
}
for _, test := range tests {
+80
View File
@@ -447,6 +447,16 @@ func DefaultClassifiers() []Classifier {
PURL: mustPURL("pkg:generic/swipl@version"),
CPEs: singleCPE("cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "dart-binary",
FileGlob: "**/dart",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)Dart,GC"\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+(\.[0-9]+)?\.beta)?) `,
),
Package: "dart",
PURL: mustPURL("pkg:generic/dart@version"),
CPEs: singleCPE("cpe:2.3:a:dart:dart_software_development_kit:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "haskell-ghc-binary",
FileGlob: "**/ghc*",
@@ -574,6 +584,76 @@ func DefaultClassifiers() []Classifier {
PURL: mustPURL("pkg:generic/curl@version"),
CPEs: singleCPE("cpe:2.3:a:haxx:curl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "lighttpd-binary",
FileGlob: "**/lighttpd",
EvidenceMatcher: FileContentsVersionMatcher(
`\x00lighttpd/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`,
),
Package: "lighttpd",
PURL: mustPURL("pkg:generic/lighttpd@version"),
CPEs: singleCPE("cpe:2.3:a:lighttpd:lighttpd:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "proftpd-binary",
FileGlob: "**/proftpd",
EvidenceMatcher: FileContentsVersionMatcher(
`\x00ProFTPD Version (?P<version>[0-9]+\.[0-9]+\.[0-9]+[a-z]?)\x00`,
),
Package: "proftpd",
PURL: mustPURL("pkg:generic/proftpd@version"),
CPEs: singleCPE("cpe:2.3:a:proftpd:proftpd:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "zstd-binary",
FileGlob: "**/zstd",
EvidenceMatcher: FileContentsVersionMatcher(
`\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`,
),
Package: "zstd",
PURL: mustPURL("pkg:generic/zstd@version"),
CPEs: singleCPE("cpe:2.3:a:facebook:zstandard:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "xz-binary",
FileGlob: "**/xz",
EvidenceMatcher: FileContentsVersionMatcher(
`\x00xz \(XZ Utils\) (?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`,
),
Package: "xz",
PURL: mustPURL("pkg:generic/xz@version"),
CPEs: singleCPE("cpe:2.3:a:tukaani:xz:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "gzip-binary",
FileGlob: "**/gzip",
EvidenceMatcher: FileContentsVersionMatcher(
`\x00(?P<version>[0-9]+\.[0-9]+)\x00`,
),
Package: "gzip",
PURL: mustPURL("pkg:generic/gzip@version"),
CPEs: singleCPE("cpe:2.3:a:gnu:gzip:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "sqlcipher-binary",
FileGlob: "**/sqlcipher",
EvidenceMatcher: FileContentsVersionMatcher(
`[^0-9]\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`,
),
Package: "sqlcipher",
PURL: mustPURL("pkg:generic/sqlcipher@version"),
CPEs: singleCPE("cpe:2.3:a:zetetic:sqlcipher:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "jq-binary",
FileGlob: "**/jq",
EvidenceMatcher: FileContentsVersionMatcher(
`\x00(?P<version>[0-9]{1,3}\.[0-9]{1,3}(\.[0-9]+)?)\x00`,
),
Package: "jq",
PURL: mustPURL("pkg:generic/jq@version"),
CPEs: singleCPE("cpe:2.3:a:jqlang:jq:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
}
}
@@ -69,6 +69,11 @@ if ! command -v xxd &> /dev/null; then
exit 1
fi
# check if xargs is even installed
if ! command -v xargs &> /dev/null; then
echo "xargs not found. Please install xargs."
exit 1
fi
PATTERN=${SEARCH_FOR:-$VERSION}
@@ -116,7 +121,7 @@ while $CONTINUE_LOOP; do
fi
# search for the pattern in the binary file and capture the offset
OFFSET=$(echo "${SELECTED_RESULT}" | cut -d ' ' -f 1)
OFFSET=$(echo "${SELECTED_RESULT}" | xargs | cut -d ' ' -f 1)
if [ -z "$OFFSET" ]; then
echo "Pattern not found."
@@ -78,6 +78,20 @@ from-images:
paths:
- /usr/lib/swipl/bin/x86_64-linux/swipl
- version: 3.5.2
images:
- ref: dart:3.5.2@sha256:1fd62cb5036bdc42de89bdae747683277986639be9b0b0a0751d2c50bbd9441f
platform: linux/amd64
paths:
- /usr/lib/dart/bin/dart
- version: 3.6.0-216.1.beta
images:
- ref: dart:3.6.0-216.1.beta@sha256:f4211fec53f972987f5bc1dd0ffd3d3cb1fd820ac1ade4e45a7a0c7bffd71d58
platform: linux/amd64
paths:
- /usr/lib/dart/bin/dart
- name: haskell-ghc
version: 9.6.5
images:
@@ -610,3 +624,58 @@ from-images:
paths:
- /usr/bin/curl
- name: lighttpd
version: 1.4.76
images:
- ref: jitesoft/lighttpd:1.4.76-cgi@sha256:f5d4500bfb992a20ca39369ae1ca1d8a7a9463bb8c59ee8dd85ddb6d96fc9fc1
platform: linux/amd64
paths:
- /usr/local/sbin/lighttpd
- name: proftpd
version: 1.3.8b
images:
- ref: mekayelanik/proftpd-server-alpine:1.3.8b-r2@sha256:a1ef73a2de04999e53bf728b548ef9922febab8f5709037e40e0141cedcd66db
platform: linux/amd64
paths:
- /usr/sbin/proftpd
- name: zstd
version: 1.5.6
images:
- ref: danysk/zstd:1.5.6@sha256:5eceba085b3a399592755dd66a37b8adfb83538af3f56b51bec6e6cc955e3b5f
platform: linux/amd64
paths:
- /usr/local/bin/zstd
- name: xz
version: 5.6.2
images:
- ref: docker:27.2.1@sha256:c51fa20028ff6590588d9ed97d3b16865d503a3d7228aa885871c5c292afa5ca
platform: linux/amd64
paths:
- /usr/bin/xz
- name: gzip
version: 1.12
images:
- ref: ubuntu:24.04@sha256:d35dfc2fe3ef66bcc085ca00d3152b482e6cafb23cdda1864154caf3b19094ba
platform: linux/amd64
paths:
- /usr/bin/gzip
- name: sqlcipher
version: 4.5.5
images:
- ref: yspreen/sqlcipher@sha256:93189cc465661f16ad23f3ace4206179bdd19967deaf08c54da5ac1e34bb6fb7
platform: linux/amd64
paths:
- /usr/local/bin/sqlcipher
- name: jq
version: 1.7.1
images:
- ref: efrecon/jq:1.7.1@sha256:0ad05e2e6d1dea5fe0852ecc23114eb768d60c4ce0985d729eb958809e7f31dd
platform: linux/amd64
paths:
- /usr/local/bin/jq
+1 -1
View File
@@ -230,7 +230,7 @@ func handleNewKeyValue(line string) (key string, val interface{}, err error) {
if err != nil {
return "", nil, fmt.Errorf("bad installed-size value=%q: %w", val, err)
}
return key, int(s), nil //nolint:gosec
return key, int(s), nil
default:
return key, val, nil
}
@@ -4201,6 +4201,9 @@
"node-static": [
"cpe:2.3:a:node-static_project:node-static:*:*:*:*:*:node.js:*:*"
],
"node-stringbuilder": [
"cpe:2.3:a:magiclen:stringbuilder:*:*:*:*:*:node.js:*:*"
],
"node-thulac": [
"cpe:2.3:a:geohey:node-thulac:*:*:*:*:*:node.js:*:*"
],
@@ -6615,6 +6618,9 @@
"heapless": [
"cpe:2.3:a:heapless_project:heapless:*:*:*:*:*:rust:*:*"
],
"ic_cdk": [
"cpe:2.3:a:dfinity:canister_developer_kit_for_the_internet_computer:*:*:*:*:*:rust:*:*"
],
"iced-x86": [
"cpe:2.3:a:iced-x86_project:iced-x86:*:*:*:*:*:rust:*:*"
],
@@ -7880,6 +7886,9 @@
"awin-data-feed": [
"cpe:2.3:a:awin:awin_data_feed:*:*:*:*:*:wordpress:*:*"
],
"awsm-team": [
"cpe:2.3:a:awsm:awsm_team:*:*:*:*:free:wordpress:*:*"
],
"ays-facebook-popup-likebox": [
"cpe:2.3:a:ays-pro:popup_box:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:aysproextensions:popup_like:*:*:*:*:*:wordpress:*:*"
@@ -8016,7 +8025,7 @@
"cpe:2.3:a:bertha:bertha_ai:*:*:*:*:*:wordpress:*:*"
],
"best-restaurant-menu-by-pricelisto": [
"cpe:2.3:a:pricelisto:best_restaurant_menu_by_pricelisto:*:*:*:*:*:wordpress:*:*"
"cpe:2.3:a:pricelisto:great_restaurant_menu_wp:*:*:*:*:*:wordpress:*:*"
],
"bestbooks": [
"cpe:2.3:a:presspage:bestbooks:*:*:*:*:*:wordpress:*:*"
@@ -9480,6 +9489,9 @@
"dark-mode": [
"cpe:2.3:a:dark_mode_project:dark_mode:*:*:*:*:*:wordpress:*:*"
],
"dark-mode-for-wp-dashboard": [
"cpe:2.3:a:naiches:dark_mode_for_wp_dashboard:*:*:*:*:*:wordpress:*:*"
],
"dashboard-to-do-list": [
"cpe:2.3:a:arwebdesign:dashboard_to-do_list:*:*:*:*:*:wordpress:*:*"
],
@@ -9842,6 +9854,9 @@
"easy-custom-auto-excerpt": [
"cpe:2.3:a:tonjoostudio:easy_custom_auto_excerpt:*:*:*:*:*:wordpress:*:*"
],
"easy-custom-code": [
"cpe:2.3:a:web357:easy_custom_code:*:*:*:*:*:wordpress:*:*"
],
"easy-digital-downloads": [
"cpe:2.3:a:sandhillsdev:easy_digital_downloads:*:*:*:*:*:wordpress:*:*"
],
@@ -10603,6 +10618,9 @@
"floating-social-media-icon": [
"cpe:2.3:a:acurax:floating_social_media_icon:*:*:*:*:*:wordpress:*:*"
],
"floating-social-media-links": [
"cpe:2.3:a:celloexpressions:floating_social_media_links:*:*:*:*:*:wordpress:*:*"
],
"flog": [
"cpe:2.3:a:flog_project:flog:*:*:*:*:*:*:*:*"
],
@@ -10718,7 +10736,7 @@
"cpe:2.3:a:formilla:live_chat:*:*:*:*:*:wordpress:*:*"
],
"forminator": [
"cpe:2.3:a:incsub:forminator:*:*:*:*:*:wordpress:*:*"
"cpe:2.3:a:incsub:forminator:*:*:*:*:free:wordpress:*:*"
],
"formlift": [
"cpe:2.3:a:formlift:formlift_for_infusionsoft_web_forms:*:*:*:*:*:wordpress:*:*"
@@ -11226,6 +11244,9 @@
"gutenslider": [
"cpe:2.3:a:gutenslider:gutenslider:*:*:*:*:*:wordpress:*:*"
],
"gutentor": [
"cpe:2.3:a:gutentor:gutentor:*:*:*:*:*:wordpress:*:*"
],
"gutenverse": [
"cpe:2.3:a:jegstudio:gutenverse:*:*:*:*:*:wordpress:*:*"
],
@@ -11803,6 +11824,9 @@
"jeg-elementor-kit": [
"cpe:2.3:a:jegtheme:jeg_elementor_kit:*:*:*:*:*:wordpress:*:*"
],
"jet-elements": [
"cpe:2.3:a:crocoblock:jetelements:*:*:*:*:*:wordpress:*:*"
],
"jetformbuilder": [
"cpe:2.3:a:crocoblock:jetformbuilder:*:*:*:*:*:wordpress:*:*"
],
@@ -12371,6 +12395,9 @@
"make-paths-relative": [
"cpe:2.3:a:yasglobal:make_paths_relative:*:*:*:*:*:wordpress:*:*"
],
"makecommerce": [
"cpe:2.3:a:makecommerce:makecommerce_for_woocommerce:*:*:*:*:*:wordpress:*:*"
],
"makestories-helper": [
"cpe:2.3:a:makestories:makestories_\\(for_google_web_stories\\):*:*:*:*:*:wordpress:*:*"
],
@@ -14314,6 +14341,9 @@
"cpe:2.3:a:cyberchimps:gutenberg_\\\u0026_elementor_templates_importer_for_responsive:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:cyberchimps:responsive_addons:*:*:*:*:*:wordpress:*:*"
],
"responsive-block-editor-addons": [
"cpe:2.3:a:cyberchimps:responsive_blocks:*:*:*:*:*:wordpress:*:*"
],
"responsive-column-widgets": [
"cpe:2.3:a:michaeluno:responsive_column_widgets:*:*:*:*:*:wordpress:*:*"
],
@@ -15172,6 +15202,9 @@
"slidedeck2": [
"cpe:2.3:a:hbwsl:slidedeck_2:*:*:*:*:lite:wordpress:*:*"
],
"slider-blocks": [
"cpe:2.3:a:makegutenblock:gutslider:*:*:*:*:*:wordpress:*:*"
],
"slider-hero": [
"cpe:2.3:a:quantumcloud:slider_hero:*:*:*:*:*:wordpress:*:*"
],
@@ -16169,11 +16202,15 @@
"cpe:2.3:a:ultimatesmsnotifications:ultimate_sms_notifications_for_woocommerce:*:*:*:*:*:wordpress:*:*"
],
"ultimate-social-media-icons": [
"cpe:2.3:a:inisev:social_media_share_buttons_\\\u0026_social_sharing_icons:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:ultimatelysocial:social_media_share_buttons_\\\u0026_social_sharing_icons:*:*:*:*:*:wordpress:*:*"
],
"ultimate-social-media-plus": [
"cpe:2.3:a:socialshare:social_share_icons_\\\u0026_social_share_buttons:*:*:*:*:*:wordpress:*:*"
],
"ultimate-store-kit": [
"cpe:2.3:a:bdthemes:ultimate_store_kit:*:*:*:*:free:wordpress:*:*"
],
"ultimate-tables": [
"cpe:2.3:a:webpsilon:ultimate_tables:*:*:*:*:*:wordpress:*:*"
],
@@ -18553,6 +18590,9 @@
"wrc-pricing-tables": [
"cpe:2.3:a:realwebcare:wrc_pricing_tables:*:*:*:*:*:wordpress:*:*"
],
"ws-contact-form": [
"cpe:2.3:a:uusweb:ws_contact_form:*:*:*:*:*:wordpress:*:*"
],
"ws-form": [
"cpe:2.3:a:westguardsolutions:ws_form:*:*:*:*:lite:wordpress:*:*"
],
+6
View File
@@ -43,3 +43,9 @@ func NewGradleLockfileCataloger() pkg.Cataloger {
return generic.NewCataloger("java-gradle-lockfile-cataloger").
WithParserByGlobs(parseGradleLockfile, gradleLockfileGlob)
}
// NewJvmDistributionCataloger returns packages representing JDK/JRE installations (of multiple distribution types).
func NewJvmDistributionCataloger() pkg.Cataloger {
return generic.NewCataloger("java-jvm-cataloger").
WithParserByGlobs(parseJVMRelease, jvmReleaseGlob)
}
+113
View File
@@ -4,6 +4,9 @@ import (
"testing"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
@@ -102,3 +105,113 @@ func Test_POMCataloger_Globs(t *testing.T) {
})
}
}
func TestJvmDistributionCataloger(t *testing.T) {
cases := []struct {
name string
fixture string
expected pkg.Package
}{
{
name: "valid 1.8.0",
fixture: "test-fixtures/jvm-installs/oracle-jdk-se-8",
expected: pkg.Package{
Name: "jdk",
Version: "1.8.0_411-b25",
FoundBy: "java-jvm-cataloger",
Locations: file.NewLocationSet(file.NewLocation("usr/lib/jvm/jdk-1.8-oracle-x64/release")),
Licenses: pkg.NewLicenseSet(),
Type: pkg.BinaryPkg,
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:java_se:1.8.0:update411:*:*:*:*:*:*", cpe.DeclaredSource),
cpe.Must("cpe:2.3:a:oracle:jre:1.8.0:update411:*:*:*:*:*:*", cpe.DeclaredSource),
cpe.Must("cpe:2.3:a:oracle:jdk:1.8.0:update411:*:*:*:*:*:*", cpe.DeclaredSource),
},
PURL: "pkg:generic/oracle/jdk@1.8.0_411-b25",
Metadata: pkg.JavaVMInstallation{
Release: pkg.JavaVMRelease{
JavaRuntimeVersion: "1.8.0_411-b25",
JavaVersion: "1.8.0_411",
OsArch: "amd64",
OsName: "Linux",
OsVersion: "2.6",
Source: ".:git:71ec2089cf8c+",
BuildType: "commercial",
},
Files: []string{
"usr/lib/jvm/jdk-1.8-oracle-x64/bin/javac",
"usr/lib/jvm/jdk-1.8-oracle-x64/release",
},
},
},
},
{
name: "valid post-jep223",
fixture: "test-fixtures/jvm-installs/valid-post-jep223",
expected: pkg.Package{
Name: "openjdk",
Version: "21.0.4+7-LTS",
FoundBy: "java-jvm-cataloger",
Locations: file.NewLocationSet(file.NewLocation("jvm/openjdk/release")),
Licenses: pkg.NewLicenseSet(),
Type: pkg.BinaryPkg,
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:oracle:openjdk:21.0.4:*:*:*:*:*:*:*", cpe.DeclaredSource)},
PURL: "pkg:generic/oracle/openjdk@21.0.4%2B7-LTS?repository_url=https://github.com/adoptium/jdk21u.git",
Metadata: pkg.JavaVMInstallation{
Release: pkg.JavaVMRelease{
Implementor: "Eclipse Adoptium",
ImplementorVersion: "Temurin-21.0.4+7",
JavaRuntimeVersion: "21.0.4+7-LTS",
JavaVersion: "21.0.4",
JavaVersionDate: "2024-07-16",
Libc: "gnu",
Modules: []string{
"java.base", "java.compiler", "java.datatransfer", "java.xml", "java.prefs",
"java.desktop", "java.instrument", "java.logging", "java.management",
"java.security.sasl", "java.naming", "java.rmi", "java.management.rmi",
"java.net.http", "java.scripting", "java.security.jgss",
"java.transaction.xa", "java.sql", "java.sql.rowset", "java.xml.crypto", "java.se",
"java.smartcardio", "jdk.accessibility", "jdk.internal.jvmstat", "jdk.attach",
"jdk.charsets", "jdk.internal.opt", "jdk.zipfs", "jdk.compiler", "jdk.crypto.ec",
"jdk.crypto.cryptoki", "jdk.dynalink", "jdk.internal.ed", "jdk.editpad", "jdk.hotspot.agent",
"jdk.httpserver", "jdk.incubator.vector", "jdk.internal.le", "jdk.internal.vm.ci",
"jdk.internal.vm.compiler", "jdk.internal.vm.compiler.management", "jdk.jartool",
"jdk.javadoc", "jdk.jcmd", "jdk.management", "jdk.management.agent", "jdk.jconsole",
"jdk.jdeps", "jdk.jdwp.agent", "jdk.jdi", "jdk.jfr", "jdk.jlink", "jdk.jpackage", "jdk.jshell",
"jdk.jsobject", "jdk.jstatd", "jdk.localedata", "jdk.management.jfr", "jdk.naming.dns",
"jdk.naming.rmi", "jdk.net", "jdk.nio.mapmode", "jdk.random", "jdk.sctp", "jdk.security.auth",
"jdk.security.jgss", "jdk.unsupported", "jdk.unsupported.desktop", "jdk.xml.dom",
},
OsArch: "aarch64",
OsName: "Linux",
Source: ".:git:13710926b798",
BuildSource: "git:1271f10a26c47e1489a814dd2731f936a588d621",
BuildSourceRepo: "https://github.com/adoptium/temurin-build.git",
SourceRepo: "https://github.com/adoptium/jdk21u.git",
FullVersion: "21.0.4+7-LTS",
SemanticVersion: "21.0.4+7",
BuildInfo: "OS: Linux Version: 5.4.0-150-generic",
JvmVariant: "Hotspot",
JvmVersion: "21.0.4+7-LTS",
ImageType: "JDK",
},
Files: []string{
"jvm/openjdk/release",
"jvm/openjdk/sibling/child/file1.txt",
},
},
},
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
p := tt.expected
p.SetID()
pkgtest.TestCataloger(t, tt.fixture, NewJvmDistributionCataloger(), []pkg.Package{p}, nil)
})
}
}
@@ -268,7 +268,7 @@ func newPE(filename string, r io.ReaderAt) (nativeImage, error) {
}
exportSymbolsOffset := uint64(exportSymbolsDataDirectory.VirtualAddress)
exports := make([]byte, exportSymbolsDataDirectory.Size)
_, err = r.ReadAt(exports, int64(exportSymbolsOffset)) //nolint:gosec
_, err = r.ReadAt(exports, int64(exportSymbolsOffset))
if err != nil {
return fileError(filename, fmt.Errorf("could not read the exported symbols data directory: %w", err))
}
@@ -412,7 +412,7 @@ func (ni nativeImagePE) fetchExportAttribute(i int) (uint32, error) {
func (ni nativeImagePE) fetchExportFunctionPointer(functionsBase uint32, i uint32) (uint32, error) {
var pointer uint32
n := uint32(len(ni.exports)) //nolint:gosec
n := uint32(len(ni.exports))
sz := uint32(unsafe.Sizeof(ni.t.functionPointer))
j := functionsBase + i*sz
if j+sz >= n {
@@ -457,7 +457,7 @@ func (ni nativeImagePE) fetchSbomSymbols(content *exportContentPE) {
sbomBytes := []byte(nativeImageSbomSymbol + "\x00")
sbomLengthBytes := []byte(nativeImageSbomLengthSymbol + "\x00")
svmVersionInfoBytes := []byte(nativeImageSbomVersionSymbol + "\x00")
n := uint32(len(ni.exports)) //nolint:gosec
n := uint32(len(ni.exports))
// Find SBOM, SBOM Length, and SVM Version Symbol
for i := uint32(0); i < content.numberOfNames; i++ {
@@ -0,0 +1,423 @@
package java
import (
"bufio"
"context"
"fmt"
"io"
"path"
"sort"
"strings"
"github.com/mitchellh/mapstructure"
"github.com/anchore/packageurl-go"
stereoFile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
const (
// this is a very permissive glob that will match more than just the JVM release file.
// we started with "**/{java,jvm}/*/release", but this prevents scanning JVM archive contents (e.g. jdk8u402.zip).
// this approach lets us check more files for JVM release info, but be rather silent about errors.
jvmReleaseGlob = "**/release"
oracleVendor = "oracle"
openJdkProduct = "openjdk"
jre = "jre"
jdk = "jdk"
)
// the /opt/java/openjdk/release file (and similar paths) is a file that is present in the multiple OpenJDK distributions
// here's an example of the contents of the file:
//
// IMPLEMENTOR="Eclipse Adoptium"
// IMPLEMENTOR_VERSION="Temurin-21.0.4+7"
// JAVA_RUNTIME_VERSION="21.0.4+7-LTS"
// JAVA_VERSION="21.0.4"
// JAVA_VERSION_DATE="2024-07-16"
// LIBC="gnu"
// MODULES="java.base java.compiler java.datatransfer java.xml java.prefs java.desktop java.instrument java.logging java.management java.security.sasl java.naming java.rmi java.management.rmi java.net.http java.scripting java.security.jgss java.transaction.xa java.sql java.sql.rowset java.xml.crypto java.se java.smartcardio jdk.accessibility jdk.internal.jvmstat jdk.attach jdk.charsets jdk.internal.opt jdk.zipfs jdk.compiler jdk.crypto.ec jdk.crypto.cryptoki jdk.dynalink jdk.internal.ed jdk.editpad jdk.hotspot.agent jdk.httpserver jdk.incubator.vector jdk.internal.le jdk.internal.vm.ci jdk.internal.vm.compiler jdk.internal.vm.compiler.management jdk.jartool jdk.javadoc jdk.jcmd jdk.management jdk.management.agent jdk.jconsole jdk.jdeps jdk.jdwp.agent jdk.jdi jdk.jfr jdk.jlink jdk.jpackage jdk.jshell jdk.jsobject jdk.jstatd jdk.localedata jdk.management.jfr jdk.naming.dns jdk.naming.rmi jdk.net jdk.nio.mapmode jdk.random jdk.sctp jdk.security.auth jdk.security.jgss jdk.unsupported jdk.unsupported.desktop jdk.xml.dom"
// OS_ARCH="aarch64"
// OS_NAME="Linux"
// SOURCE=".:git:13710926b798"
// BUILD_SOURCE="git:1271f10a26c47e1489a814dd2731f936a588d621"
// BUILD_SOURCE_REPO="https://github.com/adoptium/temurin-build.git"
// SOURCE_REPO="https://github.com/adoptium/jdk21u.git"
// FULL_VERSION="21.0.4+7-LTS"
// SEMANTIC_VERSION="21.0.4+7"
// BUILD_INFO="OS: Linux Version: 5.4.0-150-generic"
// JVM_VARIANT="Hotspot"
// JVM_VERSION="21.0.4+7-LTS"
// IMAGE_TYPE="JDK"
//
// In terms of the temurin flavor, these are controlled by:
// - config: https://github.com/adoptium/temurin-build/blob/v2023.01.03/sbin/common/config_init.sh
// - build script: https://github.com/adoptium/temurin-build/blob/v2023.01.03/sbin/build.sh#L1584-L1796
type jvmCpeInfo struct {
vendor, product, version string
}
func parseJVMRelease(_ context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
ri, err := parseJvmReleaseInfo(reader)
if err != nil {
return nil, nil, fmt.Errorf("unable to parse JVM release info %q: %w", reader.Path(), err)
}
if ri == nil {
// TODO: known-unknown: expected JDK installation package
return nil, nil, nil
}
version := jvmPackageVersion(ri)
// TODO: detect old and new version format from multiple fields
licenses := jvmLicenses(resolver, ri)
locations := file.NewLocationSet(reader.Location)
for _, lic := range licenses.ToSlice() {
locations.Add(lic.Locations.ToSlice()...)
}
installDir := path.Dir(reader.Path())
files, hasJdk := findJvmFiles(resolver, installDir)
vendor, product := jvmPrimaryVendorProduct(ri.Implementor, reader.Path(), ri.ImageType, hasJdk)
p := pkg.Package{
Name: product,
Locations: locations,
Version: version,
CPEs: jvmCpes(version, vendor, product, ri.ImageType, hasJdk),
PURL: jvmPurl(*ri, version, vendor, product),
Licenses: licenses,
Type: pkg.BinaryPkg,
Metadata: pkg.JavaVMInstallation{
Release: *ri,
Files: files,
},
}
p.SetID()
return []pkg.Package{p}, nil, nil
}
func jvmLicenses(_ file.Resolver, _ *pkg.JavaVMRelease) pkg.LicenseSet {
// TODO: get this from the dir(<RELEASE>)/legal/**/LICENSE files when we start cataloging license content
// see https://github.com/anchore/syft/issues/656
return pkg.NewLicenseSet()
}
func findJvmFiles(resolver file.Resolver, installDir string) ([]string, bool) {
ownedLocations, err := resolver.FilesByGlob(installDir + "/**")
if err != nil {
// TODO: known-unknowns
log.WithFields("path", installDir, "error", err).Trace("unable to find installed JVM files")
}
var results []string
var hasJdk bool
for _, loc := range ownedLocations {
p := loc.Path()
results = append(results, p)
if !hasJdk && strings.HasSuffix(p, "bin/javac") {
hasJdk = true
}
}
sort.Strings(results)
return results, hasJdk
}
func jvmPurl(ri pkg.JavaVMRelease, version, vendor, product string) string {
var qualifiers []packageurl.Qualifier
if ri.SourceRepo != "" {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "repository_url",
Value: ri.SourceRepo,
})
} else if ri.BuildSourceRepo != "" {
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "repository_url",
Value: ri.BuildSourceRepo,
})
}
pURL := packageurl.NewPackageURL(
packageurl.TypeGeneric,
vendor,
product,
version,
qualifiers,
"")
return pURL.ToString()
}
func jvmPrimaryVendorProduct(implementor, path, imageType string, hasJdk bool) (string, string) {
implementor = strings.ReplaceAll(strings.ToLower(implementor), " ", "")
pickProduct := func() string {
if hasJdk || jvmProjectByType(imageType) == jdk {
return jdk
}
return jre
}
switch {
case strings.Contains(implementor, "azul") || strings.Contains(path, "zulu"):
return "azul", "zulu"
case strings.Contains(implementor, "sun"):
return "sun", pickProduct()
case strings.Contains(implementor, "oracle") || strings.Contains(path, "oracle"):
return oracleVendor, pickProduct()
}
return oracleVendor, openJdkProduct
}
func jvmCpes(version, primaryVendor, primaryProduct, imageType string, hasJdk bool) []cpe.CPE {
// see https://github.com/anchore/syft/issues/2422 for more context
var candidates []jvmCpeInfo
newCandidate := func(ven, prod, ver string) {
candidates = append(candidates, jvmCpeInfo{
vendor: ven,
product: prod,
version: ver,
})
}
newEnterpriseCandidate := func(ven, ver string) {
newCandidate(ven, jre, ver)
if hasJdk || jvmProjectByType(imageType) == jdk {
newCandidate(ven, jdk, ver)
}
}
switch {
case primaryVendor == "azul":
newCandidate(primaryVendor, "zulu", version)
newCandidate(oracleVendor, openJdkProduct, version)
case primaryVendor == "sun":
newEnterpriseCandidate(primaryVendor, version)
case primaryVendor == oracleVendor && primaryProduct != openJdkProduct:
newCandidate(primaryVendor, "java_se", version)
newEnterpriseCandidate(primaryVendor, version)
default:
newCandidate(primaryVendor, primaryProduct, version)
}
var cpes []cpe.CPE
for _, candidate := range candidates {
c := newJvmCpe(candidate)
if c == nil {
continue
}
cpes = append(cpes, *c)
}
return cpes
}
func getJVMVersionAndUpdate(version string) (string, string) {
hasPlus := strings.Contains(version, "+")
hasUnderscore := strings.Contains(version, "_")
switch {
case hasUnderscore:
// assume legacy version strings are provided
// example: 1.8.0_302-b08
fields := strings.Split(version, "_")
if len(fields) == 2 {
shortVer := fields[0]
fields = strings.Split(fields[1], "-")
return shortVer, fields[0]
}
case hasPlus:
// assume JEP 223 version strings are provided
// example: 9.0.1+20
fields := strings.Split(version, "+")
return fields[0], ""
}
// this could be a legacy or modern string that does not have an update
return version, ""
}
func newJvmCpe(candidate jvmCpeInfo) *cpe.CPE {
if candidate.vendor == "" || candidate.product == "" || candidate.version == "" {
return nil
}
shortVer, update := getJVMVersionAndUpdate(candidate.version)
if shortVer == "" {
return nil
}
if update != "" && !strings.Contains(strings.ToLower(update), "update") {
update = "update" + trim0sFromLeft(update)
}
return &cpe.CPE{
Attributes: cpe.Attributes{
Part: "a",
Vendor: candidate.vendor,
Product: candidate.product,
Version: shortVer,
Update: update,
},
// note: we must use a declared source here. Though we are not directly raising up raw CPEs from cataloged material,
// these are vastly more reliable and accurate than what would be generated from the cpe generator logic.
// We want these CPEs to override any generated CPEs (and in fact prevent the generation of CPEs for these packages altogether).
Source: cpe.DeclaredSource,
}
}
func jvmProjectByType(ty string) string {
if strings.Contains(strings.ToLower(ty), jre) {
return jre
}
return jdk
}
// jvmPackageVersion attempts to extract the correct version value for the JVM given a platter of version strings to choose
// from, and makes special consideration to what a valid version is relative to JEP 223.
//
// example version values (openjdk >8):
//
// IMPLEMENTOR_VERSION "Temurin-21.0.4+7"
// JAVA_RUNTIME_VERSION "21.0.4+7-LTS"
// FULL_VERSION "21.0.4+7-LTS"
// SEMANTIC_VERSION "21.0.4+7"
// JAVA_VERSION "21.0.4"
//
// example version values (openjdk 8):
//
// JAVA_VERSION "1.8.0_422"
// FULL_VERSION "1.8.0_422-b05"
// SEMANTIC_VERSION "8.0.422+5"
//
// example version values (openjdk 8, but older):
//
// JAVA_VERSION "1.8.0_302"
// FULL_VERSION "1.8.0_302-b08"
// SEMANTIC_VERSION "8.0.302+8"
//
// example version values (oracle):
//
// IMPLEMENTOR_VERSION (missing)
// JAVA_RUNTIME_VERSION "22.0.2+9-70"
// JAVA_VERSION "22.0.2"
//
// example version values (mariner):
//
// IMPLEMENTOR_VERSION "Microsoft-9889599"
// JAVA_RUNTIME_VERSION "17.0.12+7-LTS"
// JAVA_VERSION "17.0.12"
//
// example version values (amazon):
//
// IMPLEMENTOR_VERSION "Corretto-17.0.12.7.1"
// JAVA_RUNTIME_VERSION "17.0.12+7-LTS"
// JAVA_VERSION "17.0.12"
//
// JEP 223 changes to JVM version string in the following way:
//
// Pre JEP 223 Post JEP 223
// Release Type long short long short
// ------------ -------------------- --------------------
// Early Access 1.9.0-ea-b19 9-ea 9-ea+19 9-ea
// Major 1.9.0-b100 9 9+100 9
// Security #1 1.9.0_5-b20 9u5 9.0.1+20 9.0.1
// Security #2 1.9.0_11-b12 9u11 9.0.2+12 9.0.2
// Minor #1 1.9.0_20-b62 9u20 9.1.2+62 9.1.2
// Security #3 1.9.0_25-b15 9u25 9.1.3+15 9.1.3
// Security #4 1.9.0_31-b08 9u31 9.1.4+8 9.1.4
// Minor #2 1.9.0_40-b45 9u40 9.2.4+45 9.2.4
//
// What does this mean for us? In terms of the version selected, use semver-compliant strings when available.
//
// In terms of where to get the version:
//
// SEMANTIC_VERSION Reasonably prevalent, but most accurate in terms of comparable versions
// JAVA_RUNTIME_VERSION Reasonable prevalent, but difficult to distinguish pre-release info vs aux info (jep 223 sensitive)
// FULL_VERSION Reasonable prevalent, but difficult to distinguish pre-release info vs aux info (jep 223 sensitive)
// JAVA_VERSION Most prevalent, but least specific (jep 223 sensitive)
// IMPLEMENTOR_VERSION Unusable or missing in some cases
func jvmPackageVersion(ri *pkg.JavaVMRelease) string {
var version string
switch {
case ri.JavaRuntimeVersion != "":
return ri.JavaRuntimeVersion
case ri.FullVersion != "":
// if the full version major version matches the java version major version, then use the full version
fullMajor := strings.Split(ri.FullVersion, ".")[0]
javaMajor := strings.Split(ri.JavaVersion, ".")[0]
if fullMajor == javaMajor {
return ri.FullVersion
}
fallthrough
case ri.JavaVersion != "":
return ri.JavaVersion
}
return version
}
func trim0sFromLeft(v string) string {
if v == "0" {
return v
}
return strings.TrimLeft(v, "0")
}
func parseJvmReleaseInfo(r io.ReadCloser) (*pkg.JavaVMRelease, error) {
defer r.Close()
data := make(map[string]any)
scanner := bufio.NewScanner(io.LimitReader(r, 500*stereoFile.KB))
for scanner.Scan() {
line := scanner.Text()
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
continue
}
key := parts[0]
value := strings.Trim(parts[1], `"`)
if key == "MODULES" {
data[key] = strings.Split(value, " ")
} else {
data[key] = value
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
// if we're missing key fields, then we don't have a JVM release file
if data["JAVA_VERSION"] == nil && data["JAVA_RUNTIME_VERSION"] == nil {
return nil, nil
}
var ri pkg.JavaVMRelease
if err := mapstructure.Decode(data, &ri); err != nil {
return nil, err
}
return &ri, nil
}
@@ -0,0 +1,438 @@
package java
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
func TestJvmCpes(t *testing.T) {
tests := []struct {
name string
pkgVersion string
primaryVendor string
primaryProduct string
imageType string
hasJdk bool
expected []cpe.CPE
}{
{
name: "zulu release",
pkgVersion: "9.0.1+20",
primaryVendor: "azul",
primaryProduct: "zulu",
imageType: "jdk",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "azul",
Product: "zulu",
Version: "9.0.1",
Update: "",
},
Source: cpe.DeclaredSource,
},
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "oracle",
Product: "openjdk",
Version: "9.0.1",
Update: "",
},
Source: cpe.DeclaredSource,
},
},
},
{
name: "sun release",
pkgVersion: "1.6.0_322-b002",
primaryVendor: "sun",
primaryProduct: "jre",
imageType: "jre",
hasJdk: true,
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "sun",
Product: "jre",
Version: "1.6.0",
Update: "update322",
},
Source: cpe.DeclaredSource,
},
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "sun",
Product: "jdk",
Version: "1.6.0",
Update: "update322",
},
Source: cpe.DeclaredSource,
},
},
},
{
name: "oracle se release",
pkgVersion: "1.8.0_322-b02",
primaryVendor: "oracle",
primaryProduct: "java_se",
imageType: "jdk",
hasJdk: true,
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "oracle",
Product: "java_se",
Version: "1.8.0",
Update: "update322",
},
Source: cpe.DeclaredSource,
},
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "oracle",
Product: "jre",
Version: "1.8.0",
Update: "update322",
},
Source: cpe.DeclaredSource,
},
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "oracle",
Product: "jdk",
Version: "1.8.0",
Update: "update322",
},
Source: cpe.DeclaredSource,
},
},
},
{
name: "JEP 223 version with build info",
pkgVersion: "9.0.1+20",
primaryVendor: "oracle",
primaryProduct: "openjdk",
imageType: "openjdk",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "oracle",
Product: "openjdk",
Version: "9.0.1",
Update: "",
},
Source: cpe.DeclaredSource,
},
},
},
{
name: "JEP 223 version without build info",
pkgVersion: "11.0.9",
primaryVendor: "oracle",
primaryProduct: "openjdk",
imageType: "openjdk",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "oracle",
Product: "openjdk",
Version: "11.0.9",
Update: "",
},
Source: cpe.DeclaredSource,
},
},
},
{
name: "no plus sign in version string",
pkgVersion: "1.8.0",
primaryVendor: "oracle",
primaryProduct: "openjdk",
imageType: "openjdk",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "oracle",
Product: "openjdk",
Version: "1.8.0",
Update: "",
},
Source: cpe.DeclaredSource,
},
},
},
{
name: "empty version string",
pkgVersion: "",
primaryVendor: "oracle",
primaryProduct: "",
imageType: "",
expected: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := jvmCpes(tt.pkgVersion, tt.primaryVendor, tt.primaryProduct, tt.imageType, tt.hasJdk)
assert.Equal(t, tt.expected, result)
})
}
}
func TestJvmVersion(t *testing.T) {
tests := []struct {
name string
input *pkg.JavaVMRelease
expected string
}{
{
name: "JavaRuntimeVersion fallback",
input: &pkg.JavaVMRelease{
JavaRuntimeVersion: "21.0.4+7-LTS",
JavaVersion: "bogus",
FullVersion: "bogus",
SemanticVersion: "bogus",
},
expected: "21.0.4+7-LTS",
},
{
name: "JavaVersion fallback",
input: &pkg.JavaVMRelease{
JavaVersion: "21.0.4",
FullVersion: "bogus",
SemanticVersion: "bogus",
},
expected: "21.0.4",
},
{
// there is an example of this in eclipse-temurin:8u312-b07-jdk
name: "FullVersion is more accurate",
input: &pkg.JavaVMRelease{
JavaVersion: "1.8.0_131",
FullVersion: "1.8.0_131+b08",
},
expected: "1.8.0_131+b08",
},
{
name: "empty input fields",
input: &pkg.JavaVMRelease{},
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := jvmPackageVersion(tt.input)
assert.Equal(t, tt.expected, result)
})
}
}
func TestGetJVMVersionAndUpdate(t *testing.T) {
tests := []struct {
name string
version string
expectedVer string
expectedUpdate string
}{
{
name: "legacy version with underscore and build",
version: "1.8.0_302-b08",
expectedVer: "1.8.0",
expectedUpdate: "302",
},
{
name: "legacy version with underscore but no build",
version: "1.8.0_302",
expectedVer: "1.8.0",
expectedUpdate: "302",
},
{
name: "JEP 223 version with plus sign",
version: "9.0.1+20",
expectedVer: "9.0.1",
expectedUpdate: "",
},
{
name: "JEP 223 version with plus but no update",
version: "11.0.9+",
expectedVer: "11.0.9",
expectedUpdate: "",
},
{
name: "modern version without plus or underscore",
version: "11.0.9",
expectedVer: "11.0.9",
expectedUpdate: "",
},
{
name: "legacy version without underscore or plus",
version: "1.7.0",
expectedVer: "1.7.0",
expectedUpdate: "",
},
{
name: "empty version string",
version: "",
expectedVer: "",
expectedUpdate: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ver, update := getJVMVersionAndUpdate(tt.version)
assert.Equal(t, tt.expectedVer, ver)
assert.Equal(t, tt.expectedUpdate, update)
})
}
}
func TestJvmPrimaryVendorProduct(t *testing.T) {
tests := []struct {
name string
implementor string
path string
imageType string
hasJdk bool
expectedVendor string
expectedProduct string
}{
{
name: "Azul implementor with Zulu in path",
implementor: "Azul Systems",
path: "/usr/lib/jvm/zulu-11-amd64/release",
imageType: "JDK",
hasJdk: true,
expectedVendor: "azul",
expectedProduct: "zulu",
},
{
name: "Sun implementor with JDK",
implementor: "Sun Microsystems",
path: "/usr/lib/jvm/jdk-1.8-sun-amd64/release",
imageType: "JDK",
hasJdk: true,
expectedVendor: "sun",
expectedProduct: "jdk",
},
{
name: "Oracle implementor with JRE",
implementor: "Oracle Corporation",
path: "/usr/lib/jvm/jdk-1.8-oracle-x64/release",
imageType: "JRE",
hasJdk: false,
expectedVendor: "oracle",
expectedProduct: "jre",
},
{
name: "Oracle vendor with JDK in path",
implementor: "",
path: "/usr/lib/jvm/jdk-1.8-oracle-x64/release",
imageType: "JDK",
hasJdk: true,
expectedVendor: "oracle",
expectedProduct: "jdk",
},
{
name: "OpenJDK with JDK",
implementor: "OpenJDK",
path: "/opt/java/openjdk/release",
imageType: "JDK",
hasJdk: true,
expectedVendor: "oracle", // like temurin
expectedProduct: "openjdk",
},
{
name: "Amazon Corretto with JDK",
implementor: "Amazon Corretto",
path: "/usr/lib/jvm/java-17-amazon-corretto/release",
imageType: "JDK",
hasJdk: true,
expectedVendor: "oracle", // corretto upstream is oracle openjdk
expectedProduct: "openjdk",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
vendor, product := jvmPrimaryVendorProduct(tt.implementor, tt.path, tt.imageType, tt.hasJdk)
assert.Equal(t, tt.expectedVendor, vendor)
assert.Equal(t, tt.expectedProduct, product)
})
}
}
func TestJvmPurl(t *testing.T) {
tests := []struct {
name string
ri pkg.JavaVMRelease
version string
vendor string
product string
expectedPURL string
}{
{
name: "build source repo provided",
ri: pkg.JavaVMRelease{
BuildSourceRepo: "https://github.com/adoptium/temurin-build.git",
},
version: "21.0.4",
vendor: "oracle",
product: "jdk",
expectedPURL: "pkg:generic/oracle/jdk@21.0.4?repository_url=https://github.com/adoptium/temurin-build.git",
},
{
name: "source repo provided, no build source repo",
ri: pkg.JavaVMRelease{
SourceRepo: "https://github.com/adoptium/jdk21u.git",
},
version: "21.0.4",
vendor: "azul",
product: "zulu",
expectedPURL: "pkg:generic/azul/zulu@21.0.4?repository_url=https://github.com/adoptium/jdk21u.git",
},
{
name: "no repository URLs provided",
ri: pkg.JavaVMRelease{
// No repository URLs provided
},
version: "17.0.2",
vendor: "oracle",
product: "jdk",
expectedPURL: "pkg:generic/oracle/jdk@17.0.2",
},
{
name: "JRE with source repo",
ri: pkg.JavaVMRelease{
SourceRepo: "https://github.com/adoptium/jre-repo.git",
},
version: "1.8.0_302",
vendor: "oracle",
product: "jre",
expectedPURL: "pkg:generic/oracle/jre@1.8.0_302?repository_url=https://github.com/adoptium/jre-repo.git",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actualPURL := jvmPurl(tt.ri, tt.version, tt.vendor, tt.product)
assert.Equal(t, tt.expectedPURL, actualPURL)
})
}
}
@@ -0,0 +1 @@
!/jdk-1.8-oracle-x64/bin
@@ -0,0 +1,7 @@
JAVA_VERSION="1.8.0_411"
JAVA_RUNTIME_VERSION="1.8.0_411-b25"
OS_NAME="Linux"
OS_VERSION="2.6"
OS_ARCH="amd64"
SOURCE=".:git:71ec2089cf8c+"
BUILD_TYPE="commercial"
@@ -0,0 +1,19 @@
IMPLEMENTOR="Eclipse Adoptium"
IMPLEMENTOR_VERSION="Temurin-21.0.4+7"
JAVA_RUNTIME_VERSION="21.0.4+7-LTS"
JAVA_VERSION="21.0.4"
JAVA_VERSION_DATE="2024-07-16"
LIBC="gnu"
MODULES="java.base java.compiler java.datatransfer java.xml java.prefs java.desktop java.instrument java.logging java.management java.security.sasl java.naming java.rmi java.management.rmi java.net.http java.scripting java.security.jgss java.transaction.xa java.sql java.sql.rowset java.xml.crypto java.se java.smartcardio jdk.accessibility jdk.internal.jvmstat jdk.attach jdk.charsets jdk.internal.opt jdk.zipfs jdk.compiler jdk.crypto.ec jdk.crypto.cryptoki jdk.dynalink jdk.internal.ed jdk.editpad jdk.hotspot.agent jdk.httpserver jdk.incubator.vector jdk.internal.le jdk.internal.vm.ci jdk.internal.vm.compiler jdk.internal.vm.compiler.management jdk.jartool jdk.javadoc jdk.jcmd jdk.management jdk.management.agent jdk.jconsole jdk.jdeps jdk.jdwp.agent jdk.jdi jdk.jfr jdk.jlink jdk.jpackage jdk.jshell jdk.jsobject jdk.jstatd jdk.localedata jdk.management.jfr jdk.naming.dns jdk.naming.rmi jdk.net jdk.nio.mapmode jdk.random jdk.sctp jdk.security.auth jdk.security.jgss jdk.unsupported jdk.unsupported.desktop jdk.xml.dom"
OS_ARCH="aarch64"
OS_NAME="Linux"
SOURCE=".:git:13710926b798"
BUILD_SOURCE="git:1271f10a26c47e1489a814dd2731f936a588d621"
BUILD_SOURCE_REPO="https://github.com/adoptium/temurin-build.git"
SOURCE_REPO="https://github.com/adoptium/jdk21u.git"
FULL_VERSION="21.0.4+7-LTS"
SEMANTIC_VERSION="21.0.4+7"
BUILD_INFO="OS: Linux Version: 5.4.0-150-generic"
JVM_VARIANT="Hotspot"
JVM_VERSION="21.0.4+7-LTS"
IMAGE_TYPE="JDK"
+2 -1
View File
@@ -3,11 +3,12 @@ package ocaml
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
"github.com/stretchr/testify/assert"
)
func TestParseOpamPackage(t *testing.T) {
@@ -60,10 +60,10 @@ func readStruct(metadata any, fields ...string) string {
if len(fields) > 0 {
value, ok := metadata.(map[any]any)
if !ok {
log.Tracef("unable to read '%s' from: %v", fields[0], metadata) //nolint:gosec
log.Tracef("unable to read '%s' from: %v", fields[0], metadata)
return ""
}
return readStruct(value[fields[0]], fields[1:]...) //nolint:gosec
return readStruct(value[fields[0]], fields[1:]...)
}
value, ok := metadata.(string)
if !ok {
@@ -88,12 +88,12 @@ func mapFiles(files []rpmutils.FileInfo, digestAlgorithm string) []pkg.RpmFileRe
}
out = append(out, pkg.RpmFileRecord{
Path: f.Name(),
Mode: pkg.RpmFileMode(f.Mode()), //nolint:gosec
Mode: pkg.RpmFileMode(f.Mode()),
Size: int(f.Size()),
Digest: digest,
UserName: f.UserName(),
GroupName: f.GroupName(),
Flags: rpmdb.FileFlags(f.Flags()).String(), //nolint:gosec
Flags: rpmdb.FileFlags(f.Flags()).String(),
})
}
return out
+74
View File
@@ -18,6 +18,80 @@ var jenkinsPluginPomPropertiesGroupIDs = []string{
"com.cloudbees.jenkins.plugins",
}
type JavaVMInstallation struct {
Release JavaVMRelease `json:"release"`
Files []string `json:"files"`
}
func (m JavaVMInstallation) OwnedFiles() []string {
return m.Files
}
type JavaVMRelease struct {
// Implementor is extracted with the `java.vendor` JVM property
Implementor string `mapstructure:"IMPLEMENTOR,omitempty" json:"implementor,omitempty"`
// ImplementorVersion is extracted with the `java.vendor.version` JVM property
ImplementorVersion string `mapstructure:"IMPLEMENTOR_VERSION,omitempty" json:"implementorVersion,omitempty"`
// JavaRuntimeVersion is extracted from the 'java.runtime.version' JVM property
JavaRuntimeVersion string `mapstructure:"JAVA_RUNTIME_VERSION,omitempty" json:"javaRuntimeVersion,omitempty"`
// JavaVersion matches that from `java -version` command output
JavaVersion string `mapstructure:"JAVA_VERSION,omitempty" json:"javaVersion,omitempty"`
// JavaVersionDate is extracted from the 'java.version.date' JVM property
JavaVersionDate string `mapstructure:"JAVA_VERSION_DATE,omitempty" json:"javaVersionDate,omitempty"`
// Libc can either be 'glibc' or 'musl'
Libc string `mapstructure:"LIBC,omitempty" json:"libc,omitempty"`
// Modules is a list of JVM modules that are packaged
Modules []string `mapstructure:"MODULES,omitempty" json:"modules,omitempty"`
// OsArch is the target CPU architecture
OsArch string `mapstructure:"OS_ARCH,omitempty" json:"osArch,omitempty"`
// OsName is the name of the target runtime operating system environment
OsName string `mapstructure:"OS_NAME,omitempty" json:"osName,omitempty"`
// OsVersion is the version of the target runtime operating system environment
OsVersion string `mapstructure:"OS_VERSION,omitempty" json:"osVersion,omitempty"`
// Source refers to the origin repository of OpenJDK source
Source string `mapstructure:"SOURCE,omitempty" json:"source,omitempty"`
// BuildSource Git SHA of the build repository
BuildSource string `mapstructure:"BUILD_SOURCE,omitempty" json:"buildSource,omitempty"`
// BuildSourceRepo refers to rhe repository URL for the build source
BuildSourceRepo string `mapstructure:"BUILD_SOURCE_REPO,omitempty" json:"buildSourceRepo,omitempty"`
// SourceRepo refers to the OpenJDK repository URL
SourceRepo string `mapstructure:"SOURCE_REPO,omitempty" json:"sourceRepo,omitempty"`
// FullVersion is extracted from the 'java.runtime.version' JVM property
FullVersion string `mapstructure:"FULL_VERSION,omitempty" json:"fullVersion,omitempty"`
// SemanticVersion is derived from the OpenJDK version
SemanticVersion string `mapstructure:"SEMANTIC_VERSION,omitempty" json:"semanticVersion,omitempty"`
// BuildInfo contains additional build information
BuildInfo string `mapstructure:"BUILD_INFO,omitempty" json:"buildInfo,omitempty"`
// JvmVariant specifies the JVM variant (e.g., Hotspot or OpenJ9)
JvmVariant string `mapstructure:"JVM_VARIANT,omitempty" json:"jvmVariant,omitempty"`
// JvmVersion is extracted from the 'java.vm.version' JVM property
JvmVersion string `mapstructure:"JVM_VERSION,omitempty" json:"jvmVersion,omitempty"`
// ImageType can be 'JDK' or 'JRE'
ImageType string `mapstructure:"IMAGE_TYPE,omitempty" json:"imageType,omitempty"`
// BuildType can be 'commercial' (used in some older oracle JDK distributions)
BuildType string `mapstructure:"BUILD_TYPE,omitempty" json:"buildType,omitempty"`
}
// JavaArchive encapsulates all Java ecosystem metadata for a package as well as an (optional) parent relationship.
type JavaArchive struct {
VirtualPath string `json:"virtualPath" cyclonedx:"virtualPath"` // we need to include the virtual path in cyclonedx documents to prevent deduplication of jars within jars