Compare commits

...
54 Commits
Author SHA1 Message Date
Keith ZantowandAlex Goodman ccbee94b87 feat: report unknowns in sbom (#2998)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-10-07 16:11:37 -04:00
dependabot[bot] 4d7ed9f749 chore(deps): bump sigstore/cosign-installer from 3.6.0 to 3.7.0 (#3299)
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.6.0 to 3.7.0.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/v3.6.0...v3.7.0)

---
updated-dependencies:
- dependency-name: sigstore/cosign-installer
  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-10-07 15:21:34 -04:00
anchore-actions-token-generator[bot]andkzantow 4c4e5cb06c chore(deps): update stereoscope to efa76446cc1c7e6c4117350943a2754b2453aec4 (#3301)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: kzantow <3009477+kzantow@users.noreply.github.com>
2024-10-07 15:21:26 -04:00
dependabot[bot] 8b6159dbd8 chore(deps): bump golang.org/x/net from 0.29.0 to 0.30.0 (#3304)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.29.0 to 0.30.0.
- [Commits](https://github.com/golang/net/compare/v0.29.0...v0.30.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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-10-07 15:20:38 -04:00
dependabot[bot] 7b30ce15d7 chore(deps): bump actions/cache from 4.0.2 to 4.1.0 (#3305)
Bumps [actions/cache](https://github.com/actions/cache) from 4.0.2 to 4.1.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/0c45773b623bea8c8e75f6c82b208c3cf94ea4f9...2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2)

---
updated-dependencies:
- dependency-name: actions/cache
  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-10-07 15:20:29 -04:00
anchore-actions-token-generator[bot]andwagoodman 27ee203495 chore(deps): update CPE dictionary index (#3302)
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-10-07 15:20:12 -04:00
Piotr RadkowskiandPiotr Radkowski 3b9c55d28b Fix: Parse package.json with non-standard fields in 'author' section (#3300)
* Improved parsing of package.json 'author' section

Signed-off-by: Piotr Radkowski <piotr.radkowski@contractors.roche.com>

* test: parse 'package.json' files with non-standard fields in author section

Signed-off-by: Piotr Radkowski <piotr.radkowski@contractors.roche.com>

---------

Signed-off-by: Piotr Radkowski <piotr.radkowski@contractors.roche.com>
Co-authored-by: Piotr Radkowski <piotr.radkowski@contractors.roche.com>
2024-10-07 10:26:04 -04:00
dependabot[bot] 25f5c6729f chore(deps): bump github/codeql-action from 3.26.10 to 3.26.11 (#3298)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.10 to 3.26.11.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/e2b3eafc8d227b0241d48be5f425d47c2d750a13...6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea)

---
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-10-05 09:25:01 -04:00
William Murphy 0d457142cc chore: add pull request template (#3294)
Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2024-10-05 09:05:11 -04:00
anchore-actions-token-generator[bot]andspiffcs fc8457418a chore(deps): update tools to latest versions (#3296)
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-10-05 07:32:32 -04:00
Alex Goodman 13c6876906 Track supporting DPKG evidence (#3228)
* add dpkg evidence support

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

* use path over filepath

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-10-04 11:07:29 -04:00
William MurphyandChristopher Phillips 770fdc53ea Fix: make failed CPE validation correctly return error (#2762)
* Test CPE attributes correctly returns error

Previously, this method incorrectly return an empty Attributes object
and a nil error, leading to callers attempting to use the empty
attributes object.

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* chore: merge with main and refactor call that relied on old nil behavior

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

* test: add test to cover new OSCPE err pattern

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

---------

Signed-off-by: Will Murphy <will.murphy@anchore.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2024-10-03 16:42:57 -04:00
dependabot[bot] 32c0d1e673 chore(deps): bump github.com/jedib0t/go-pretty/v6 from 6.5.9 to 6.6.0 (#3293)
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.5.9 to 6.6.0.
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.5.9...v6.6.0)

---
updated-dependencies:
- dependency-name: github.com/jedib0t/go-pretty/v6
  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-10-03 10:14:13 -04:00
witchcraze 263ea6b1bb feat: update haproxy classifier (#3277)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2024-10-02 15:10:39 -04:00
anchore-actions-token-generator[bot]andspiffcs cc4f62b3d4 chore(deps): update tools to latest versions (#3291)
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-10-02 09:07:25 -04:00
Niv Govrin dbad17de9e fix: don't use builtin scanner in licensecheck (#3290)
Signed-off-by: Niv Govrin <nivgo@oligosecurity.io>
2024-10-01 13:53:54 -04:00
anchore-actions-token-generator[bot]andwagoodman 93beceb4a2 chore(deps): update CPE dictionary index (#3288)
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-10-01 10:50:15 -04:00
dependabot[bot] 9b242b0309 chore(deps): bump github/codeql-action from 3.26.9 to 3.26.10 (#3289)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.9 to 3.26.10.
- [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/461ef6c76dfe95d5c364de2f431ddbd31a417628...e2b3eafc8d227b0241d48be5f425d47c2d750a13)

---
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-10-01 10:48:46 -04:00
witchcraze f5f8005fe0 update redis classifier (#3281)
* update redis classifier
Signed-off-by: witchcraze <witchcraze@gmail.com>

* Remove snippets to pass Validation.
In this case, 9000 byte was required...
Signed-off-by: witchcraze <witchcraze@gmail.com>
2024-09-30 15:37:47 -04:00
witchcraze 2a3d171c10 fix: improve node classifier version matching (#3284)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2024-09-27 08:53:35 -04:00
witchcraze 1a746b2c05 fix: update ruby classifier for -rc, -dev, etc. versions (#3285)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2024-09-27 08:51:50 -04:00
anchore-actions-token-generator[bot]andwagoodman e37c4686c2 chore(deps): update CPE dictionary index (#3262)
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-26 13:49:18 -04:00
dependabot[bot] 5393cd5dec chore(deps): bump github.com/docker/docker (#3264)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 27.3.0+incompatible to 27.3.1+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v27.3.0...v27.3.1)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  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-26 13:49:02 -04:00
dependabot[bot] f9ef9cf1dc chore(deps): bump github/codeql-action from 3.26.8 to 3.26.9 (#3275)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.26.8 to 3.26.9.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/294a9d92911152fe08befb9ec03e240add280cb3...461ef6c76dfe95d5c364de2f431ddbd31a417628)

---
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-26 13:48:45 -04:00
anchore-actions-token-generator[bot]andkzantow 16122eb32d chore(deps): update stereoscope to dc10ea61fd18efa45b516eda4de8bc19d8322429 (#3280)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: kzantow <3009477+kzantow@users.noreply.github.com>
2024-09-26 13:48:33 -04:00
dependabot[bot] 39b2bf5518 chore(deps): bump actions/checkout from 4.1.7 to 4.2.0 (#3283)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4.1.7 to 4.2.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/692973e3d937129bcbf40652eb9f2f61becf3332...d632683dd7b4114ad314bca15554477dd762a938)

---
updated-dependencies:
- dependency-name: actions/checkout
  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-26 13:48:12 -04:00
Alex Goodman d7005d7d8c add awaiting response management (#3272)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-09-25 08:56:21 -04:00
Christian Dupuis 92c1ddec5a fix: correct excluded mount point comparison to file paths (#3269)
Signed-off-by: Christian Dupuis <cd@docker.com>
2024-09-24 17:05:16 -04:00
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
Ryuichi Okumura fcd5ec951d chore: make ci-check.sh an executable file (#3220)
Signed-off-by: Ryuichi Okumura <okuryu@okuryu.com>
2024-09-11 10:02:37 -04:00
dependabot[bot] 61a9fde01c chore(deps): bump github.com/opencontainers/runc from 1.1.12 to 1.1.14 (#3219)
Bumps [github.com/opencontainers/runc](https://github.com/opencontainers/runc) from 1.1.12 to 1.1.14.
- [Release notes](https://github.com/opencontainers/runc/releases)
- [Changelog](https://github.com/opencontainers/runc/blob/main/CHANGELOG.md)
- [Commits](https://github.com/opencontainers/runc/compare/v1.1.12...v1.1.14)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/runc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-10 21:20:43 +00:00
Keith Zantow c33a51d3d8 chore: restore ci-check.sh script (#3218)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2024-09-10 15:19:05 -04:00
228 changed files with 10528 additions and 603 deletions
+6 -6
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
@@ -42,7 +42,7 @@ tools:
# used for signing the checksums file at release
- name: cosign
version:
want: v2.4.0
want: v2.4.1
method: github-release
with:
repo: sigstore/cosign
@@ -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.58.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
+25
View File
@@ -0,0 +1,25 @@
# Description
Please include a summary of the changes along with any relevant motivation and context,
or link to an issue where this is explained.
<!-- If this completes an issue, please include: -->
- Fixes #
## Type of change
<!-- Delete any that are not relevant -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (please discuss with the team first; Syft is 1.0 software and we won't accept breaking changes without going to 2.0)
- [ ] Documentation (updates the documentation)
- [ ] Chore (improve the developer experience, fix a test flake, etc, without changing the visible behavior of Syft)
- [ ] Performance (make Syft run faster or use less memory, without changing visible behavior much)
# Checklist:
- [ ] I have added unit tests that cover changed behavior
- [ ] I have tested my code in common scenarios and confirmed there are no regressions
- [ ] I have added comments to my code, particularly in hard-to-understand sections
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
red=$(tput setaf 1)
bold=$(tput bold)
normal=$(tput sgr0)
# assert we are running in CI (or die!)
if [[ -z "$CI" ]]; then
echo "${bold}${red}This step should ONLY be run in CI. Exiting...${normal}"
exit 1
fi
+2 -2
View File
@@ -17,13 +17,13 @@ jobs:
# the job by event.
steps:
- name: Checkout code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
- name: Restore base benchmark result
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 #v4.1.0
with:
path: test/results/benchmark-main.txt
# use base sha for PR or new commit hash for main push in benchmark result key
+4 -4
View File
@@ -36,7 +36,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Install Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 #v5.0.2
@@ -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@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea #v3.26.11
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@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea #v3.26.11
# ℹ️ 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@6db8d6351fd0be61f9ed8ebd12ccd35dcec51fea #v3.26.11
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- run: python .github/scripts/labeler.py
env:
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
release:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Update version file
run: make ci-release-version-file
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
environment: release
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Check if running on main
if: github.ref != 'refs/heads/main'
@@ -105,7 +105,7 @@ jobs:
# required for goreleaser signs section with cosign
id-token: write
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
with:
fetch-depth: 0
@@ -0,0 +1,11 @@
name: "Manage Awaiting Response Label"
on:
issue_comment:
types: [created]
jobs:
run:
uses: "anchore/workflows/.github/workflows/remove-awaiting-response-label.yaml@main"
secrets:
token: ${{ secrets.OSS_PROJECT_GH_TOKEN }}
@@ -18,7 +18,7 @@ jobs:
permissions:
packages: write
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -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
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -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
@@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 #v5.0.2
with:
@@ -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
+13 -13
View File
@@ -17,7 +17,7 @@ jobs:
name: "Static analysis"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -31,7 +31,7 @@ jobs:
name: "Unit tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -47,7 +47,7 @@ jobs:
name: "Integration tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -65,7 +65,7 @@ jobs:
name: "Build snapshot artifacts"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -78,7 +78,7 @@ jobs:
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
# see https://github.com/actions/upload-artifact/issues/199 for more info
- name: Upload snapshot artifacts
uses: actions/cache/save@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
uses: actions/cache/save@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 #v4.1.0
with:
# we need to preserve the snapshot data itself as well as the task data that confirms if the
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
@@ -95,7 +95,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -104,7 +104,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
uses: actions/cache/restore@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 #v4.1.0
with:
path: |
snapshot
@@ -139,9 +139,9 @@ jobs:
runs-on: macos-latest
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@v3.6.0
uses: sigstore/cosign-installer@v3.7.0
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -152,7 +152,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
uses: actions/cache/restore@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 #v4.1.0
with:
path: |
snapshot
@@ -178,7 +178,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -187,7 +187,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
uses: actions/cache/restore@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 #v4.1.0
with:
path: |
snapshot
@@ -215,7 +215,7 @@ jobs:
- Acceptance-Mac
- Cli-Linux
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 #v4.2.0
- name: Delete snapshot cache
run: gh cache delete "snapshot-build-${{ github.run_id }}"
+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:
+1 -10
View File
@@ -593,16 +593,7 @@ tasks:
ci-check:
# desc: "[CI only] Are you in CI?"
cmds:
- cmd: |
red=$(tput setaf 1)
bold=$(tput bold)
normal=$(tput sgr0)
# assert we are running in CI (or die!)
if [[ -z "$CI" ]]; then
echo "${bold}${red}This step should ONLY be run in CI. Exiting...${normal}"
exit 1
fi
- cmd: .github/scripts/ci-check.sh
silent: true
ci-release:
+108 -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"`
@@ -50,16 +53,20 @@ type Catalog struct {
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
Source sourceConfig `yaml:"source" json:"source" mapstructure:"source"`
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
// configuration for inclusion of unknown information within elements
Unknowns unknownsConfig `yaml:"unknowns" mapstructure:"unknowns"`
}
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(),
@@ -67,6 +74,7 @@ func DefaultCatalog() Catalog {
Java: defaultJavaConfig(),
File: defaultFileConfig(),
Relationships: defaultRelationshipsConfig(),
Unknowns: defaultUnknowns(),
Source: defaultSourceConfig(),
Parallelism: 1,
}
@@ -77,6 +85,8 @@ func (cfg Catalog) ToSBOMConfig(id clio.Identification) *syft.CreateSBOMConfig {
WithTool(id.Name, id.Version).
WithParallelism(cfg.Parallelism).
WithRelationshipsConfig(cfg.ToRelationshipsConfig()).
WithComplianceConfig(cfg.ToComplianceConfig()).
WithUnknownsConfig(cfg.ToUnknownsConfig()).
WithSearchConfig(cfg.ToSearchConfig()).
WithPackagesConfig(cfg.ToPackagesConfig()).
WithFilesConfig(cfg.ToFilesConfig()).
@@ -102,6 +112,20 @@ 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) ToUnknownsConfig() cataloging.UnknownsConfig {
return cataloging.UnknownsConfig{
IncludeExecutablesWithoutPackages: cfg.Unknowns.ExecutablesWithoutPackages,
IncludeUnexpandedArchives: cfg.Unknowns.UnexpandedArchives,
}
}
func (cfg Catalog) ToFilesConfig() filecataloging.Config {
hashers, err := intFile.Hashers(cfg.File.Metadata.Digests...)
if err != nil {
@@ -130,9 +154,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 +166,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 +175,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 +217,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 +232,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 +246,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 +261,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"`
}
+31
View File
@@ -0,0 +1,31 @@
package options
import (
"github.com/anchore/clio"
"github.com/anchore/syft/syft/cataloging"
)
type unknownsConfig struct {
RemoveWhenPackagesDefined bool `json:"remove-when-packages-defined" yaml:"remove-when-packages-defined" mapstructure:"remove-when-packages-defined"`
ExecutablesWithoutPackages bool `json:"executables-without-packages" yaml:"executables-without-packages" mapstructure:"executables-without-packages"`
UnexpandedArchives bool `json:"unexpanded-archives" yaml:"unexpanded-archives" mapstructure:"unexpanded-archives"`
}
var _ interface {
clio.FieldDescriber
} = (*unknownsConfig)(nil)
func (o *unknownsConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.RemoveWhenPackagesDefined, `remove unknown errors on files with discovered packages`)
descriptions.Add(&o.ExecutablesWithoutPackages, `include executables without any identified packages`)
descriptions.Add(&o.UnexpandedArchives, `include archives which were not expanded and searched`)
}
func defaultUnknowns() unknownsConfig {
def := cataloging.DefaultUnknownsConfig()
return unknownsConfig{
RemoveWhenPackagesDefined: def.RemoveWhenPackagesDefined,
ExecutablesWithoutPackages: def.IncludeExecutablesWithoutPackages,
UnexpandedArchives: def.IncludeUnexpandedArchives,
}
}
+25 -25
View File
@@ -3,37 +3,37 @@ 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
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f
github.com/anchore/stereoscope v0.0.3
github.com/anchore/stereoscope v0.0.4-0.20241005180410-efa76446cc1c
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
// we are hinting brotli to latest due to warning when installing archiver v3:
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
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.1+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
@@ -46,7 +46,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/iancoleman/strcase v0.3.0
github.com/invopop/jsonschema v0.7.0
github.com/jedib0t/go-pretty/v6 v6.5.9
github.com/jedib0t/go-pretty/v6 v6.6.0
github.com/jinzhu/copier v0.4.0
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/knqyf263/go-rpmdb v0.1.1
@@ -79,12 +79,12 @@ require (
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
go.uber.org/goleak v1.3.0
golang.org/x/mod v0.21.0
golang.org/x/net v0.29.0
golang.org/x/net v0.30.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
@@ -181,11 +181,11 @@ require (
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runc v1.1.12 // indirect
github.com/opencontainers/runc v1.1.14 // indirect
github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.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,21 +221,21 @@ 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
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.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
+62 -52
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=
@@ -115,8 +115,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f h1:B/E9ixKNCasntpoch61NDaQyGPDXLEJlL+B9B/PbdbA=
github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/stereoscope v0.0.3 h1:JRPHySy8S6P+Ff3IDiQ29ap1i8/laUQxDk9K1eFh/2U=
github.com/anchore/stereoscope v0.0.3/go.mod h1:5DJheGPjVRsSqegTB24Zi6SCHnYQnA519yeIG+RG+I4=
github.com/anchore/stereoscope v0.0.4-0.20241005180410-efa76446cc1c h1:JXezMk8fF5ns4AgRGW49SGfoRgDjJHsDmcpNw272jkU=
github.com/anchore/stereoscope v0.0.4-0.20241005180410-efa76446cc1c/go.mod h1:GMupz2FoBhy5RTTmawU06c2pZxgVTceahLWiwJef2uI=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@@ -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.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
github.com/docker/docker v27.3.1+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=
@@ -477,8 +477,8 @@ github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy77
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jedib0t/go-pretty/v6 v6.5.9 h1:ACteMBRrrmm1gMsXe9PSTOClQ63IXDUt03H5U+UV8OU=
github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
github.com/jedib0t/go-pretty/v6 v6.6.0 h1:wmZVuAcEkZRT+Aq1xXpE8IGat4vE5WXOMmBpbQqERXw=
github.com/jedib0t/go-pretty/v6 v6.6.0/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -609,8 +609,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss=
github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8=
github.com/opencontainers/runc v1.1.14 h1:rgSuzbmgz5DUJjeSnw337TxDbRuqjs6iqQck/2weR6w=
github.com/opencontainers/runc v1.1.14/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1LjPNeMTfRGvOA=
github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w=
github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
@@ -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=
@@ -864,8 +864,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -954,8 +954,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1067,15 +1067,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1088,8 +1088,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -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.18"
)
+3 -3
View File
@@ -1,7 +1,6 @@
package file
import (
"fmt"
"os"
"sort"
"strings"
@@ -19,12 +18,13 @@ func NewZipFileManifest(archivePath string) (ZipFileManifest, error) {
zipReader, err := OpenZip(archivePath)
manifest := make(ZipFileManifest)
if err != nil {
return manifest, fmt.Errorf("unable to open zip archive (%s): %w", archivePath, err)
log.Debugf("unable to open zip archive (%s): %v", archivePath, err)
return manifest, err
}
defer func() {
err = zipReader.Close()
if err != nil {
log.Warnf("unable to close zip archive (%s): %+v", archivePath, err)
log.Debugf("unable to close zip archive (%s): %+v", archivePath, err)
}
}()
+8 -4
View File
@@ -8,6 +8,8 @@ import (
"io"
"math"
"os"
"github.com/anchore/syft/internal/log"
)
// directoryEndLen, readByf, directoryEnd, and findSignatureInBlock were copied from the golang stdlib, specifically:
@@ -46,7 +48,8 @@ func OpenZip(filepath string) (*ZipReadCloser, error) {
// need to find the start of the archive and keep track of this offset.
offset, err := findArchiveStartOffset(f, fi.Size())
if err != nil {
return nil, fmt.Errorf("cannot find beginning of zip archive=%q : %w", filepath, err)
log.Debugf("cannot find beginning of zip archive=%q : %v", filepath, err)
return nil, err
}
if _, err := f.Seek(0, io.SeekStart); err != nil {
@@ -56,13 +59,14 @@ 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
r, err := zip.NewReader(io.NewSectionReader(f, offset64, size), size)
if err != nil {
return nil, fmt.Errorf("unable to open ZipReadCloser @ %q: %w", filepath, err)
log.Debugf("unable to open ZipReadCloser @ %q: %v", filepath, err)
return nil, err
}
return &ZipReadCloser{
@@ -183,7 +187,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
+7 -1
View File
@@ -22,7 +22,13 @@ func Parse(reader io.Reader, l file.Location) (licenses []pkg.License, err error
if err != nil {
return nil, err
}
cov := licensecheck.Scan(contents)
scanner, err := licensecheck.NewScanner(licensecheck.BuiltinLicenses())
if err != nil {
return nil, err
}
cov := scanner.Scan(contents)
if cov.Percent < coverageThreshold {
// unknown or no licenses here?
return licenses, nil
+7 -4
View File
@@ -9,15 +9,18 @@ func EvidentBy(catalog *pkg.Collection) []artifact.Relationship {
var edges []artifact.Relationship
for _, p := range catalog.Sorted() {
for _, l := range p.Locations.ToSlice() {
if v, exists := l.Annotations[pkg.EvidenceAnnotationKey]; !exists || v != pkg.PrimaryEvidenceAnnotation {
// skip non-primary evidence from being expressed as a relationship.
// note: this may be configurable in the future.
continue
kind := pkg.SupportingEvidenceAnnotation
if v, exists := l.Annotations[pkg.EvidenceAnnotationKey]; exists {
kind = v
}
edges = append(edges, artifact.Relationship{
From: p,
To: l.Coordinates,
Type: artifact.EvidentByRelationship,
Data: map[string]string{
"kind": kind,
},
})
}
}
@@ -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)
}
+31 -3
View File
@@ -11,8 +11,10 @@ import (
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/event/monitor"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/sbom"
)
type Executor struct {
@@ -35,6 +37,12 @@ func NewTaskExecutor(tasks []Task, numWorkers int) *Executor {
}
func (p *Executor) Execute(ctx context.Context, resolver file.Resolver, s sbomsync.Builder, prog *monitor.CatalogerTaskProgress) error {
var lock sync.Mutex
withLock := func(fn func()) {
lock.Lock()
defer lock.Unlock()
fn()
}
var errs error
wg := &sync.WaitGroup{}
for i := 0; i < p.numWorkers; i++ {
@@ -48,9 +56,16 @@ func (p *Executor) Execute(ctx context.Context, resolver file.Resolver, s sbomsy
return
}
if err := runTaskSafely(ctx, tsk, resolver, s); err != nil {
errs = multierror.Append(errs, fmt.Errorf("failed to run task: %w", err))
prog.SetError(err)
err := runTaskSafely(ctx, tsk, resolver, s)
unknowns, err := unknown.ExtractCoordinateErrors(err)
if len(unknowns) > 0 {
appendUnknowns(s, tsk.Name(), unknowns)
}
if err != nil {
withLock(func() {
errs = multierror.Append(errs, fmt.Errorf("failed to run task: %w", err))
prog.SetError(err)
})
}
prog.Increment()
}
@@ -62,6 +77,19 @@ func (p *Executor) Execute(ctx context.Context, resolver file.Resolver, s sbomsy
return errs
}
func appendUnknowns(builder sbomsync.Builder, taskName string, unknowns []unknown.CoordinateError) {
if accessor, ok := builder.(sbomsync.Accessor); ok {
accessor.WriteToSBOM(func(sb *sbom.SBOM) {
for _, u := range unknowns {
if sb.Artifacts.Unknowns == nil {
sb.Artifacts.Unknowns = map[file.Coordinates][]string{}
}
sb.Artifacts.Unknowns[u.Coordinates] = append(sb.Artifacts.Unknowns[u.Coordinates], formatUnknown(u.Reason.Error(), taskName))
}
})
}
}
func runTaskSafely(ctx context.Context, t Task, resolver file.Resolver, s sbomsync.Builder) (err error) {
// handle individual cataloger panics
defer func() {
+16 -21
View File
@@ -3,7 +3,6 @@ package task
import (
"context"
"crypto"
"fmt"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/artifact"
@@ -32,15 +31,12 @@ func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash
}
result, err := digestsCataloger.Catalog(ctx, resolver, coordinates...)
if err != nil {
return fmt.Errorf("unable to catalog file digests: %w", err)
}
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.FileDigests = result
})
return nil
return err
}
return NewTask("file-digest-cataloger", fn)
@@ -62,15 +58,12 @@ func NewFileMetadataCatalogerTask(selection file.Selection) Task {
}
result, err := metadataCataloger.Catalog(ctx, resolver, coordinates...)
if err != nil {
return err
}
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.FileMetadata = result
})
return nil
return err
}
return NewTask("file-metadata-cataloger", fn)
@@ -87,15 +80,12 @@ func NewFileContentCatalogerTask(cfg filecontent.Config) Task {
accessor := builder.(sbomsync.Accessor)
result, err := cat.Catalog(ctx, resolver)
if err != nil {
return err
}
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.FileContents = result
})
return nil
return err
}
return NewTask("file-content-cataloger", fn)
@@ -112,15 +102,12 @@ func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config)
accessor := builder.(sbomsync.Accessor)
result, err := cat.Catalog(resolver)
if err != nil {
return err
}
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.Executables = result
})
return nil
return err
}
return NewTask("file-executable-cataloger", fn)
@@ -134,9 +121,10 @@ func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accesso
}
if selection == file.FilesOwnedByPackageSelection {
var coordinates []file.Coordinates
var coordinates file.CoordinateSet
accessor.ReadFromSBOM(func(sbom *sbom.SBOM) {
// get any file coordinates that are owned by a package
for _, r := range sbom.Relationships {
if r.Type != artifact.ContainsRelationship {
continue
@@ -145,16 +133,23 @@ func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accesso
continue
}
if c, ok := r.To.(file.Coordinates); ok {
coordinates = append(coordinates, c)
coordinates.Add(c)
}
}
// get any file coordinates referenced by a package directly
for p := range sbom.Artifacts.Packages.Enumerate() {
coordinates.Add(p.Locations.CoordinateSet().ToSlice()...)
}
})
if len(coordinates) == 0 {
coords := coordinates.ToSlice()
if len(coords) == 0 {
return nil, false
}
return coordinates, true
return coords, true
}
return nil, false
+137 -40
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")
@@ -100,48 +103,12 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
t := bus.StartCatalogerTask(info, -1, "")
pkgs, relationships, err := c.Catalog(ctx, resolver)
if err != nil {
return fmt.Errorf("unable to catalog packages with %q: %w", catalogerName, err)
}
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...)
@@ -150,13 +117,143 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
t.SetCompleted()
log.WithFields("name", catalogerName).Trace("package cataloger completed")
return nil
return err
}
tags = append(tags, pkgcataloging.PackageTag)
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)
+114
View File
@@ -0,0 +1,114 @@
package task
import (
"context"
"strings"
"github.com/mholt/archiver/v3"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
const unknownsLabelerTaskName = "unknowns-labeler"
func NewUnknownsLabelerTask(cfg cataloging.UnknownsConfig) Task {
return NewTask(unknownsLabelerTaskName, unknownsLabelerTask{cfg}.processUnknowns)
}
type unknownsLabelerTask struct {
cataloging.UnknownsConfig
}
// processUnknowns removes unknown entries that have valid packages reported for the locations
func (c unknownsLabelerTask) processUnknowns(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
accessor := builder.(sbomsync.Accessor)
accessor.WriteToSBOM(func(s *sbom.SBOM) {
c.finalize(resolver, s)
})
return nil
}
func (c unknownsLabelerTask) finalize(resolver file.Resolver, s *sbom.SBOM) {
hasPackageReference := coordinateReferenceLookup(resolver, s)
if c.RemoveWhenPackagesDefined {
for coords := range s.Artifacts.Unknowns {
if !hasPackageReference(coords) {
continue
}
delete(s.Artifacts.Unknowns, coords)
}
}
if s.Artifacts.Unknowns == nil {
s.Artifacts.Unknowns = map[file.Coordinates][]string{}
}
if c.IncludeExecutablesWithoutPackages {
for coords := range s.Artifacts.Executables {
if !hasPackageReference(coords) {
s.Artifacts.Unknowns[coords] = append(s.Artifacts.Unknowns[coords], formatUnknown("no package identified in executable file", unknownsLabelerTaskName))
}
}
}
if c.IncludeUnexpandedArchives {
for coords := range s.Artifacts.FileMetadata {
unarchiver, notArchiveErr := archiver.ByExtension(coords.RealPath)
if unarchiver != nil && notArchiveErr == nil && !hasPackageReference(coords) {
s.Artifacts.Unknowns[coords] = append(s.Artifacts.Unknowns[coords], "archive not cataloged")
}
}
}
}
func formatUnknown(err string, task ...string) string {
return strings.Join(task, "/") + ": " + err
}
func coordinateReferenceLookup(resolver file.Resolver, s *sbom.SBOM) func(coords file.Coordinates) bool {
allPackageCoords := file.NewCoordinateSet()
// include all directly included locations that result in packages
for p := range s.Artifacts.Packages.Enumerate() {
allPackageCoords.Add(p.Locations.CoordinateSet().ToSlice()...)
}
// include owned files, for example specified by package managers.
// relationships for these owned files may be disabled, but we always want to include them
for p := range s.Artifacts.Packages.Enumerate() {
if f, ok := p.Metadata.(pkg.FileOwner); ok {
for _, ownedFilePath := range f.OwnedFiles() {
// resolve these owned files, as they may have symlinks
// but coordinates we will test against are always absolute paths
locations, err := resolver.FilesByPath(ownedFilePath)
if err != nil {
log.Debugf("unable to resolve owned file '%s': %v", ownedFilePath, err)
}
for _, loc := range locations {
allPackageCoords.Add(loc.Coordinates)
}
}
}
}
// include relationships
for _, r := range s.Relationships {
_, fromPkgOk := r.From.(pkg.Package)
fromFile, fromFileOk := r.From.(file.Coordinates)
_, toPkgOk := r.To.(pkg.Package)
toFile, toFileOk := r.To.(file.Coordinates)
if fromPkgOk && toFileOk {
allPackageCoords.Add(toFile)
} else if fromFileOk && toPkgOk {
allPackageCoords.Add(fromFile)
}
}
return allPackageCoords.Contains
}
+201
View File
@@ -0,0 +1,201 @@
package unknown
import (
"errors"
"fmt"
"strings"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
)
type hasCoordinates interface {
GetCoordinates() file.Coordinates
}
type CoordinateError struct {
Coordinates file.Coordinates
Reason error
}
var _ error = (*CoordinateError)(nil)
func (u *CoordinateError) Error() string {
if u.Coordinates.FileSystemID == "" {
return fmt.Sprintf("%s: %v", u.Coordinates.RealPath, u.Reason)
}
return fmt.Sprintf("%s (%s): %v", u.Coordinates.RealPath, u.Coordinates.FileSystemID, u.Reason)
}
// New returns a new CoordinateError unless the reason is a CoordinateError itself, in which case
// reason will be returned directly or if reason is nil, nil will be returned
func New(coords hasCoordinates, reason error) *CoordinateError {
if reason == nil {
return nil
}
coordinates := coords.GetCoordinates()
reasonCoordinateError := &CoordinateError{}
if errors.As(reason, &reasonCoordinateError) {
// if the reason is already a coordinate error, it is potentially for a different location,
// so we do not want to surface this location having an error
return reasonCoordinateError
}
return &CoordinateError{
Coordinates: coordinates,
Reason: reason,
}
}
// Newf returns a new CoordinateError with a reason of an error created from given format and args
func Newf(coords hasCoordinates, format string, args ...any) *CoordinateError {
return New(coords, fmt.Errorf(format, args...))
}
// Append returns an error joined to the first error/set of errors, with a new CoordinateError appended to the end
func Append(errs error, coords hasCoordinates, reason error) error {
return Join(errs, New(coords, reason))
}
// Appendf returns an error joined to the first error/set of errors, with a new CoordinateError appended to the end,
// created from the given reason and args
func Appendf(errs error, coords hasCoordinates, format string, args ...any) error {
return Append(errs, coords, fmt.Errorf(format, args...))
}
// Join joins the provided sets of errors together in a flattened manner, taking into account nested errors created
// from other sources, including errors.Join, multierror.Append, and unknown.Join
func Join(errs ...error) error {
var out []error
for _, err := range errs {
// append errors, de-duplicated
for _, e := range flatten(err) {
if containsErr(out, e) {
continue
}
out = append(out, e)
}
}
if len(out) == 1 {
return out[0]
}
if len(out) == 0 {
return nil
}
return errors.Join(out...)
}
// Joinf joins the provided sets of errors together in a flattened manner, taking into account nested errors created
// from other sources, including errors.Join, multierror.Append, and unknown.Join and appending a new error,
// created from the format and args provided -- the error is NOT a CoordinateError
func Joinf(errs error, format string, args ...any) error {
return Join(errs, fmt.Errorf(format, args...))
}
// IfEmptyf returns a new Errorf-formatted error, only when the provided slice is empty or nil when
// the slice has entries
func IfEmptyf[T any](emptyTest []T, format string, args ...any) error {
if len(emptyTest) == 0 {
return fmt.Errorf(format, args...)
}
return nil
}
// ExtractCoordinateErrors extracts all coordinate errors returned, and any _additional_ errors in the graph
// are encapsulated in the second, error return parameter
func ExtractCoordinateErrors(err error) (coordinateErrors []CoordinateError, remainingErrors error) {
remainingErrors = visitErrors(err, func(e error) error {
if coordinateError, _ := e.(*CoordinateError); coordinateError != nil {
coordinateErrors = append(coordinateErrors, *coordinateError)
return nil
}
return e
})
return coordinateErrors, remainingErrors
}
func flatten(errs ...error) []error {
var out []error
for _, err := range errs {
if err == nil {
continue
}
// turn all errors nested under a coordinate error to individual coordinate errors
if e, ok := err.(*CoordinateError); ok {
if e == nil {
continue
}
for _, r := range flatten(e.Reason) {
out = append(out, New(e.Coordinates, r))
}
} else
// from multierror.Append
if e, ok := err.(interface{ WrappedErrors() []error }); ok {
if e == nil {
continue
}
out = append(out, flatten(e.WrappedErrors()...)...)
} else
// from errors.Join
if e, ok := err.(interface{ Unwrap() []error }); ok {
if e == nil {
continue
}
out = append(out, flatten(e.Unwrap()...)...)
} else {
out = append(out, err)
}
}
return out
}
// containsErr returns true if a duplicate error is found
func containsErr(out []error, err error) bool {
defer func() {
if err := recover(); err != nil {
log.Tracef("error comparing errors: %v", err)
}
}()
for _, e := range out {
if e == err {
return true
}
}
return false
}
// visitErrors visits every wrapped error. the returned error replaces the provided error, null errors are omitted from
// the object graph
func visitErrors(err error, fn func(error) error) error {
// unwrap errors from errors.Join
if errs, ok := err.(interface{ Unwrap() []error }); ok {
var out []error
for _, e := range errs.Unwrap() {
out = append(out, visitErrors(e, fn))
}
// errors.Join omits nil errors and will return nil if all passed errors are nil
return errors.Join(out...)
}
// unwrap errors from multierror.Append -- these also implement Unwrap() error, so check this first
if errs, ok := err.(interface{ WrappedErrors() []error }); ok {
var out []error
for _, e := range errs.WrappedErrors() {
out = append(out, visitErrors(e, fn))
}
// errors.Join omits nil errors and will return nil if all passed errors are nil
return errors.Join(out...)
}
// unwrap singly wrapped errors
if e, ok := err.(interface{ Unwrap() error }); ok {
wrapped := e.Unwrap()
got := visitErrors(wrapped, fn)
if got == nil {
return nil
}
if wrapped.Error() != got.Error() {
prefix := strings.TrimSuffix(err.Error(), wrapped.Error())
return fmt.Errorf("%s%w", prefix, got)
}
return err
}
return fn(err)
}
+236
View File
@@ -0,0 +1,236 @@
package unknown
import (
"errors"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
)
func Test_visitErrors(t *testing.T) {
tests := []struct {
name string
in error
transform func(error) error
expected string
}{
{
name: "return",
in: fmt.Errorf("err1"),
transform: func(e error) error {
return e
},
expected: "err1",
},
{
name: "omit",
in: fmt.Errorf("err1"),
transform: func(_ error) error {
return nil
},
expected: "<nil>",
},
{
name: "wrapped return",
in: fmt.Errorf("wrapped: %w", fmt.Errorf("err1")),
transform: func(e error) error {
return e
},
expected: "wrapped: err1",
},
{
name: "wrapped omit",
in: fmt.Errorf("wrapped: %w", fmt.Errorf("err1")),
transform: func(e error) error {
if e.Error() == "err1" {
return nil
}
return e
},
expected: "<nil>",
},
{
name: "joined return",
in: errors.Join(fmt.Errorf("err1"), fmt.Errorf("err2")),
transform: func(e error) error {
return e
},
expected: "err1\nerr2",
},
{
name: "joined omit",
in: errors.Join(fmt.Errorf("err1"), fmt.Errorf("err2")),
transform: func(_ error) error {
return nil
},
expected: "<nil>",
},
{
name: "joined omit first",
in: errors.Join(fmt.Errorf("err1"), fmt.Errorf("err2")),
transform: func(e error) error {
if e.Error() == "err1" {
return nil
}
return e
},
expected: "err2",
},
{
name: "joined wrapped return",
in: errors.Join(fmt.Errorf("wrapped: %w", fmt.Errorf("err1")), fmt.Errorf("err2")),
transform: func(e error) error {
return e
},
expected: "wrapped: err1\nerr2",
},
{
name: "joined wrapped omit first",
in: errors.Join(fmt.Errorf("wrapped: %w", fmt.Errorf("err1")), fmt.Errorf("err2")),
transform: func(e error) error {
if e.Error() == "err1" {
return nil
}
return e
},
expected: "err2",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
gotErr := visitErrors(test.in, test.transform)
got := fmt.Sprintf("%v", gotErr)
require.Equal(t, test.expected, got)
})
}
}
func Test_Join(t *testing.T) {
err1 := fmt.Errorf("err1")
err2 := fmt.Errorf("err2")
tests := []struct {
name string ``
in []error
expected string
}{
{
name: "basic",
in: []error{fmt.Errorf("err")},
expected: "err",
},
{
name: "wrapped",
in: []error{fmt.Errorf("outer: %w", fmt.Errorf("err"))},
expected: "outer: err",
},
{
name: "wrapped joined",
in: []error{errors.Join(fmt.Errorf("outer: %w", fmt.Errorf("err1")), fmt.Errorf("err2"))},
expected: "outer: err1\nerr2",
},
{
name: "duplicates",
in: []error{err1, err1, err2},
expected: "err1\nerr2",
},
{
name: "nested duplicates",
in: []error{errors.Join(err1, err2), err1, err2},
expected: "err1\nerr2",
},
{
name: "nested duplicates coords",
in: []error{New(file.NewLocation("l1"), errors.Join(fmt.Errorf("err1"), fmt.Errorf("err2"))), fmt.Errorf("err1"), fmt.Errorf("err2")},
expected: "l1: err1\nl1: err2\nerr1\nerr2",
},
{
name: "all nil",
in: []error{nil, nil, nil},
expected: "",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := Join(test.in...)
if test.expected == "" {
require.Nil(t, got)
return
}
require.NotNil(t, got)
require.Equal(t, test.expected, got.Error())
})
}
}
func Test_flatten(t *testing.T) {
coords := file.Coordinates{
RealPath: "real/path",
}
e1 := fmt.Errorf("e1")
e2 := fmt.Errorf("e2")
c1 := New(coords, fmt.Errorf("c1"))
c2 := New(coords, fmt.Errorf("c2"))
tests := []struct {
name string ``
in error
expected string
}{
{
name: "basic",
in: errors.Join(e1, e2),
expected: "e1//e2",
},
{
name: "coords",
in: New(coords, e1),
expected: "real/path: e1",
},
{
name: "coords with joined children",
in: New(coords, errors.Join(e1, e2)),
expected: "real/path: e1//real/path: e2",
},
{
name: "very nested",
in: errors.Join(errors.Join(errors.Join(errors.Join(e1, c1), e2), c2), e2),
expected: "e1//real/path: c1//e2//real/path: c2//e2",
},
}
toString := func(errs ...error) string {
var parts []string
for _, e := range errs {
parts = append(parts, e.Error())
}
return strings.Join(parts, "//")
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := flatten(test.in)
require.NotNil(t, got)
require.Equal(t, test.expected, toString(got...))
})
}
}
func Test_Append(t *testing.T) {
e1 := New(file.NewLocation("l1"), fmt.Errorf("e1"))
e2 := Append(e1, file.NewLocation("l2"), fmt.Errorf("e2"))
e3 := Appendf(e2, file.NewLocation("l3"), "%s", "e3")
require.Equal(t, "l1: e1\nl2: e2\nl3: e3", e3.Error())
e1 = New(file.NewLocation("l1"), nil)
require.Nil(t, e1)
e2 = Append(e1, file.NewLocation("l2"), fmt.Errorf("e2"))
e3 = Appendf(e2, file.NewLocation("l3"), "%s", "e3")
require.Equal(t, "l2: e2\nl3: e3", e3.Error())
e1 = New(file.NewLocation("l1"), fmt.Errorf("e1"))
e2 = Append(e1, file.NewLocation("l2"), nil)
e3 = Appendf(e2, file.NewLocation("l3"), "%s", "e3")
require.Equal(t, "l1: e1\nl3: e3", e3.Error())
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+99 -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.18/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@@ -777,6 +777,12 @@
},
"executable": {
"$ref": "#/$defs/Executable"
},
"unknowns": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
@@ -955,6 +961,24 @@
"virtualPath"
]
},
"JavaJvmInstallation": {
"properties": {
"release": {
"$ref": "#/$defs/JavaVMRelease"
},
"files": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
"required": [
"release",
"files"
]
},
"JavaManifest": {
"properties": {
"main": {
@@ -1062,6 +1086,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 +1678,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
}
+15
View File
@@ -0,0 +1,15 @@
package cataloging
type UnknownsConfig struct {
RemoveWhenPackagesDefined bool
IncludeExecutablesWithoutPackages bool
IncludeUnexpandedArchives bool
}
func DefaultUnknownsConfig() UnknownsConfig {
return UnknownsConfig{
RemoveWhenPackagesDefined: true,
IncludeExecutablesWithoutPackages: true,
IncludeUnexpandedArchives: true,
}
}
+1 -1
View File
@@ -95,7 +95,7 @@ func NewAttributes(cpeStr string) (Attributes, error) {
}
// ensure that this Attributes can be validated after being fully sanitized
if ValidateString(c.String()) != nil {
if err = ValidateString(c.String()); err != nil {
return Attributes{}, err
}
+10 -2
View File
@@ -17,6 +17,7 @@ func Test_NewAttributes(t *testing.T) {
name string
input string
expected Attributes
wantErr require.ErrorAssertionFunc
}{
{
name: "gocase",
@@ -33,14 +34,21 @@ func Test_NewAttributes(t *testing.T) {
input: `cpe:/a:%240.99_kindle_books_project:%240.99_kindle_books:6::~~~android~~`,
expected: MustAttributes(`cpe:2.3:a:\$0.99_kindle_books_project:\$0.99_kindle_books:6:*:*:*:*:android:*:*`),
},
{
name: "null byte in version for some reason",
input: "cpe:2.3:a:oracle:openjdk:11.0.22+7\u0000-J-ms8m:*:*:*:*:*:*:*",
wantErr: require.Error,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual, err := NewAttributes(test.input)
if err != nil {
t.Fatalf("got an error while creating Attributes: %+v", err)
if test.wantErr != nil {
test.wantErr(t, err)
return
}
require.NoError(t, err)
if d := cmp.Diff(actual, test.expected); d != "" {
t.Errorf("Attributes mismatch (-want +got):\n%s", d)
+34
View File
@@ -19,8 +19,10 @@ 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
Unknowns cataloging.UnknownsConfig
DataGeneration cataloging.DataGenerationConfig
Packages pkgcataloging.Config
Files filecataloging.Config
@@ -38,6 +40,7 @@ type CreateSBOMConfig struct {
func DefaultCreateSBOMConfig() *CreateSBOMConfig {
return &CreateSBOMConfig{
Compliance: cataloging.DefaultComplianceConfig(),
Search: cataloging.DefaultSearchConfig(),
Relationships: cataloging.DefaultRelationshipsConfig(),
DataGeneration: cataloging.DefaultDataGenerationConfig(),
@@ -93,6 +96,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
@@ -105,6 +114,12 @@ func (c *CreateSBOMConfig) WithRelationshipsConfig(cfg cataloging.RelationshipsC
return c
}
// WithUnknownsConfig allows for defining the specific behavior dealing with unknowns
func (c *CreateSBOMConfig) WithUnknownsConfig(cfg cataloging.UnknownsConfig) *CreateSBOMConfig {
c.Unknowns = cfg
return c
}
// WithDataGenerationConfig allows for defining what data elements that cannot be discovered from the underlying
// target being scanned that should be generated after package creation.
func (c *CreateSBOMConfig) WithDataGenerationConfig(cfg cataloging.DataGenerationConfig) *CreateSBOMConfig {
@@ -165,6 +180,7 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
// generate package and file tasks based on the configuration
environmentTasks := c.environmentTasks()
relationshipsTasks := c.relationshipTasks(src)
unknownTasks := c.unknownsTasks()
fileTasks := c.fileTasks()
pkgTasks, selectionEvidence, err := c.packageTasks(src)
if err != nil {
@@ -184,6 +200,11 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
taskGroups = append(taskGroups, relationshipsTasks)
}
// all unknowns tasks should happen after all scanning is complete
if len(unknownTasks) > 0 {
taskGroups = append(taskGroups, unknownTasks)
}
// identifying the environment (i.e. the linux release) must be done first as this is required for package cataloging
taskGroups = append(
[][]task.Task{
@@ -225,6 +246,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)
@@ -329,6 +351,18 @@ func (c *CreateSBOMConfig) environmentTasks() []task.Task {
return tsks
}
// unknownsTasks returns a set of tasks that perform any necessary post-processing
// to identify SBOM elements as unknowns
func (c *CreateSBOMConfig) unknownsTasks() []task.Task {
var tasks []task.Task
if t := task.NewUnknownsLabelerTask(c.Unknowns); t != nil {
tasks = append(tasks, t)
}
return tasks
}
func (c *CreateSBOMConfig) validate() error {
if c.Relationships.ExcludeBinaryPackagesWithFileOwnershipOverlap {
if !c.Relationships.PackageFileOwnershipOverlap {
+15
View File
@@ -90,6 +90,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
pkgCatalogerNamesWithTagOrName(t, "image"),
fileCatalogerNames(true, true, true),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -108,6 +109,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
pkgCatalogerNamesWithTagOrName(t, "directory"),
fileCatalogerNames(true, true, true),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -127,6 +129,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
pkgCatalogerNamesWithTagOrName(t, "directory"),
fileCatalogerNames(true, true, true),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -145,6 +148,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
pkgCatalogerNamesWithTagOrName(t, "image"),
fileCatalogerNames(false, true, true), // note: the digest cataloger is not included
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -163,6 +167,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
pkgCatalogerNamesWithTagOrName(t, "image"),
// note: there are no file catalogers in their own group
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -184,6 +189,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
fileCatalogerNames(true, true, true)...,
),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -204,6 +210,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"),
fileCatalogerNames(true, true, true),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -224,6 +231,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"),
fileCatalogerNames(true, true, true),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -244,6 +252,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
addTo(pkgIntersect("image", "javascript"), "persistent"),
fileCatalogerNames(true, true, true),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -265,6 +274,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"),
fileCatalogerNames(true, true, true),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -285,6 +295,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
pkgCatalogerNamesWithTagOrName(t, "image"),
fileCatalogerNames(true, true, true),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
@@ -385,6 +396,10 @@ func relationshipCatalogerNames() []string {
return []string{"relationships-cataloger"}
}
func unknownsTaskNames() []string {
return []string{"unknowns-labeler"}
}
func environmentCatalogerNames() []string {
return []string{"environment-cataloger"}
}
+21 -16
View File
@@ -15,6 +15,7 @@ import (
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/mimetype"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/event/monitor"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
@@ -45,6 +46,8 @@ func NewCataloger(cfg Config) *Cataloger {
}
func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.Executable, error) {
var errs error
locs, err := resolver.FilesByMIMEType(i.config.MIMETypes...)
if err != nil {
return nil, fmt.Errorf("unable to get file locations for binaries: %w", err)
@@ -61,7 +64,10 @@ func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.E
for _, loc := range locs {
prog.AtomicStage.Set(loc.Path())
exec := processExecutableLocation(loc, resolver)
exec, err := processExecutableLocation(loc, resolver)
if err != nil {
errs = unknown.Append(errs, loc, err)
}
if exec != nil {
prog.Increment()
@@ -74,30 +80,24 @@ func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.E
prog.AtomicStage.Set(fmt.Sprintf("%s executables", humanize.Comma(prog.Current())))
prog.SetCompleted()
return results, nil
return results, errs
}
func processExecutableLocation(loc file.Location, resolver file.Resolver) *file.Executable {
func processExecutableLocation(loc file.Location, resolver file.Resolver) (*file.Executable, error) {
reader, err := resolver.FileContentsByLocation(loc)
if err != nil {
// TODO: known-unknowns
log.WithFields("error", err).Warnf("unable to get file contents for %q", loc.RealPath)
return nil
return nil, fmt.Errorf("unable to get file contents: %w", err)
}
defer internal.CloseAndLogError(reader, loc.RealPath)
uReader, err := unionreader.GetUnionReader(reader)
if err != nil {
// TODO: known-unknowns
log.WithFields("error", err).Warnf("unable to get union reader for %q", loc.RealPath)
return nil
return nil, fmt.Errorf("unable to get union reader: %w", err)
}
exec, err := processExecutable(loc, uReader)
if err != nil {
log.WithFields("error", err).Warnf("unable to process executable %q", loc.RealPath)
}
return exec
return processExecutable(loc, uReader)
}
func catalogingProgress(locations int64) *monitor.CatalogerTaskProgress {
@@ -153,10 +153,12 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
format, err := findExecutableFormat(reader)
if err != nil {
log.Debugf("unable to determine executable kind for %v: %v", loc.RealPath, err)
return nil, fmt.Errorf("unable to determine executable kind: %w", err)
}
if format == "" {
// this is not an "unknown", so just log -- this binary does not have parseable data in it
log.Debugf("unable to determine executable format for %q", loc.RealPath)
return nil, nil
}
@@ -165,16 +167,19 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
switch format {
case file.ELF:
if err := findELFFeatures(&data, reader); err != nil {
if err = findELFFeatures(&data, reader); err != nil {
log.WithFields("error", err).Tracef("unable to determine ELF features for %q", loc.RealPath)
err = fmt.Errorf("unable to determine ELF features: %w", err)
}
case file.PE:
if err := findPEFeatures(&data, reader); err != nil {
if err = findPEFeatures(&data, reader); err != nil {
log.WithFields("error", err).Tracef("unable to determine PE features for %q", loc.RealPath)
err = fmt.Errorf("unable to determine PE features: %w", err)
}
case file.MachO:
if err := findMachoFeatures(&data, reader); err != nil {
if err = findMachoFeatures(&data, reader); err != nil {
log.WithFields("error", err).Tracef("unable to determine Macho features for %q", loc.RealPath)
err = fmt.Errorf("unable to determine Macho features: %w", err)
}
}
@@ -183,7 +188,7 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
data.ImportedLibraries = []string{}
}
return &data, nil
return &data, err
}
func findExecutableFormat(reader unionreader.UnionReader) (file.ExecutableFormat, error) {
+5 -9
View File
@@ -8,6 +8,7 @@ import (
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
)
@@ -20,8 +21,8 @@ func findELFFeatures(data *file.Executable, reader unionreader.UnionReader) erro
libs, err := f.ImportedLibraries()
if err != nil {
// TODO: known-unknowns
log.WithFields("error", err).Trace("unable to read imported libraries from elf file")
err = unknown.Joinf(err, "unable to read imported libraries from elf file: %w", err)
libs = nil
}
@@ -34,7 +35,7 @@ func findELFFeatures(data *file.Executable, reader unionreader.UnionReader) erro
data.HasEntrypoint = elfHasEntrypoint(f)
data.HasExports = elfHasExports(f)
return nil
return err
}
func findELFSecurityFeatures(f *elf.File) *file.ELFSecurityFeatures {
@@ -62,7 +63,6 @@ func checkElfStackCanary(file *elf.File) *bool {
func hasAnyDynamicSymbols(file *elf.File, symbolNames ...string) *bool {
dynSyms, err := file.DynamicSymbols()
if err != nil {
// TODO: known-unknowns
log.WithFields("error", err).Trace("unable to read dynamic symbols from elf file")
return nil
}
@@ -129,7 +129,6 @@ func hasBindNowDynTagOrFlag(f *elf.File) bool {
func hasElfDynFlag(f *elf.File, flag elf.DynFlag) bool {
vals, err := f.DynValue(elf.DT_FLAGS)
if err != nil {
// TODO: known-unknowns
log.WithFields("error", err).Trace("unable to read DT_FLAGS from elf file")
return false
}
@@ -144,7 +143,6 @@ func hasElfDynFlag(f *elf.File, flag elf.DynFlag) bool {
func hasElfDynFlag1(f *elf.File, flag elf.DynFlag1) bool {
vals, err := f.DynValue(elf.DT_FLAGS_1)
if err != nil {
// TODO: known-unknowns
log.WithFields("error", err).Trace("unable to read DT_FLAGS_1 from elf file")
return false
}
@@ -175,7 +173,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 {
@@ -203,7 +201,6 @@ func checkLLVMControlFlowIntegrity(file *elf.File) *bool {
// look for any symbols that are functions and end with ".cfi"
dynSyms, err := file.Symbols()
if err != nil {
// TODO: known-unknowns
log.WithFields("error", err).Trace("unable to read symbols from elf file")
return nil
}
@@ -225,7 +222,6 @@ var fortifyPattern = regexp.MustCompile(`__\w+_chk@.+`)
func checkClangFortifySource(file *elf.File) *bool {
dynSyms, err := file.Symbols()
if err != nil {
// TODO: known-unknowns
log.WithFields("error", err).Trace("unable to read symbols from elf file")
return nil
}
@@ -254,7 +250,7 @@ func elfHasExports(f *elf.File) bool {
// really anything that is not marked with 'U' (undefined) is considered an export.
symbols, err := f.DynamicSymbols()
if err != nil {
// TODO: known-unknowns?
log.WithFields("error", err).Trace("unable to get ELF dynamic symbols")
return false
}
@@ -27,6 +27,7 @@ func Test_findELFSecurityFeatures(t *testing.T) {
name string
fixture string
want *file.ELFSecurityFeatures
wantErr require.ErrorAssertionFunc
wantStripped bool
}{
{
@@ -221,6 +222,7 @@ func Test_elfHasExports(t *testing.T) {
f, err := elf.NewFile(readerForFixture(t, tt.fixture))
require.NoError(t, err)
assert.Equal(t, tt.want, elfHasExports(f))
require.NoError(t, err)
})
}
}
+8 -5
View File
@@ -13,6 +13,7 @@ import (
"github.com/anchore/syft/internal/bus"
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/event/monitor"
"github.com/anchore/syft/syft/file"
)
@@ -46,6 +47,7 @@ func NewCataloger(cfg Config) *Cataloger {
func (i *Cataloger) Catalog(_ context.Context, resolver file.Resolver) (map[file.Coordinates]string, error) {
results := make(map[file.Coordinates]string)
var locations []file.Location
var errs error
locations, err := resolver.FilesByGlob(i.globs...)
if err != nil {
@@ -59,8 +61,9 @@ func (i *Cataloger) Catalog(_ context.Context, resolver file.Resolver) (map[file
metadata, err := resolver.FileMetadataByLocation(location)
if err != nil {
errs = unknown.Append(errs, location, err)
prog.SetError(err)
return nil, err
continue
}
if i.skipFilesAboveSizeInBytes > 0 && metadata.Size() > i.skipFilesAboveSizeInBytes {
@@ -69,12 +72,12 @@ func (i *Cataloger) Catalog(_ context.Context, resolver file.Resolver) (map[file
result, err := i.catalogLocation(resolver, location)
if internal.IsErrPathPermission(err) {
log.Debugf("file contents cataloger skipping - %+v", err)
errs = unknown.Append(errs, location, fmt.Errorf("permission error reading file contents: %w", err))
continue
}
if err != nil {
prog.SetError(err)
return nil, err
errs = unknown.Append(errs, location, err)
continue
}
prog.Increment()
@@ -87,7 +90,7 @@ func (i *Cataloger) Catalog(_ context.Context, resolver file.Resolver) (map[file
prog.AtomicStage.Set(fmt.Sprintf("%s files", humanize.Comma(prog.Current())))
prog.SetCompleted()
return results, nil
return results, errs
}
func (i *Cataloger) catalogLocation(resolver file.Resolver, location file.Location) (string, error) {
+6 -2
View File
@@ -13,6 +13,7 @@ import (
"github.com/anchore/syft/internal/bus"
intFile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/event/monitor"
"github.com/anchore/syft/syft/file"
intCataloger "github.com/anchore/syft/syft/file/cataloger/internal"
@@ -33,6 +34,7 @@ func NewCataloger(hashes []crypto.Hash) *Cataloger {
func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates][]file.Digest, error) {
results := make(map[file.Coordinates][]file.Digest)
var locations []file.Location
var errs error
if len(coordinates) == 0 {
locations = intCataloger.AllRegularFiles(ctx, resolver)
@@ -58,12 +60,14 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
if internal.IsErrPathPermission(err) {
log.Debugf("file digests cataloger skipping %q: %+v", location.RealPath, err)
errs = unknown.Append(errs, location, err)
continue
}
if err != nil {
prog.SetError(err)
return nil, fmt.Errorf("failed to process file %q: %w", location.RealPath, err)
errs = unknown.Append(errs, location, err)
continue
}
prog.Increment()
@@ -76,7 +80,7 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
prog.AtomicStage.Set(fmt.Sprintf("%s files", humanize.Comma(prog.Current())))
prog.SetCompleted()
return results, nil
return results, errs
}
func (i *Cataloger) catalogLocation(resolver file.Resolver, location file.Location) ([]file.Digest, error) {
@@ -8,6 +8,7 @@ import (
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/event/monitor"
"github.com/anchore/syft/syft/file"
)
@@ -20,6 +21,7 @@ func NewCataloger() *Cataloger {
}
func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates]file.Metadata, error) {
var errs error
results := make(map[file.Coordinates]file.Metadata)
var locations <-chan file.Location
ctx, cancel := context.WithCancel(ctx)
@@ -34,7 +36,7 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
for _, c := range coordinates {
locs, err := resolver.FilesByPath(c.RealPath)
if err != nil {
log.Warn("unable to get file locations for path %q: %w", c.RealPath, err)
errs = unknown.Append(errs, c, err)
continue
}
for _, loc := range locs {
@@ -71,7 +73,7 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
prog.AtomicStage.Set(fmt.Sprintf("%s locations", humanize.Comma(prog.Current())))
prog.SetCompleted()
return results, nil
return results, errs
}
func catalogingProgress(locations int64) *monitor.CatalogerTaskProgress {
+4
View File
@@ -39,3 +39,7 @@ func (c Coordinates) String() string {
}
return fmt.Sprintf("Location<%s>", str)
}
func (c Coordinates) GetCoordinates() Coordinates {
return c
}
@@ -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
View File
@@ -221,6 +221,7 @@ func Test_encodeDecodeFileMetadata(t *testing.T) {
},
},
},
Unknowns: map[file.Coordinates][]string{},
Executables: map[file.Coordinates]file.Executable{
c: {
Format: file.ELF,
+1
View File
@@ -13,6 +13,7 @@ type File struct {
Digests []file.Digest `json:"digests,omitempty"`
Licenses []FileLicense `json:"licenses,omitempty"`
Executable *file.Executable `json:"executable,omitempty"`
Unknowns []string `json:"unknowns,omitempty"`
}
type FileMetadataEntry struct {
+6
View File
@@ -101,6 +101,11 @@ func toFile(s sbom.SBOM) []model.File {
contents = contentsForLocation
}
var unknowns []string
if unknownsForLocation, exists := artifacts.Unknowns[coordinates]; exists {
unknowns = unknownsForLocation
}
var licenses []model.FileLicense
for _, l := range artifacts.FileLicenses[coordinates] {
var evidence *model.FileLicenseEvidence
@@ -132,6 +137,7 @@ func toFile(s sbom.SBOM) []model.File {
Contents: contents,
Licenses: licenses,
Executable: executable,
Unknowns: unknowns,
})
}
+8 -1
View File
@@ -38,6 +38,7 @@ func toSyftModel(doc model.Document) *sbom.SBOM {
FileContents: fileArtifacts.FileContents,
FileLicenses: fileArtifacts.FileLicenses,
Executables: fileArtifacts.Executables,
Unknowns: fileArtifacts.Unknowns,
LinuxDistribution: toSyftLinuxRelease(doc.Distro),
},
Source: *toSyftSourceData(doc.Source),
@@ -66,6 +67,7 @@ func deduplicateErrors(errors []error) []string {
return errorMessages
}
//nolint:funlen
func toSyftFiles(files []model.File) sbom.Artifacts {
ret := sbom.Artifacts{
FileMetadata: make(map[file.Coordinates]file.Metadata),
@@ -73,6 +75,7 @@ func toSyftFiles(files []model.File) sbom.Artifacts {
FileContents: make(map[file.Coordinates]string),
FileLicenses: make(map[file.Coordinates][]file.License),
Executables: make(map[file.Coordinates]file.Executable),
Unknowns: make(map[file.Coordinates][]string),
}
for _, f := range files {
@@ -130,6 +133,10 @@ func toSyftFiles(files []model.File) sbom.Artifacts {
if f.Executable != nil {
ret.Executables[coord] = *f.Executable
}
if len(f.Unknowns) > 0 {
ret.Unknowns[coord] = f.Unknowns
}
}
return ret
@@ -146,7 +153,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) {
@@ -234,6 +234,7 @@ func Test_toSyftFiles(t *testing.T) {
FileMetadata: map[file.Coordinates]file.Metadata{},
FileDigests: map[file.Coordinates][]file.Digest{},
Executables: map[file.Coordinates]file.Executable{},
Unknowns: make(map[file.Coordinates][]string),
},
},
{
@@ -349,6 +350,7 @@ func Test_toSyftFiles(t *testing.T) {
t.Run(tt.name, func(t *testing.T) {
tt.want.FileContents = make(map[file.Coordinates]string)
tt.want.FileLicenses = make(map[file.Coordinates][]file.License)
tt.want.Unknowns = make(map[file.Coordinates][]string)
assert.Equal(t, tt.want, toSyftFiles(tt.files))
})
}
@@ -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
}
+7 -6
View File
@@ -93,12 +93,13 @@ func (ps pathSkipper) pathIndexVisitor(_ string, givenPath string, _ os.FileInfo
for _, mi := range ps.mounts {
conditionalPaths, ignorable := ps.ignorableMountTypes[mi.FSType]
if len(conditionalPaths) == 0 {
// Rule 1: ignore any path within a mount point that is of the given filesystem type unconditionally
if !containsPath(givenPath, mi.Mountpoint) {
continue
}
// Rule 0: Make sure the given path is within the mount point; if not let the scan continue
if !containsPath(givenPath, mi.Mountpoint) {
continue
}
// Rule 1: ignore any path within a mount point that is of the given filesystem type unconditionally
if len(conditionalPaths) == 0 {
if !ignorable {
// we've matched on the most specific path at this point, which means we should stop searching
// mount points for this path
@@ -151,7 +152,7 @@ func simpleClean(p string) string {
return "."
}
if p == "/" {
return "/"
return ""
}
return strings.TrimSuffix(p, "/")
}
@@ -358,6 +358,43 @@ func Test_newPathSkipper(t *testing.T) {
},
},
},
{
name: "buildkit github ubuntu 22.04",
root: "/run/src/core/sbom",
mounts: []*mountinfo.Info{
{Mountpoint: "/", FSType: "overlay"},
{Mountpoint: "/proc", FSType: "proc"},
{Mountpoint: "/dev", FSType: "tmpfs"},
{Mountpoint: "/dev/pts", FSType: "devpts"},
{Mountpoint: "/dev/shm", FSType: "tmpfs"},
{Mountpoint: "/dev/mqueue", FSType: "mqueue"},
{Mountpoint: "/sys", FSType: "sysfs"},
{Mountpoint: "/etc/resolv.conf", FSType: "ext4"},
{Mountpoint: "/etc/hosts", FSType: "ext4"},
{Mountpoint: "/sys/fs/cgroup", FSType: "cgroup2"},
{Mountpoint: "/run/out", FSType: "ext4"},
{Mountpoint: "/run/src/core/sbom", FSType: "overlay"},
{Mountpoint: "/tmp", FSType: "tmpfs"},
{Mountpoint: "/dev/otel-grpc.sock", FSType: "overlay"},
{Mountpoint: "/proc/bus", FSType: "proc"},
{Mountpoint: "/proc/fs", FSType: "proc"},
{Mountpoint: "/proc/irq", FSType: "proc"},
{Mountpoint: "/proc/sys", FSType: "proc"},
{Mountpoint: "/proc/sysrq-trigger", FSType: "proc"},
{Mountpoint: "/proc/acpi", FSType: "tmpfs"},
{Mountpoint: "/proc/kcore", FSType: "tmpfs"},
{Mountpoint: "/proc/keys", FSType: "tmpfs"},
{Mountpoint: "/proc/latency_stats", FSType: "tmpfs"},
{Mountpoint: "/proc/timer_list", FSType: "tmpfs"},
{Mountpoint: "/sys/firmware", FSType: "tmpfs"},
{Mountpoint: "/proc/scsi", FSType: "tmpfs"},
},
want: []expect{
{
path: "/run/src/core/sbom",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -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"),
@@ -190,6 +190,14 @@ func TestApkDBCataloger(t *testing.T) {
}
func Test_corruptDb(t *testing.T) {
pkgtest.NewCatalogTester().
FromDirectory(t, "test-fixtures/corrupt").
WithCompareOptions(cmpopts.IgnoreFields(pkg.ApkDBEntry{}, "Files", "GitCommit", "Checksum")).
WithError().
TestCataloger(t, NewDBCataloger())
}
func TestCatalogerDependencyTree(t *testing.T) {
assertion := func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
expected := map[string][]string{
+5 -1
View File
@@ -12,6 +12,7 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/linux"
@@ -38,6 +39,7 @@ type parsedData struct {
func parseApkDB(_ context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
scanner := bufio.NewScanner(reader)
var errs error
var apks []parsedData
var currentEntry parsedData
entryParsingInProgress := false
@@ -81,10 +83,12 @@ func parseApkDB(_ context.Context, resolver file.Resolver, env *generic.Environm
field := parseApkField(line)
if field == nil {
log.Warnf("unable to parse field data from line %q", line)
errs = unknown.Appendf(errs, reader, "unable to parse field data from line %q", line)
continue
}
if len(field.name) == 0 {
log.Warnf("failed to parse field name from line %q", line)
errs = unknown.Appendf(errs, reader, "failed to parse field name from line %q", line)
continue
}
if len(field.value) == 0 {
@@ -131,7 +135,7 @@ func parseApkDB(_ context.Context, resolver file.Resolver, env *generic.Environm
pkgs = append(pkgs, newPackage(apk, r, reader.Location))
}
return pkgs, nil, nil
return pkgs, nil, errs
}
func findReleases(resolver file.Resolver, dbPath string) []linux.Release {
@@ -0,0 +1,43 @@
this is a corrupt db
C:Q1v4QhLje3kWlC8DJj+ZfJTjlJRSU=
P:alpine-baselayout-data
V:3.2.0-r22
A:x86_64
S:11435
I:73728
o:alpine-baselayout
t:1655134784
c:cb70ca5c6d6db0399d2dd09189c5d57827bce5cd
r:alpine-baselayout
F:etc
R:fstab
Z:Q11Q7hNe8QpDS531guqCdrXBzoA/o=
R:group
Z:Q13K+olJg5ayzHSVNUkggZJXuB+9Y=
R:hostname
Z:Q16nVwYVXP/tChvUPdukVD2ifXOmc=
R:hosts
Z:Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=
R:inittab
Z:Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=
R:modules
Z:Q1toogjUipHGcMgECgPJX64SwUT1M=
R:mtab
a:0:0:777
Z:Q1kiljhXXH1LlQroHsEJIkPZg2eiw=
R:passwd
Z:Q1TchuuLUfur0izvfZQZxgN/LJhB8=
R:profile
Z:Q1F3DgXUP+jNZDknmQPPb5t9FSfDg=
R:protocols
Z:Q1omKlp3vgGq2ZqYzyD/KHNdo8rDc=
R:services
Z:Q19WLCv5ItKg4MH7RWfNRh1I7byQc=
R:shadow
a:0:42:640
Z:Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=
R:shells
Z:Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=
R:sysctl.conf
Z:Q14upz3tfnNxZkIEsUhWn7Xoiw96g=
@@ -11,6 +11,14 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
)
func TestAlpmUnknowns(t *testing.T) {
pkgtest.NewCatalogTester().
FromDirectory(t, "test-fixtures/installed").
WithCompareOptions(cmpopts.IgnoreFields(pkg.AlpmFileRecord{}, "Time")).
WithError().
TestCataloger(t, NewDBCataloger())
}
func TestAlpmCataloger(t *testing.T) {
gmpDbLocation := file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc")
treeSitterDbLocation := file.NewLocation("var/lib/pacman/local/tree-sitter-0.22.6-1/desc")
+31 -33
View File
@@ -17,6 +17,7 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -41,6 +42,8 @@ type parsedData struct {
// parseAlpmDB parses the arch linux pacman database flat-files and returns the packages and relationships found within.
func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
var errs error
data, err := parseAlpmDBEntry(reader)
if err != nil {
return nil, nil, err
@@ -52,24 +55,25 @@ func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environ
base := path.Dir(reader.RealPath)
var locs []file.Location
// replace the files found the pacman database with the files from the mtree These contain more metadata and
// thus more useful.
files, fileLoc := fetchPkgFiles(base, resolver)
backups, backupLoc := fetchBackupFiles(base, resolver)
var locs []file.Location
if fileLoc != nil {
files, fileLoc, err := fetchPkgFiles(base, resolver)
errs = unknown.Join(errs, err)
if err == nil {
locs = append(locs, fileLoc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
data.Files = files
}
if backupLoc != nil {
backups, backupLoc, err := fetchBackupFiles(base, resolver)
errs = unknown.Join(errs, err)
if err == nil {
locs = append(locs, backupLoc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
data.Backup = backups
}
if data.Package == "" {
return nil, nil, nil
return nil, nil, errs
}
return []pkg.Package{
@@ -79,63 +83,56 @@ func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environ
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
locs...,
),
}, nil, nil
}, nil, errs
}
func fetchPkgFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, *file.Location) {
func fetchPkgFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, file.Location, error) {
// TODO: probably want to use MTREE and PKGINFO here
target := path.Join(base, "mtree")
loc, err := getLocation(target, resolver)
if err != nil {
log.WithFields("error", err, "path", target).Trace("failed to find mtree file")
return []pkg.AlpmFileRecord{}, nil
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to find mtree file: %w", err))
}
if loc == nil {
return []pkg.AlpmFileRecord{}, nil
}
reader, err := resolver.FileContentsByLocation(*loc)
reader, err := resolver.FileContentsByLocation(loc)
if err != nil {
return []pkg.AlpmFileRecord{}, nil
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to get contents: %w", err))
}
defer internal.CloseAndLogError(reader, loc.RealPath)
pkgFiles, err := parseMtree(reader)
if err != nil {
log.WithFields("error", err, "path", target).Trace("failed to parse mtree file")
return []pkg.AlpmFileRecord{}, nil
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to parse mtree: %w", err))
}
return pkgFiles, loc
return pkgFiles, loc, nil
}
func fetchBackupFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, *file.Location) {
func fetchBackupFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, file.Location, error) {
// We only really do this to get any backup database entries from the files database
target := filepath.Join(base, "files")
loc, err := getLocation(target, resolver)
if err != nil {
log.WithFields("error", err, "path", target).Trace("failed to find alpm files")
return []pkg.AlpmFileRecord{}, nil
}
if loc == nil {
return []pkg.AlpmFileRecord{}, nil
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to find alpm files: %w", err))
}
reader, err := resolver.FileContentsByLocation(*loc)
reader, err := resolver.FileContentsByLocation(loc)
if err != nil {
return []pkg.AlpmFileRecord{}, nil
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to get contents: %w", err))
}
defer internal.CloseAndLogError(reader, loc.RealPath)
filesMetadata, err := parseAlpmDBEntry(reader)
if err != nil {
return []pkg.AlpmFileRecord{}, nil
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to parse alpm db entry: %w", err))
}
if filesMetadata != nil {
return filesMetadata.Backup, loc
return filesMetadata.Backup, loc, nil
}
return []pkg.AlpmFileRecord{}, loc
return []pkg.AlpmFileRecord{}, loc, nil
}
func parseAlpmDBEntry(reader io.Reader) (*parsedData, error) {
@@ -171,20 +168,21 @@ func newScanner(reader io.Reader) *bufio.Scanner {
return scanner
}
func getLocation(path string, resolver file.Resolver) (*file.Location, error) {
func getLocation(path string, resolver file.Resolver) (file.Location, error) {
loc := file.NewLocation(path)
locs, err := resolver.FilesByPath(path)
if err != nil {
return nil, err
return loc, err
}
if len(locs) == 0 {
return nil, fmt.Errorf("could not find file: %s", path)
return loc, fmt.Errorf("could not find file: %s", path)
}
if len(locs) > 1 {
log.WithFields("path", path).Trace("multiple files found for path, using first path")
}
return &locs[0], nil
return locs[0], nil
}
func parseDatabase(b *bufio.Scanner) (*parsedData, error) {
@@ -0,0 +1,34 @@
%NME%
tree-sitter
%VER.6-1
%BASE%
tree-sitter
%DESC%
Incremental parsing library
%BUILDDATE%
1714945746
%INSTALLDATE%
1715026360
%PACKA@archlinux.org>
%SIZE%
223539
%REASON%
1
%LICENSE%
MIT
%VALIDATION%
pgp
%PROVIDE
libtree-sitter.so=0-64
@@ -6,8 +6,10 @@ package binary
import (
"context"
"encoding/json"
"fmt"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -59,12 +61,14 @@ func (c cataloger) Name() string {
func (c cataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
var packages []pkg.Package
var relationships []artifact.Relationship
var errs error
for _, cls := range c.classifiers {
log.WithFields("classifier", cls.Class).Trace("cataloging binaries")
newPkgs, err := catalog(resolver, cls)
if err != nil {
log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err)
log.WithFields("error", err, "classifier", cls.Class).Debugf("unable to catalog binary package: %v", err)
errs = unknown.Join(errs, fmt.Errorf("%s: %w", cls.Class, err))
continue
}
newPackages:
@@ -82,7 +86,7 @@ func (c cataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Pac
}
}
return packages, relationships, nil
return packages, relationships, errs
}
// mergePackages merges information from the extra package into the target package
@@ -98,6 +102,7 @@ func mergePackages(target *pkg.Package, extra *pkg.Package) {
}
func catalog(resolver file.Resolver, cls Classifier) (packages []pkg.Package, err error) {
var errs error
locations, err := resolver.FilesByGlob(cls.FileGlob)
if err != nil {
return nil, err
@@ -105,11 +110,12 @@ func catalog(resolver file.Resolver, cls Classifier) (packages []pkg.Package, er
for _, location := range locations {
pkgs, err := cls.EvidenceMatcher(cls, matcherContext{resolver: resolver, location: location})
if err != nil {
return nil, err
errs = unknown.Append(errs, location, err)
continue
}
packages = append(packages, pkgs...)
}
return packages, nil
return packages, errs
}
// packagesMatch returns true if the binary packages "match" based on basic criteria
@@ -348,6 +348,17 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("haproxy-binary"),
},
},
{
logicalFixture: "haproxy/2.0.0/linux-amd64",
expected: pkg.Package{
Name: "haproxy",
Version: "2.0.0",
Type: "binary",
PURL: "pkg:generic/haproxy@2.0.0",
Locations: locations("haproxy"),
Metadata: metadata("haproxy-binary"),
},
},
{
logicalFixture: "haproxy/2.7.3/linux-amd64",
expected: pkg.Package{
@@ -485,6 +496,17 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("redis-binary"),
},
},
{
logicalFixture: "redis-server/7.2.5/linux-386",
expected: pkg.Package{
Name: "redis",
Version: "7.2.5",
Type: "binary",
PURL: "pkg:generic/redis@7.2.5",
Locations: locations("redis-server"),
Metadata: metadata("redis-binary"),
},
},
{
logicalFixture: "python-shared-lib/3.7.4/linux-amd64",
expected: pkg.Package{
@@ -625,6 +647,36 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("go-binary"),
},
},
{
logicalFixture: "node/0.10.48/linux-amd64",
expected: pkg.Package{
Name: "node",
Version: "0.10.48",
PURL: "pkg:generic/node@0.10.48",
Locations: locations("node"),
Metadata: metadata("nodejs-binary"),
},
},
{
logicalFixture: "node/0.12.18/linux-amd64",
expected: pkg.Package{
Name: "node",
Version: "0.12.18",
PURL: "pkg:generic/node@0.12.18",
Locations: locations("node"),
Metadata: metadata("nodejs-binary"),
},
},
{
logicalFixture: "node/4.9.1/linux-amd64",
expected: pkg.Package{
Name: "node",
Version: "4.9.1",
PURL: "pkg:generic/node@4.9.1",
Locations: locations("node"),
Metadata: metadata("nodejs-binary"),
},
},
{
logicalFixture: "node/19.2.0/linux-amd64",
expected: pkg.Package{
@@ -850,6 +902,60 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
},
},
},
{
// note: dynamic (non-snippet) test case
name: "positive-ruby-3.4.0-dev",
logicalFixture: "ruby-shared-libs/3.4.0-dev/linux-amd64",
expected: pkg.Package{
Name: "ruby",
Version: "3.4.0dev",
Type: "binary",
PURL: "pkg:generic/ruby@3.4.0dev",
Locations: locations("ruby", "libruby.so.3.4.0"),
Metadata: pkg.BinarySignature{
Matches: []pkg.ClassifierMatch{
match("ruby-binary", "ruby"),
match("ruby-binary", "libruby.so.3.4.0"),
},
},
},
},
{
// note: dynamic (non-snippet) test case
name: "positive-ruby-3.4.0-preview1",
logicalFixture: "ruby-shared-libs/3.4.0-preview1/linux-amd64",
expected: pkg.Package{
Name: "ruby",
Version: "3.4.0preview1",
Type: "binary",
PURL: "pkg:generic/ruby@3.4.0preview1",
Locations: locations("ruby", "libruby.so.3.4.0"),
Metadata: pkg.BinarySignature{
Matches: []pkg.ClassifierMatch{
match("ruby-binary", "ruby"),
match("ruby-binary", "libruby.so.3.4.0"),
},
},
},
},
{
// note: dynamic (non-snippet) test case
name: "positive-ruby-3.3.0-rc1",
logicalFixture: "ruby-shared-libs/3.3.0-rc1/linux-amd64",
expected: pkg.Package{
Name: "ruby",
Version: "3.3.0rc1",
Type: "binary",
PURL: "pkg:generic/ruby@3.3.0rc1",
Locations: locations("ruby", "libruby.so.3.3.0"),
Metadata: pkg.BinarySignature{
Matches: []pkg.ClassifierMatch{
match("ruby-binary", "ruby"),
match("ruby-binary", "libruby.so.3.3.0"),
},
},
},
},
{
// note: dynamic (non-snippet) test case
logicalFixture: "ruby-bullseye-shared-libs/2.7.7/linux-amd64",
@@ -961,6 +1067,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 +1210,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 {
@@ -1452,7 +1668,7 @@ func Test_Cataloger_ResilientToErrors(t *testing.T) {
resolver := &panicyResolver{}
_, _, err := c.Catalog(context.Background(), resolver)
assert.NoError(t, err)
assert.Error(t, err)
assert.True(t, resolver.searchCalled)
}
+94 -4
View File
@@ -78,7 +78,7 @@ func DefaultClassifiers() []Classifier {
FileGlob: "**/redis-server",
EvidenceMatcher: evidenceMatchers(
FileContentsVersionMatcher(`(?s)payload %5.*?(?P<version>\d.\d\.\d\d*)[a-z0-9]{12,15}-[0-9]{19}`),
FileContentsVersionMatcher(`(?s)\x00(?P<version>\d.\d\.\d\d*)[a-z0-9]{12}-[0-9]{19}\x00.*?payload %5`),
FileContentsVersionMatcher(`(?s)\x00(?P<version>\d.\d\.\d\d*)[a-z0-9]{12,15}-[0-9]{19}\x00.*?payload %5`),
),
Package: "redis",
PURL: mustPURL("pkg:generic/redis@version"),
@@ -153,8 +153,14 @@ func DefaultClassifiers() []Classifier {
{
Class: "nodejs-binary",
FileGlob: "**/node",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)node\.js\/v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
EvidenceMatcher: evidenceMatchers(
// [NUL]node v0.10.48[NUL]
// [NUL]v0.12.18[NUL]
// [NUL]v4.9.1[NUL]
// node.js/v22.9.0
FileContentsVersionMatcher(`(?m)\x00(node )?v(?P<version>(0|4|5)\.[0-9]+\.[0-9]+)\x00`),
FileContentsVersionMatcher(`(?m)node\.js\/v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
),
Package: "node",
PURL: mustPURL("pkg:generic/node@version"),
CPEs: singleCPE("cpe:2.3:a:nodejs:node.js:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
@@ -190,6 +196,7 @@ func DefaultClassifiers() []Classifier {
Class: "haproxy-binary",
FileGlob: "**/haproxy",
EvidenceMatcher: evidenceMatchers(
FileContentsVersionMatcher(`(?m)version (?P<version>[0-9]+\.[0-9]+(\.|-dev|-rc)[0-9]+)(-[a-z0-9]{7})?, released 20`),
FileContentsVersionMatcher(`(?m)HA-Proxy version (?P<version>[0-9]+\.[0-9]+(\.|-dev)[0-9]+)`),
FileContentsVersionMatcher(`(?m)(?P<version>[0-9]+\.[0-9]+(\.|-dev)[0-9]+)-[0-9a-zA-Z]{7}.+HAProxy version`),
),
@@ -447,6 +454,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 +591,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),
},
}
}
@@ -586,6 +673,9 @@ var libpythonMatcher = fileNameTemplateVersionMatcher(
)
var rubyMatcher = FileContentsVersionMatcher(
// ruby 3.4.0dev (2024-09-15T01:06:11Z master 532af89e3b) [x86_64-linux]
// ruby 3.4.0preview1 (2024-05-16 master 9d69619623) [x86_64-linux]
// ruby 3.3.0rc1 (2023-12-11 master a49643340e) [x86_64-linux]
// ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
// ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [x86_64-linux]
`(?m)ruby (?P<version>[0-9]+\.[0-9]+\.[0-9]+(p[0-9]+)?) `)
`(?m)ruby (?P<version>[0-9]+\.[0-9]+\.[0-9]+((p|preview|rc|dev)[0-9]*)?) `)
+9 -6
View File
@@ -2,6 +2,7 @@ package binary
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -29,13 +30,15 @@ func packageURL(metadata elfBinaryPackageNotes) string {
os := metadata.OS
osVersion := metadata.OSVersion
var atts cpe.Attributes
atts, err := cpe.NewAttributes(metadata.OSCPE)
if err == nil {
// only "upgrade" the OS information if there is something more specific to use in it's place
if os == "" && osVersion == "" || os == "" && atts.Version != "" || atts.Product != "" && osVersion == "" {
os = atts.Product
osVersion = atts.Version
}
if err != nil {
log.WithFields("error", err).Warn("unable to parse cpe attributes for elf binary package")
}
// only "upgrade" the OS information if there is something more specific to use in it's place
if os == "" && osVersion == "" || os == "" && atts.Version != "" || atts.Product != "" && osVersion == "" {
os = atts.Product
osVersion = atts.Version
}
if os != "" {
@@ -11,6 +11,7 @@ import (
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/mimetype"
"github.com/anchore/syft/internal/unknown"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
@@ -52,6 +53,7 @@ func (c *elfPackageCataloger) Name() string {
}
func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
var errs error
locations, err := resolver.FilesByMIMEType(mimetype.ExecutableMIMETypeSet.List()...)
if err != nil {
return nil, nil, fmt.Errorf("unable to get binary files by mime type: %w", err)
@@ -62,7 +64,8 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
for _, location := range locations {
notes, key, err := parseElfPackageNotes(resolver, location, c)
if err != nil {
return nil, nil, err
errs = unknown.Append(errs, location, err)
continue
}
if notes == nil {
continue
@@ -87,7 +90,7 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
// why not return relationships? We have an executable cataloger that will note the dynamic libraries imported by
// each binary. After all files and packages are processed there is a final task that creates package-to-package
// and package-to-file relationships based on the dynamic libraries imported by each binary.
return pkgs, nil, nil
return pkgs, nil, errs
}
func parseElfPackageNotes(resolver file.Resolver, location file.Location, c *elfPackageCataloger) (*elfBinaryPackageNotes, elfPackageKey, error) {
@@ -104,7 +107,7 @@ func parseElfPackageNotes(resolver file.Resolver, location file.Location, c *elf
if err != nil {
log.WithFields("file", location.Path(), "error", err).Trace("unable to parse ELF notes")
return nil, elfPackageKey{}, nil
return nil, elfPackageKey{}, err
}
if notes == nil {
@@ -173,7 +176,7 @@ func getELFNotes(r file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
if len(notes) > headerSize {
var metadata elfBinaryPackageNotes
newPayload := bytes.TrimRight(notes[headerSize:], "\x00")
if err := json.Unmarshal(newPayload, &metadata); err == nil {
if err = json.Unmarshal(newPayload, &metadata); err == nil {
return &metadata, nil
}
log.WithFields("file", r.Location.Path(), "error", err).Trace("unable to unmarshal ELF package notes as JSON")
@@ -3,6 +3,8 @@ package binary
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
@@ -14,6 +16,7 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
name string
fixture string
expected []pkg.Package
wantErr require.ErrorAssertionFunc
}{
{
name: "go case",
@@ -63,6 +66,7 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
},
},
},
wantErr: require.Error,
},
{
name: "fedora 64 bit binaries",
@@ -116,6 +120,7 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
WithImageResolver(t, v.fixture).
IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change
Expects(v.expected, nil).
WithErrorAssertion(v.wantErr).
TestCataloger(t, NewELFPackageCataloger())
})
}
@@ -113,6 +113,18 @@ func Test_packageURL(t *testing.T) {
},
want: "pkg:generic/system/test@1.0",
},
{
name: "bad or missing OSCPE data cannot be parsed allows for correct string",
metadata: elfBinaryPackageNotes{
Name: "test",
Version: "1.0",
ELFBinaryPackageNoteJSONPayload: pkg.ELFBinaryPackageNoteJSONPayload{
System: "system",
OSCPE: "%$#*(#*@&$(",
},
},
want: "pkg:generic/system/test@1.0",
},
}
for _, test := range tests {
@@ -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:
@@ -116,6 +130,13 @@ from-images:
paths:
- /usr/local/sbin/haproxy
- version: 2.0.0
images:
- ref: haproxy:2.0.0@sha256:f88bca67a2782e7bd88af168d5c11216c32104d12fe9240fac54a1d3196e3f9c
platform: linux/amd64
paths:
- /usr/local/sbin/haproxy
- version: 2.7.3
images:
- ref: haproxy:2.7.3@sha256:17d8aa6bf16882a294bdcccc757dd4002045f34b719e9f94dfd4801614801aea
@@ -242,6 +263,27 @@ from-images:
paths:
- /usr/local/openresty/nginx/sbin/nginx
- version: 0.10.48
images:
- ref: node:0.10.48@sha256:c32b4d56f05c69df6e87d06bf7d5f6a5c6a0e7bcdb8e5ffab0e7a1853a90008f
platform: linux/amd64
paths:
- /usr/local/bin/node
- version: 0.12.18
images:
- ref: node:0.12.18@sha256:02e8e9903c8d974e8874d0144413d398a41d62f54bafec4d2cf3ac2a8501dd28
platform: linux/amd64
paths:
- /usr/local/bin/node
- version: 4.9.1
images:
- ref: node:4.9.1@sha256:41d0ad2557ea2a9e57e1a458c1d659e92f601586e07dcffef74c9cef542f6f6e
platform: linux/amd64
paths:
- /usr/local/bin/node
- version: 19.2.0
images:
- ref: node:19.2.0@sha256:9bf5846b28f63acab0ccb0a39a245fbc414e6b7ecd467282f58016537c06e159
@@ -426,6 +468,13 @@ from-images:
paths:
- /usr/local/bin/redis-server
- version: 7.2.5
images:
- ref: redis:7.2.5@sha256:3b44455ef048b5f0a3a26d874c00c0f4ffe6a71a40834f2b038f816c6ad5ca4f
platform: linux/386
paths:
- /usr/local/bin/redis-server
- version: 2.9.0
images:
- ref: wordpress:cli-2.9.0-php8.3@sha256:3a008aafccc6a8d2d7ec2b069722cb43b3569b61cd833f9f2f13d74a9284746e
@@ -549,6 +598,36 @@ from-images:
- /usr/local/lib/libruby.so.2.7.7
- /usr/local/lib/libruby.so.2.7
- name: ruby-shared-libs
version: 3.4.0-dev
images:
- ref: rubylang/ruby:master-dev-nightly-20240804-focal@sha256:b5fe8b9ac1e9638a4b08bda6a6343082a07623d1c01926e637eb4bbbbf159409
platform: linux/amd64
paths:
- /usr/local/bin/ruby
- /usr/local/lib/libruby.so.3.4.0
- /usr/local/lib/libruby.so.3.4
- name: ruby-shared-libs
version: 3.4.0-preview1
images:
- ref: ruby:3.4.0-preview1@sha256:88cad31539e1a4ab987ac4b00c45b86d8346c25e3b08ac647ccccfb21cee5890
platform: linux/amd64
paths:
- /usr/local/bin/ruby
- /usr/local/lib/libruby.so.3.4.0
- /usr/local/lib/libruby.so.3.4
- name: ruby-shared-libs
version: 3.3.0-rc1
images:
- ref: ruby:3.3.0-rc1@sha256:453c8bbb9b4e3b04b94ba58db77b648222f3c92936dc12b5ae96df48076c106b
platform: linux/amd64
paths:
- /usr/local/bin/ruby
- /usr/local/lib/libruby.so.3.3.0
- /usr/local/lib/libruby.so.3.3
- name: ruby-shared-libs
version: 2.6.10
images:
@@ -610,3 +689,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

Some files were not shown because too many files have changed in this diff Show More