Compare commits

..
Author SHA1 Message Date
Alex Goodman 47cc8b58a7 persist artifact ID as supplemental package data
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-05-07 21:51:58 -04:00
dependabot[bot] 8aaf36b1ad chore(deps): bump github.com/github/go-spdx/v2 from 2.3.2 to 2.3.3 (#3863)
Bumps [github.com/github/go-spdx/v2](https://github.com/github/go-spdx) from 2.3.2 to 2.3.3.
- [Release notes](https://github.com/github/go-spdx/releases)
- [Commits](https://github.com/github/go-spdx/compare/v2.3.2...v2.3.3)

---
updated-dependencies:
- dependency-name: github.com/github/go-spdx/v2
  dependency-version: 2.3.3
  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>
2025-05-07 10:00:05 -04:00
dependabot[bot] af273002b8 chore(deps): bump golang.org/x/net from 0.39.0 to 0.40.0 (#3859)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.39.0 to 0.40.0.
- [Commits](https://github.com/golang/net/compare/v0.39.0...v0.40.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.40.0
  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>
2025-05-06 12:12:58 -04:00
Christopher Angelo Phillips 6eff158ad3 chore: update license sort to be stable with contents field (#3860)
---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-05-06 11:45:47 -04:00
Alex Goodman 7b25ea5eda annotate hidden paths in all-layers scope (#3855)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-05-06 09:50:04 -04:00
Christopher Angelo Phillips 1ba1186410 fix: use "contents" field and remove "fullText" license field (#3857)
---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-05-05 21:40:09 +00:00
GGMUandAlex Goodman 6db60c5975 Add deep-squashed scope to annotate all layers where a package exists (#3138)
* add squash all layers resolver

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* add squash with all layers logic

Signed-off-by: tomersein <tomersein@gmail.com>

* add squash with all layers logic

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squashed all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squash with all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* squash with all layers

Signed-off-by: tomersein <tomersein@gmail.com>

* adjust resolver to strictly return squash paths only

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

* show all packages have locations + primary evidence

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

* fix race condition in test

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

* consider access paths

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

---------

Signed-off-by: tomersein <tomersein@gmail.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-05-05 14:35:57 -04:00
Kudryavcev Nikolay e13c9e7813 fix: propagate unarchive error of file source (#3845)
Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
2025-05-05 15:37:47 +00:00
Alex Goodman d47a6c3a6d Improve support for cataloging nix package relationships (#3837)
* add nix DB cataloger

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

* add derivation path to nix store pkg metadata

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

* go mod tidy

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

* allow for derivation path to be optional

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

* repin build image and disable syscall filtering

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

* bump storage capacity

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

* track nix derivation details on packages

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

* image fixture should have derivation examples

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

* address comments

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-05-05 15:35:13 +00:00
anchore-actions-token-generator[bot]andwagoodman 7505a04aad chore(deps): update tools to latest versions (#3848)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
2025-05-05 15:26:14 +00:00
anchore-actions-token-generator[bot]andwagoodman f1620b120a chore(deps): update CPE dictionary index (#3851)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
2025-05-05 15:14:28 +00:00
Christopher Angelo Phillips 00f53b1777 chore: upgrade fixtures to use version 4 lockfile (#3852)
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-05-05 14:38:23 +00:00
dependabot[bot] 3faf43d592 chore(deps): bump github/codeql-action from 3.28.16 to 3.28.17 (#3846)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.16 to 3.28.17.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/28deaeda66b76a05916b6923827895f2b14ab387...60168efe1c415ce0f5521ea06d5c2062adbeed1b)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-05-05 10:23:08 -04:00
Christopher Angelo Phillips 6ba087c72c fix: Do not use hashes for SPDX license names/expressions (#3844)
---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-05-02 09:34:08 -04:00
Christopher Angelo Phillips 94e63eb367 feat: detect when full license text has been provided and preserve as separate field (#3450)
* feat: add full text field to syft license struct
---------
Signed-off-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com>
2025-05-01 15:00:46 -04:00
dependabot[bot] 4999de4114 chore(deps): bump github.com/Masterminds/semver/v3 from 3.3.0 to 3.3.1 (#3843)
Bumps [github.com/Masterminds/semver/v3](https://github.com/Masterminds/semver) from 3.3.0 to 3.3.1.
- [Release notes](https://github.com/Masterminds/semver/releases)
- [Changelog](https://github.com/Masterminds/semver/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Masterminds/semver/compare/v3.3.0...v3.3.1)

---
updated-dependencies:
- dependency-name: github.com/Masterminds/semver/v3
  dependency-version: 3.3.1
  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>
2025-05-01 10:06:11 -04:00
anchore-actions-token-generator[bot]andspiffcs 9ecfe9a53c chore(deps): update tools to latest versions (#3841)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-05-01 09:29:19 -04:00
Alan PopeandAlex Goodman baa1080ef6 Update github.com/Masterminds/semver to v3 (#3836)
* Update semver to v3. Fixes #3829

Signed-off-by: Alan Pope <alan.pope@anchore.com>

* use single instance of regex obj

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

---------

Signed-off-by: Alan Pope <alan.pope@anchore.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-30 20:38:12 +00:00
Laurent GoderreandAlex Goodman 529840bfc0 Add support for PHP Pear (#2775)
* Add support for PHP Pear and unify PECL with it

Signed-off-by: Laurent Goderre <laurent.goderre@docker.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* fix tests

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

* remove log statements

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

* fix struct comment

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

---------

Signed-off-by: Laurent Goderre <laurent.goderre@docker.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-30 20:16:58 +00:00
Oleksandr Vodotiiets 78ef2cf53b fix: Improve detection of erlang binary in alpine Linux (#3839)
Signed-off-by: Oleksandr Vodotiiets <alex.vodotiiets@gmail.com>
Signed-off-by: Oleksandr Vodotiiets <61548316+avodotiiets@users.noreply.github.com>
2025-04-30 18:50:12 +00:00
VictorHuu 09c3b7cbea fix:Resolve ancestral symlinks correctly (#3783)
* Resolve upstream symlinks correctly

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* in case of the root directory

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* for static analysis check pass

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* add unit test cases for the symlink scenarios

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

---------

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>
2025-04-30 14:47:32 -04:00
anchore-actions-token-generator[bot]andwagoodman 6dca10fe1f chore(deps): update CPE dictionary index (#3834)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
2025-04-30 14:40:52 -04:00
anchore-actions-token-generator[bot]andspiffcs 1ecf1ce7bf chore(deps): update tools to latest versions (#3835)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-04-30 14:40:37 -04:00
dependabot[bot] 20ca60de8b chore(deps): bump github.com/charmbracelet/bubbletea from 1.3.4 to 1.3.5 (#3838)
Bumps [github.com/charmbracelet/bubbletea](https://github.com/charmbracelet/bubbletea) from 1.3.4 to 1.3.5.
- [Release notes](https://github.com/charmbracelet/bubbletea/releases)
- [Changelog](https://github.com/charmbracelet/bubbletea/blob/main/.goreleaser.yml)
- [Commits](https://github.com/charmbracelet/bubbletea/compare/v1.3.4...v1.3.5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-30 14:40:00 -04:00
VictorHuu fa599547a3 fix the fluent-bit regex detection pattern (#3817)
Signed-off-by: Yuntao Hu <victorhu493@gmail.com>
2025-04-25 11:50:45 -04:00
dependabot[bot] 22d8b30813 chore(deps): bump anchore/sbom-action from 0.18.0 to 0.19.0 (#3832)
Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.18.0 to 0.19.0.
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](https://github.com/anchore/sbom-action/compare/f325610c9f50a54015d37c8d16cb3b0e2c8f4de0...9f7302141466aa6482940f15371237e9d9f4c34a)

---
updated-dependencies:
- dependency-name: anchore/sbom-action
  dependency-version: 0.19.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-25 11:29:24 -04:00
anchore-actions-token-generator[bot]andspiffcs a714fb8391 chore(deps): update tools to latest versions (#3830)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-04-25 09:59:29 -04:00
Alex Goodman 03fa142de9 Resolve owned file paths when searching for overlaps (#3828)
* resolve owned file paths when searching for overlaps

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

* dont remove empty paths

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-24 21:59:45 +00:00
anchore-actions-token-generator[bot]andwagoodman 4211d79667 chore(deps): update anchore dependencies (#3827)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
2025-04-24 16:03:09 -04:00
VictorHuuandAlex Goodman 9af087d213 fix: Make the fileresolver Support Prefix Match of Files (#3820)
* fix the dpkg files pattern detection

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* consider slash before the path is concatenated

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* add test

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

---------

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-24 17:38:05 +00:00
Alex Goodman 5c6c6aa123 Add support for detecting javascript assets in .NET projects using libman (#3825)
* add support for .NET libman files

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

* fix when no libman detected

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

* add libman.json docs

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-24 17:11:01 +00:00
anchore-actions-token-generator[bot]andspiffcs 43a85dfb85 chore(deps): update tools to latest versions (#3823)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-04-24 13:08:32 -04:00
Adam McClenaghan 61a3d1784a (feat): support skipping archive extraction with file source (#3795)
Signed-off-by: Adam McClenaghan <adam@mcclenaghan.co.uk>
2025-04-24 12:22:36 -04:00
Alex Goodman df18edf905 Consider DLL claims for dependencies of .NET packages from deps.json (#3822)
* consider child dll claims for .NET packages from deps.json

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

* make dll claim propagation configurable

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-24 11:59:16 -04:00
Alex Goodman 2dd9d583af PE cataloger should consider compile target paths from deps.json (#3821)
* fix PE compile path processing

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

* fix tests

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

* adjust fixture comment

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-24 14:01:53 +01:00
Adam McClenaghan f6d4a7d27a Perf: skip license scanner injection (#3796)
* (perf): allow library users to skip default scanner injection

Signed-off-by: Adam McClenaghan <adam@mcclenaghan.co.uk>

* (perf): remove prints

Signed-off-by: Adam McClenaghan <adam@mcclenaghan.co.uk>

* perf: move to cataloging licenses.go

Signed-off-by: adammcclenaghan <adam.mcclenaghan@upwind.io>

* perf: Simplify to expose a SetContextLicenseScanner func

Signed-off-by: adammcclenaghan <adam.mcclenaghan@upwind.io>

---------

Signed-off-by: Adam McClenaghan <adam@mcclenaghan.co.uk>
Signed-off-by: adammcclenaghan <adam.mcclenaghan@upwind.io>
2025-04-23 16:01:10 -04:00
dependabot[bot] 273d414b6b chore(deps): bump sigstore/cosign-installer from 3.8.1 to 3.8.2 (#3818)
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.8.1 to 3.8.2.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/v3.8.1...v3.8.2)

---
updated-dependencies:
- dependency-name: sigstore/cosign-installer
  dependency-version: 3.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-23 11:27:08 -04:00
dependabot[bot] 0a0c2963f4 chore(deps): bump github/codeql-action from 3.28.15 to 3.28.16 (#3819)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.15 to 3.28.16.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/45775bd8235c68ba998cffa5171334d58593da47...28deaeda66b76a05916b6923827895f2b14ab387)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-23 11:25:42 -04:00
anchore-actions-token-generator[bot]andspiffcs 1d7529d01f chore(deps): update tools to latest versions (#3815)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-04-22 13:10:35 -04:00
Will Murphy a69f6aec90 docs: document test commands (#3816)
So that contributors can quickly figure out how to run tests.

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2025-04-22 10:23:52 -04:00
Stijn Taelemans df11561929 Support detection of Chrome binaries (#3136)
* test: add test cases

Signed-off-by: Stijn Taelemans <taelemansst@gmail.com>

* feat: add classifier for chrome

Signed-off-by: Stijn Taelemans <taelemansst@gmail.com>

---------

Signed-off-by: Stijn Taelemans <taelemansst@gmail.com>
2025-04-21 16:37:15 -04:00
VictorHuu ab570497b0 fix:allow golang tip image detection regex pattern (#3757)
* Modify the Regex version matcher for golang in the binary classifiers to make it compatible with golang tip images

Signed-off-by: Victor Hu <victorhu493@gmail.com>
Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* Preliminary fix the regex matching for golang tip image and add the corresponding unit tests

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* consider VERSION.cache when it comes to golang tip images

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* consider VERSION.cache when it comes to golang tip images

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

---------

Signed-off-by: Victor Hu <victorhu493@gmail.com>
Signed-off-by: Yuntao Hu <victorhu493@gmail.com>
2025-04-21 14:06:52 -04:00
VictorHuuandAlex Goodman ea7e9e696b fix:Make the parse of the replace part in ``go.mod`` more compliant and traceable (#3812)
* We only need to replace the name of a GoLang package when the name is a web link

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* make the detection of a localfile path pattern more easy

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* consider the m.New.Version so the granularity is narrowed

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* to pass the static-analysis

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* to pass the static-analysis

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>

* add test

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

---------

Signed-off-by: Yuntao Hu <victorhu493@gmail.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-21 17:58:54 +00:00
Adam McClenaghan 1f15361ecf (fix): delete collection name/type key entries when empty (#3797)
Signed-off-by: Adam McClenaghan <adam@mcclenaghan.co.uk>
2025-04-21 13:41:39 -04:00
anchore-actions-token-generator[bot]andwagoodman 0bcf2881c4 chore(deps): update CPE dictionary index (#3813)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
2025-04-21 09:59:06 -04:00
anchore-actions-token-generator[bot]andspiffcs b9ae936731 chore(deps): update tools to latest versions (#3806)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-04-17 12:26:29 -04:00
dependabot[bot] e452cc7623 chore(deps): bump github.com/go-git/go-git/v5 from 5.15.0 to 5.16.0 (#3807)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.15.0 to 5.16.0.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.15.0...v5.16.0)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-version: 5.16.0
  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>
2025-04-17 12:26:18 -04:00
Keith Zantow b13ffdd304 fix: comma separated selectors in cataloger list command (#3804)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2025-04-16 10:41:48 -04:00
dependabot[bot] a5da154327 chore(deps): bump github.com/anchore/stereoscope from 0.1.2 to 0.1.3 (#3803) 2025-04-15 19:31:45 +00:00
Musang Kim 1866e25f9a chore: fix conan parser typos (#3802)
Signed-off-by: Musang <s.musang.kim@gmail.com>
2025-04-15 10:51:02 -04:00
anchore-actions-token-generator[bot]andspiffcs 1e336e3f07 chore(deps): update tools to latest versions (#3798)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-04-14 14:43:53 -04:00
anchore-actions-token-generator[bot]andwagoodman eee9d0a41e chore(deps): update CPE dictionary index (#3799)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
2025-04-14 14:43:25 -04:00
dependabot[bot] a5632c0044 chore(deps): bump github.com/mholt/archives from 0.1.0 to 0.1.1 (#3778)
Bumps [github.com/mholt/archives](https://github.com/mholt/archives) from 0.1.0 to 0.1.1.
- [Release notes](https://github.com/mholt/archives/releases)
- [Commits](https://github.com/mholt/archives/compare/v0.1.0...v0.1.1)

---
updated-dependencies:
- dependency-name: github.com/mholt/archives
  dependency-version: 0.1.1
  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>
2025-04-11 14:50:51 -04:00
dependabot[bot] 2a409488a0 chore(deps): bump marocchino/sticky-pull-request-comment (#3788)
Bumps [marocchino/sticky-pull-request-comment](https://github.com/marocchino/sticky-pull-request-comment) from 2.9.1 to 2.9.2.
- [Release notes](https://github.com/marocchino/sticky-pull-request-comment/releases)
- [Commits](https://github.com/marocchino/sticky-pull-request-comment/compare/52423e01640425a022ef5fd42c6fb5f633a02728...67d0dec7b07ed060a405f9b2a64b8ab319fdd7db)

---
updated-dependencies:
- dependency-name: marocchino/sticky-pull-request-comment
  dependency-version: 2.9.2
  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>
2025-04-11 12:43:11 -04:00
dependabot[bot] d145e80c20 chore(deps): bump github.com/magiconair/properties from 1.8.9 to 1.8.10 (#3789)
Bumps [github.com/magiconair/properties](https://github.com/magiconair/properties) from 1.8.9 to 1.8.10.
- [Release notes](https://github.com/magiconair/properties/releases)
- [Commits](https://github.com/magiconair/properties/compare/v1.8.9...v1.8.10)

---
updated-dependencies:
- dependency-name: github.com/magiconair/properties
  dependency-version: 1.8.10
  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>
2025-04-11 12:42:57 -04:00
dependabot[bot] 05de0d2a1b chore(deps): bump github.com/charmbracelet/bubbles from 0.20.0 to 0.21.0 (#3790)
Bumps [github.com/charmbracelet/bubbles](https://github.com/charmbracelet/bubbles) from 0.20.0 to 0.21.0.
- [Release notes](https://github.com/charmbracelet/bubbles/releases)
- [Changelog](https://github.com/charmbracelet/bubbles/blob/master/.goreleaser.yml)
- [Commits](https://github.com/charmbracelet/bubbles/compare/v0.20.0...v0.21.0)

---
updated-dependencies:
- dependency-name: github.com/charmbracelet/bubbles
  dependency-version: 0.21.0
  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>
2025-04-11 12:42:46 -04:00
Alex Goodman 24df095a5e empty source during decoding should not be fatal (#3791)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-11 10:12:29 -04:00
dependabot[bot] e7f0a602c2 chore(deps): bump github.com/go-git/go-git/v5 from 5.14.0 to 5.15.0 (#3792)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.14.0 to 5.15.0.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.14.0...v5.15.0)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-version: 5.15.0
  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>
2025-04-11 10:12:05 -04:00
anchore-actions-token-generator[bot]andspiffcs e73293cd45 chore(deps): update tools to latest versions (#3785)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-04-08 14:03:29 -04:00
dependabot[bot] 3b3943d2d6 chore(deps): bump github/codeql-action from 3.28.13 to 3.28.15 (#3786)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.13 to 3.28.15.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/1b549b9259bda1cb5ddde3b41741a82a2d15a841...45775bd8235c68ba998cffa5171334d58593da47)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-04-08 14:03:06 -04:00
dependabot[bot] 97228af539 chore(deps): bump golang.org/x/net from 0.38.0 to 0.39.0 (#3787)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.38.0 to 0.39.0.
- [Commits](https://github.com/golang/net/compare/v0.38.0...v0.39.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.39.0
  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>
2025-04-08 14:02:48 -04:00
anchore-actions-token-generator[bot]andwagoodman 987ba83674 chore(deps): update CPE dictionary index (#3782)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
2025-04-07 10:01:11 -04:00
anchore-actions-token-generator[bot]andspiffcs f11377fe30 chore(deps): update tools to latest versions (#3775)
---------
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>
2025-04-03 17:35:26 +00:00
Alex Goodman 12f36420dd Parse GitHub actions comments (#3776)
* add version comment parsing support to github actions

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

* update json schema with github actions metadata

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

* add originator processing for github actions type

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-03 14:46:27 +00:00
Alex Goodman f851085668 Expand python license scanning to cover unclaimed files (#3779)
* expand python license scanning to cover unclaimed files

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

* speed up tests using the license scanner

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-04-03 10:31:02 -04:00
Christopher Angelo Phillips da62a82413 feat: adds the DirectoryTag to the r cataloger (#3774)
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-04-01 11:46:51 -04:00
273 changed files with 19424 additions and 1725 deletions
+7 -7
View File
@@ -26,7 +26,7 @@ tools:
# used for linting
- name: golangci-lint
version:
want: v1.64.8
want: v2.1.6
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.3
want: v2.5.0
method: github-release
with:
repo: sigstore/cosign
@@ -58,7 +58,7 @@ tools:
# used to release all artifacts
- name: goreleaser
version:
want: v2.8.1
want: v2.9.0
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.42.1
want: v3.43.3
method: github-release
with:
repo: go-task/task
@@ -111,7 +111,7 @@ tools:
# used for triggering a release
- name: gh
version:
want: v2.69.0
want: v2.72.0
method: github-release
with:
repo: cli/cli
@@ -119,7 +119,7 @@ tools:
# used to upload test fixture cache
- name: oras
version:
want: v1.2.2
want: v1.2.3
method: github-release
with:
repo: oras-project/oras
@@ -127,7 +127,7 @@ tools:
# used to upload test fixture cache
- name: yq
version:
want: v4.45.1
want: v4.45.2
method: github-release
with:
repo: mikefarah/yq
+1 -1
View File
@@ -45,7 +45,7 @@ jobs:
path: test/results/**/*
- name: Update PR benchmark results comment
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 #v2.9.1
uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db #v2.9.2
continue-on-error: true
with:
header: benchmark
+3 -3
View File
@@ -45,7 +45,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@1b549b9259bda1cb5ddde3b41741a82a2d15a841 #v3.28.13
uses: github/codeql-action/init@60168efe1c415ce0f5521ea06d5c2062adbeed1b #v3.28.17
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -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@1b549b9259bda1cb5ddde3b41741a82a2d15a841 #v3.28.13
uses: github/codeql-action/autobuild@60168efe1c415ce0f5521ea06d5c2062adbeed1b #v3.28.17
# ℹ️ 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@1b549b9259bda1cb5ddde3b41741a82a2d15a841 #v3.28.13
uses: github/codeql-action/analyze@60168efe1c415ce0f5521ea06d5c2062adbeed1b #v3.28.17
+2 -2
View File
@@ -40,7 +40,7 @@ jobs:
- name: Delete existing comment
if: ${{ hashFiles( env.CI_COMMENT_FILE ) == '' }}
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 #v2.9.1
uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db #v2.9.2
with:
header: ${{ env.COMMENT_HEADER }}
hide: true
@@ -48,7 +48,7 @@ jobs:
- name: Add comment
if: ${{ hashFiles( env.CI_COMMENT_FILE ) != '' }}
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 #v2.9.1
uses: marocchino/sticky-pull-request-comment@67d0dec7b07ed060a405f9b2a64b8ab319fdd7db #v2.9.2
with:
header: ${{ env.COMMENT_HEADER }}
path: ${{ env.CI_COMMENT_FILE }}
+1 -1
View File
@@ -148,7 +148,7 @@ jobs:
# for updating brew formula in anchore/homebrew-syft
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
- uses: anchore/sbom-action@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 #v0.18.0
- uses: anchore/sbom-action@9f7302141466aa6482940f15371237e9d9f4c34a #v0.19.0
continue-on-error: true
with:
file: go.mod
+3 -2
View File
@@ -29,7 +29,8 @@ jobs:
Unit-Test:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
name: "Unit tests"
runs-on: ubuntu-24.04
# we need more storage than what's on the default runner
runs-on: ubuntu-22.04-4core-16gb
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
@@ -194,7 +195,7 @@ jobs:
runs-on: macos-latest
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@v3.8.1
uses: sigstore/cosign-installer@v3.8.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
+49 -41
View File
@@ -1,15 +1,8 @@
issues:
max-same-issues: 25
uniq-by-line: false
# TODO: enable this when we have coverage on docstring comments
# # The list of ids of default excludes to include or disable.
# include:
# - EXC0002 # disable excluding of issues about comments from golint
version: "2"
run:
tests: false
linters:
# inverted configuration with `enable-all` and `disable` is not scalable during updates of golangci-lint
disable-all: true
default: none
enable:
- asciicheck
- bodyclose
@@ -22,11 +15,8 @@ linters:
- goconst
- gocritic
- gocyclo
- gofmt
- goimports
- goprintffuncname
- gosec
- gosimple
- govet
- ineffassign
- misspell
@@ -34,37 +24,35 @@ linters:
- nolintlint
- revive
- staticcheck
- stylecheck
- typecheck
- unconvert
- unparam
- unused
- whitespace
linters-settings:
funlen:
# Checks the number of lines in a function.
# If lower than 0, disable the check.
# Default: 60
lines: 70
# Checks the number of statements in a function.
# If lower than 0, disable the check.
# Default: 40
statements: 50
gocritic:
enabled-checks:
- deferInLoop
- ruleguard
settings:
ruleguard:
rules: "test/rules/rules.go"
gosec:
excludes:
- G115
run:
timeout: 10m
tests: false
settings:
funlen:
lines: 70
statements: 50
gocritic:
enabled-checks:
- deferInLoop
- ruleguard
settings:
ruleguard:
rules: test/rules/rules.go
gosec:
excludes:
- G115
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
# do not enable...
# - deadcode # The owner seems to have abandoned the linter. Replaced by "unused".
@@ -91,3 +79,23 @@ run:
# - testpackage
# - varcheck # The owner seems to have abandoned the linter. Replaced by "unused".
# - wsl # this doens't have an auto-fixer yet and is pretty noisy (https://github.com/bombsimon/wsl/issues/90)
issues:
max-same-issues: 25
uniq-by-line: false
# TODO: enable this when we have coverage on docstring comments
# # The list of ids of default excludes to include or disable.
# include:
# - EXC0002 # disable excluding of issues about comments from golint
formatters:
enable:
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
+10
View File
@@ -227,6 +227,16 @@ rough outline how that works:
## Testing
### Testing commands
* `make help` shows a list of available commands
* `make unit`, `make integration`, `make cli`, and `make acceptance` run those test suites (see below)
* `make test` runs all those tests (and is therefore pretty slow)
* `make fixtures` clears and re-fetches all test fixtures.
* `go test ./syft/pkg/` for example can test particular packages, assuming fixtures are already made
* `make clean-cache` cleans all test cache. Note that subsequent test runs will be slower after this
### Levels of testing
- `unit`: The default level of test which is distributed throughout the repo are unit tests. Any `_test.go` file that
+2 -2
View File
@@ -140,11 +140,11 @@ Note that flags using the @<version> can be used for earlier versions of each sp
- Linux kernel archives (vmlinz)
- Linux kernel modules (ko)
- Nix (outputs in /nix/store)
- PHP (composer)
- PHP (composer, PECL, Pear)
- Python (wheel, egg, poetry, requirements.txt)
- Red Hat (rpm)
- Ruby (gem)
- Rust (cargo.lock)
- Rust (cargo.lock, auditable binary)
- Swift (cocoapods, swift-package-manager)
- Wordpress plugins
- Terraform providers (.terraform.lock.hcl)
+1 -1
View File
@@ -224,7 +224,7 @@ tasks:
- "go run -race cmd/syft/main.go alpine:latest"
validate-cyclonedx-schema:
desc: Run integration tests
desc: Validate that Syft produces valid CycloneDX documents
cmds:
- "cd schema/cyclonedx && make"
+2 -2
View File
@@ -52,7 +52,7 @@ func Attest(app clio.Application) *cobra.Command {
opts := defaultAttestOptions()
// template format explicitly not allowed
opts.Format.Template.Enabled = false
opts.Template.Enabled = false
return app.SetupCommand(&cobra.Command{
Use: "attest --output [FORMAT] <IMAGE>",
@@ -136,7 +136,7 @@ func writeSBOMToFormattedFile(s *sbom.SBOM, sbomFile io.Writer, opts *attestOpti
return fmt.Errorf("no output file provided")
}
encs, err := opts.Format.Encoders()
encs, err := opts.Encoders()
if err != nil {
return fmt.Errorf("unable to create encoders: %w", err)
}
+6 -3
View File
@@ -13,6 +13,7 @@ import (
"github.com/spf13/cobra"
"github.com/anchore/clio"
"github.com/anchore/syft/cmd/syft/internal/options"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/task"
"github.com/anchore/syft/syft/cataloging"
@@ -86,11 +87,13 @@ func runCatalogerList(opts *catalogerListOptions) error {
}
func catalogerListReport(opts *catalogerListOptions, allTaskGroups [][]task.Task) (string, error) {
defaultCatalogers := options.Flatten(opts.DefaultCatalogers)
selectCatalogers := options.Flatten(opts.SelectCatalogers)
selectedTaskGroups, selectionEvidence, err := task.SelectInGroups(
allTaskGroups,
cataloging.NewSelectionRequest().
WithDefaults(opts.DefaultCatalogers...).
WithExpression(opts.SelectCatalogers...),
WithDefaults(defaultCatalogers...).
WithExpression(selectCatalogers...),
)
if err != nil {
return "", fmt.Errorf("unable to select catalogers: %w", err)
@@ -99,7 +102,7 @@ func catalogerListReport(opts *catalogerListOptions, allTaskGroups [][]task.Task
switch opts.Output {
case "json":
report, err = renderCatalogerListJSON(flattenTaskGroups(selectedTaskGroups), selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
report, err = renderCatalogerListJSON(flattenTaskGroups(selectedTaskGroups), selectionEvidence, defaultCatalogers, selectCatalogers)
case "table", "":
if opts.ShowHidden {
report = renderCatalogerListTables(allTaskGroups, selectionEvidence)
@@ -184,11 +184,11 @@ Default selections: 1
• 'image'
Selection expressions: 6
• 'b' (intersect)
• '-directory' (remove)
• '-c' (remove)
• '-directory' (remove)
• '-file' (remove)
• '+task3' (add)
• '+file-task1' (add)
• '+task3' (add)
┌────────────────┬──────────────────┐
│ FILE CATALOGER │ TAGS │
├────────────────┼──────────────────┤
@@ -200,6 +200,43 @@ Selection expressions: 6
│ task1 │ 1, a, b, image │
│ task3 │ 3, c, d, directory │
└───────────────────┴────────────────────┘
`,
},
{
name: "with comma separated expressions, table",
options: func() *catalogerListOptions {
c := defaultCatalogerListOptions()
c.Output = "table"
c.DefaultCatalogers = []string{
"image,task3,file-task1",
}
c.SelectCatalogers = []string{
"-task2,c,b",
}
return c
}(),
want: `
Default selections: 4
• 'file-task1'
• 'image'
• 'task3'
• 'file'
Selection expressions: 3
• 'b' (intersect)
• 'c' (intersect)
• '-task2' (remove)
┌────────────────┬──────────────────┐
│ FILE CATALOGER │ TAGS │
├────────────────┼──────────────────┤
│ file-task1 │ file, ft, ft-1-b │
│ file-task2 │ file, ft, ft-2-b │
└────────────────┴──────────────────┘
┌───────────────────┬────────────────────┐
│ PACKAGE CATALOGER │ TAGS │
├───────────────────┼────────────────────┤
│ task1 │ 1, a, b, image │
│ task3 │ 3, c, d, directory │
└───────────────────┴────────────────────┘
`,
},
{
@@ -219,7 +256,7 @@ Selection expressions: 6
return c
}(),
want: `
{"default":["image"],"selection":["-directory","+task3","-c","b"],"catalogers":[{"name":"file-task1","tags":["file"]},{"name":"file-task2","tags":["file"]},{"name":"task1","tags":["b","image"]},{"name":"task3","tags":["task3"]}]}
{"default":["image"],"selection":["+task3","-c","-directory","b"],"catalogers":[{"name":"file-task1","tags":["file"]},{"name":"file-task2","tags":["file"]},{"name":"task1","tags":["b","image"]},{"name":"task3","tags":["task3"]}]}
`,
},
}
+2 -2
View File
@@ -110,7 +110,7 @@ func (o *scanOptions) PostLoad() error {
}
func (o *scanOptions) validateLegacyOptionsNotUsed() error {
if len(fangs.Flatten(o.Config.ConfigFile)) == 0 {
if len(fangs.Flatten(o.ConfigFile)) == 0 {
return nil
}
@@ -122,7 +122,7 @@ func (o *scanOptions) validateLegacyOptionsNotUsed() error {
File any `yaml:"file" json:"file" mapstructure:"file"`
}
for _, f := range fangs.Flatten(o.Config.ConfigFile) {
for _, f := range fangs.Flatten(o.ConfigFile) {
by, err := os.ReadFile(f)
if err != nil {
return fmt.Errorf("unable to read config file during validations %q: %w", f, err)
+12 -6
View File
@@ -24,6 +24,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/java"
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
"github.com/anchore/syft/syft/pkg/cataloger/nix"
"github.com/anchore/syft/syft/pkg/cataloger/python"
"github.com/anchore/syft/syft/source"
)
@@ -48,6 +49,7 @@ type Catalog struct {
Java javaConfig `yaml:"java" json:"java" mapstructure:"java"`
JavaScript javaScriptConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"`
LinuxKernel linuxKernelConfig `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"`
Nix nixConfig `yaml:"nix" json:"nix" mapstructure:"nix"`
Python pythonConfig `yaml:"python" json:"python" mapstructure:"python"`
// configuration for the source (the subject being analyzed)
@@ -75,6 +77,7 @@ func DefaultCatalog() Catalog {
Package: defaultPackageConfig(),
License: defaultLicenseConfig(),
LinuxKernel: defaultLinuxKernelConfig(),
Nix: defaultNixConfig(),
Dotnet: defaultDotnetConfig(),
Golang: defaultGolangConfig(),
Java: defaultJavaConfig(),
@@ -170,6 +173,7 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
Dotnet: dotnet.DefaultCatalogerConfig().
WithDepPackagesMustHaveDLL(cfg.Dotnet.DepPackagesMustHaveDLL).
WithDepPackagesMustClaimDLL(cfg.Dotnet.DepPackagesMustClaimDLL).
WithPropagateDLLClaimsToParents(cfg.Dotnet.PropagateDLLClaimsToParents).
WithRelaxDLLClaimsWhenBundlingDetected(cfg.Dotnet.RelaxDLLClaimsWhenBundlingDetected),
Golang: golang.DefaultCatalogerConfig().
WithSearchLocalModCacheLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchLocalModCacheLicenses)).
@@ -192,6 +196,8 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
LinuxKernel: kernel.LinuxKernelCatalogerConfig{
CatalogModules: cfg.LinuxKernel.CatalogModules,
},
Nix: nix.DefaultConfig().
WithCaptureOwnedFiles(cfg.Nix.CaptureOwnedFiles),
Python: python.CatalogerConfig{
GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
},
@@ -272,12 +278,12 @@ func (cfg *Catalog) PostLoad() error {
return fmt.Errorf("cannot use both 'catalogers' and 'select-catalogers'/'default-catalogers' flags")
}
cfg.From = flatten(cfg.From)
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)
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...)
@@ -295,7 +301,7 @@ func (cfg *Catalog) PostLoad() error {
return nil
}
func flatten(commaSeparatedEntries []string) []string {
func Flatten(commaSeparatedEntries []string) []string {
var out []string
for _, v := range commaSeparatedEntries {
for _, s := range strings.Split(v, ",") {
+1 -1
View File
@@ -139,7 +139,7 @@ func Test_enrichmentEnabled(t *testing.T) {
for _, test := range tests {
t.Run(test.directives, func(t *testing.T) {
got := enrichmentEnabled(flatten([]string{test.directives}), test.test)
got := enrichmentEnabled(Flatten([]string{test.directives}), test.test)
assert.Equal(t, test.expected, got)
})
}
+6 -2
View File
@@ -10,6 +10,8 @@ type dotnetConfig struct {
DepPackagesMustClaimDLL bool `mapstructure:"dep-packages-must-claim-dll" json:"dep-packages-must-claim-dll" yaml:"dep-packages-must-claim-dll"`
PropagateDLLClaimsToParents bool `mapstructure:"propagate-dll-claims-to-parents" json:"propagate-dll-claims-to-parents" yaml:"propagate-dll-claims-to-parents"`
RelaxDLLClaimsWhenBundlingDetected bool `mapstructure:"relax-dll-claims-when-bundling-detected" json:"relax-dll-claims-when-bundling-detected" yaml:"relax-dll-claims-when-bundling-detected"`
}
@@ -18,8 +20,9 @@ var _ interface {
} = (*dotnetConfig)(nil)
func (o *dotnetConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.DepPackagesMustHaveDLL, `only keep dep.json packages which an executable on disk can be found for`)
descriptions.Add(&o.DepPackagesMustClaimDLL, `only keep dep.json packages which have a runtime/resource DLL claimed in the deps.json targets section (but not necessarily found on disk)`)
descriptions.Add(&o.DepPackagesMustHaveDLL, `only keep dep.json packages which an executable on disk is found. The package is also included if a DLL is found for any child package, even if the package itself does not have a DLL.`)
descriptions.Add(&o.DepPackagesMustClaimDLL, `only keep dep.json packages which have a runtime/resource DLL claimed in the deps.json targets section (but not necessarily found on disk). The package is also included if any child package claims a DLL, even if the package itself does not claim a DLL.`)
descriptions.Add(&o.PropagateDLLClaimsToParents, `treat DLL claims or on-disk evidence for child packages as DLL claims or on-disk evidence for any parent package`)
descriptions.Add(&o.RelaxDLLClaimsWhenBundlingDetected, `show all packages from the deps.json if bundling tooling is present as a dependency (e.g. ILRepack)`)
}
@@ -28,6 +31,7 @@ func defaultDotnetConfig() dotnetConfig {
return dotnetConfig{
DepPackagesMustHaveDLL: def.DepPackagesMustHaveDLL,
DepPackagesMustClaimDLL: def.DepPackagesMustClaimDLL,
PropagateDLLClaimsToParents: def.PropagateDLLClaimsToParents,
RelaxDLLClaimsWhenBundlingDetected: def.RelaxDLLClaimsWhenBundlingDetected,
}
}
+25
View File
@@ -0,0 +1,25 @@
package options
import (
"github.com/anchore/clio"
"github.com/anchore/syft/syft/pkg/cataloger/nix"
)
type nixConfig struct {
CaptureOwnedFiles bool `json:"capture-owned-files" yaml:"capture-owned-files" mapstructure:"capture-owned-files"`
}
func defaultNixConfig() nixConfig {
def := nix.DefaultConfig()
return nixConfig{
def.CaptureOwnedFiles,
}
}
var _ interface {
clio.FieldDescriber
} = (*nixConfig)(nil)
func (o *nixConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.CaptureOwnedFiles, `enumerate all files owned by packages found within Nix store paths`)
}
+1 -1
View File
@@ -89,7 +89,7 @@ func (o Output) SBOMWriter() (sbom.Writer, error) {
usesTemplateOutput := names.Has(string(template.ID))
if usesTemplateOutput && o.Format.Template.Path == "" {
if usesTemplateOutput && o.Template.Path == "" {
return nil, fmt.Errorf(`must specify path to template file when using "template" output format`)
}
@@ -502,8 +502,8 @@ var commonTestCases = []testCase{
},
},
{
name: "find php pecl package",
pkgType: pkg.PhpPeclPkg,
name: "find php pear/pecl package",
pkgType: pkg.PhpPearPkg,
pkgLanguage: pkg.PHP,
pkgInfo: map[string]string{
"memcached": "3.2.0",
@@ -12,6 +12,7 @@ import (
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
@@ -85,6 +86,7 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Remove(string(pkg.GithubActionPkg))
definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg))
definedPkgs.Remove(string(pkg.TerraformPkg))
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // we have coverage for pear instead
var cases []testCase
cases = append(cases, commonTestCases...)
@@ -92,40 +94,7 @@ func TestPkgCoverageImage(t *testing.T) {
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
pkgCount := 0
for a := range sbom.Artifacts.Packages.Enumerate(c.pkgType) {
if a.Language.String() != "" {
observedLanguages.Add(a.Language.String())
}
observedPkgs.Add(string(a.Type))
expectedVersion, ok := c.pkgInfo[a.Name]
if !ok {
t.Errorf("unexpected package found: %s", a.Name)
}
if expectedVersion != a.Version {
t.Errorf("unexpected package version (pkg=%s): %s, expected: %s", a.Name, a.Version, expectedVersion)
}
if a.Language != c.pkgLanguage {
t.Errorf("bad language (pkg=%+v): %+v", a.Name, a.Language)
}
if a.Type != c.pkgType {
t.Errorf("bad package type (pkg=%+v): %+v", a.Name, a.Type)
}
pkgCount++
}
if pkgCount != len(c.pkgInfo)+c.duplicates {
t.Logf("Discovered packages of type %+v", c.pkgType)
for a := range sbom.Artifacts.Packages.Enumerate(c.pkgType) {
t.Log(" ", a)
}
t.Fatalf("unexpected package count: %d!=%d", pkgCount, len(c.pkgInfo))
}
assertPackages(t, sbom, c, observedLanguages, observedPkgs)
})
}
@@ -175,44 +144,7 @@ func TestPkgCoverageDirectory(t *testing.T) {
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
actualPkgCount := 0
for actualPkg := range sbom.Artifacts.Packages.Enumerate(test.pkgType) {
observedLanguages.Add(actualPkg.Language.String())
observedPkgs.Add(string(actualPkg.Type))
expectedVersion, ok := test.pkgInfo[actualPkg.Name]
if !ok {
t.Errorf("unexpected package found: %s", actualPkg.Name)
}
if expectedVersion != actualPkg.Version {
t.Errorf("unexpected package version (pkg=%s): %s", actualPkg.Name, actualPkg.Version)
}
var foundLang bool
for _, lang := range strings.Split(test.pkgLanguage.String(), ",") {
if actualPkg.Language.String() == lang {
foundLang = true
break
}
}
if !foundLang {
t.Errorf("bad language (pkg=%+v): %+v", actualPkg.Name, actualPkg.Language)
}
if actualPkg.Type != test.pkgType {
t.Errorf("bad package type (pkg=%+v): %+v", actualPkg.Name, actualPkg.Type)
}
actualPkgCount++
}
if actualPkgCount != len(test.pkgInfo)+test.duplicates {
for actualPkg := range sbom.Artifacts.Packages.Enumerate(test.pkgType) {
t.Log(" ", actualPkg)
}
t.Fatalf("unexpected package count: %d!=%d", actualPkgCount, len(test.pkgInfo))
}
assertPackages(t, sbom, test, observedLanguages, observedPkgs)
})
}
@@ -227,6 +159,7 @@ func TestPkgCoverageDirectory(t *testing.T) {
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
definedPkgs.Remove(string(pkg.Rpkg))
definedPkgs.Remove(string(pkg.UnknownPkg))
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // this is covered as pear packages
// for directory scans we should not expect to see any of the following package types
definedPkgs.Remove(string(pkg.KbPkg))
@@ -241,6 +174,61 @@ func TestPkgCoverageDirectory(t *testing.T) {
}
}
func assertPackages(t *testing.T, sbom sbom.SBOM, test testCase, observedLanguages *strset.Set, observedPkgs *strset.Set) {
actualPkgCount := 0
for actualPkg := range sbom.Artifacts.Packages.Enumerate(test.pkgType) {
observedLanguages.Add(actualPkg.Language.String())
observedPkgs.Add(string(actualPkg.Type))
expectedVersion, ok := test.pkgInfo[actualPkg.Name]
if !ok {
t.Errorf("unexpected package found: %s", actualPkg.Name)
}
if expectedVersion != actualPkg.Version {
t.Errorf("unexpected package version (pkg=%s): %s", actualPkg.Name, actualPkg.Version)
}
var foundLang bool
for _, lang := range strings.Split(test.pkgLanguage.String(), ",") {
if actualPkg.Language.String() == lang {
foundLang = true
break
}
}
if !foundLang {
t.Errorf("bad language (pkg=%+v): %+v", actualPkg.Name, actualPkg.Language)
}
if actualPkg.Type != test.pkgType {
t.Errorf("bad package type (pkg=%+v): %+v", actualPkg.Name, actualPkg.Type)
}
actualPkgCount++
// all packages should have at least one location associated with it, and of those locations at least one should be primary evidence
locs := actualPkg.Locations.ToSlice()
assert.NotEmpty(t, locs, "package %q has no locations (type=%q)", actualPkg.Name, actualPkg.Type)
var primaryEvidenceFound bool
for _, l := range locs {
if _, exists := l.Annotations[pkg.EvidenceAnnotationKey]; !exists {
t.Errorf("missing evidence annotation (pkg=%s type=%s)", actualPkg.Name, actualPkg.Type)
}
if l.Annotations[pkg.EvidenceAnnotationKey] == pkg.PrimaryEvidenceAnnotation {
primaryEvidenceFound = true
}
}
assert.True(t, primaryEvidenceFound, "no primary evidence found for package %q", actualPkg.Name)
}
if actualPkgCount != len(test.pkgInfo)+test.duplicates {
for actualPkg := range sbom.Artifacts.Packages.Enumerate(test.pkgType) {
t.Log(" ", actualPkg)
}
t.Fatalf("unexpected package count: %d!=%d", actualPkgCount, len(test.pkgInfo))
}
}
func TestPkgCoverageImage_HasEvidence(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-pkg-coverage", source.SquashedScope)
@@ -56,6 +56,8 @@ func TestAllPackageCatalogersReachableInTasks(t *testing.T) {
// not reachable since they are deprecated
"dotnet-portable-executable-cataloger",
"dotnet-deps-cataloger",
"nix-store-cataloger",
"php-pecl-serialized-cataloger",
// not reachable by design
"sbom-cataloger",
)
@@ -1 +1 @@
a:5:{s:4:"name";s:9:"memcached";s:4:"date";s:10:"2022-01-11";s:4:"time";s:8:"15:23:47";s:7:"version";a:2:{s:7:"release";s:5:"3.2.0";s:3:"api";s:5:"3.2.0";}s:7:"license";a:2:{s:7:"attribs";a:1:{s:3:"uri";s:26:"http://www.php.net/license";}s:8:"_content";s:11:"PHP License";}}
a:6:{s:4:"name";s:9:"memcached";s:7:"channel";s:12:"pecl.php.net";s:4:"date";s:10:"2022-01-11";s:4:"time";s:8:"15:23:47";s:7:"version";a:2:{s:7:"release";s:5:"3.2.0";s:3:"api";s:5:"3.2.0";}s:7:"license";a:2:{s:7:"attribs";a:1:{s:3:"uri";s:26:"http://www.php.net/license";}s:8:"_content";s:11:"PHP License";}}
+1 -5
View File
@@ -36,11 +36,7 @@ func capture(target **os.File, writer io.Writer, bufSize int) func() {
}()
buf := make([]byte, bufSize)
for {
if original == nil {
break
}
for original != nil {
n, err := r.Read(buf)
if n > 0 {
_, _ = writer.Write(buf[0:n])
@@ -6,6 +6,7 @@ import (
"io"
"path"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
@@ -76,7 +77,6 @@ func newAlpineConfiguration(resolver file.Resolver) (*AlpineConfiguration, []fil
return &AlpineConfiguration{
APKKeys: keys,
}, locations, nil
}
func getVersion(resolver file.Resolver) (string, []file.Location, error) {
@@ -92,6 +92,7 @@ func getVersion(resolver file.Resolver) (string, []file.Location, error) {
if err != nil {
return "", nil, fmt.Errorf("unable to read alpine version: %w", err)
}
defer internal.CloseAndLogError(reader, locations[0].RealPath)
version, err := io.ReadAll(reader)
if err != nil {
@@ -111,7 +112,11 @@ func getAPKKeys(resolver file.Resolver) (map[string]string, []file.Location, err
}
for _, location := range locations {
basename := path.Base(location.RealPath)
//nolint:gocritic
reader, err := resolver.FileContentsByLocation(location)
if err != nil {
return nil, nil, fmt.Errorf("unable to resolve file contents by location at %s: %w", location.RealPath, err)
}
content, err := io.ReadAll(reader)
if err != nil {
return nil, nil, fmt.Errorf("unable to read apk key content at %s: %w", location.RealPath, err)
-1
View File
@@ -124,5 +124,4 @@ func showAlpineConfiguration(s sbom.SBOM) {
panic(err)
}
fmt.Println(string(meta))
}
-1
View File
@@ -30,7 +30,6 @@ func main() {
if err := enc.Encode(sbom.Descriptor.Configuration); err != nil {
panic(err)
}
}
func imageReference() string {
+23 -23
View File
@@ -5,7 +5,7 @@ go 1.24.1
require (
github.com/BurntSushi/toml v1.5.0
github.com/CycloneDX/cyclonedx-go v0.9.2
github.com/Masterminds/semver v1.5.0
github.com/Masterminds/semver/v3 v3.3.1
github.com/Masterminds/sprig/v3 v3.3.0
github.com/OneOfOne/xxhash v1.2.8
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
@@ -19,17 +19,18 @@ require (
github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d
github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6
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.20250220190351-d62adb6e1115
github.com/anchore/stereoscope v0.1.2
github.com/anchore/stereoscope v0.1.4
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/aquasecurity/go-pep440-version v0.0.1
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/bmatcuk/doublestar/v4 v4.8.1
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.3.4
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.5
github.com/charmbracelet/lipgloss v1.1.0
github.com/dave/jennifer v1.7.1
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
@@ -37,10 +38,10 @@ require (
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.2
github.com/github/go-spdx/v2 v2.3.3
github.com/gkampitakis/go-snaps v0.5.11
github.com/go-git/go-billy/v5 v5.6.2
github.com/go-git/go-git/v5 v5.14.0
github.com/go-git/go-git/v5 v5.16.0
github.com/go-test/deep v1.1.1
github.com/go-viper/mapstructure/v2 v2.2.1
github.com/gohugoio/hashstructure v0.5.0
@@ -57,9 +58,10 @@ require (
github.com/jinzhu/copier v0.4.0
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/knqyf263/go-rpmdb v0.1.1
github.com/magiconair/properties v1.8.9
github.com/mholt/archives v0.1.0
github.com/magiconair/properties v1.8.10
github.com/mholt/archives v0.1.1
github.com/moby/sys/mountinfo v0.7.2
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1
github.com/olekukonko/tablewriter v0.0.5
github.com/opencontainers/go-digest v1.0.0
github.com/pelletier/go-toml v1.9.5
@@ -85,7 +87,7 @@ require (
go.uber.org/goleak v1.3.0
golang.org/x/exp v0.0.0-20250305212735-054e65f0b394
golang.org/x/mod v0.24.0
golang.org/x/net v0.38.0
golang.org/x/net v0.40.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.37.0
)
@@ -96,7 +98,6 @@ require (
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.11.7 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
@@ -118,7 +119,7 @@ require (
github.com/charmbracelet/x/ansi v0.8.0 // indirect
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cloudflare/circl v1.6.0 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/containerd v1.7.27 // indirect
github.com/containerd/containerd/api v1.8.0 // indirect
@@ -132,9 +133,9 @@ require (
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v28.0.4+incompatible // indirect
github.com/docker/cli v28.1.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.0.4+incompatible // indirect
github.com/docker/docker v28.1.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.8.2 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
@@ -145,7 +146,7 @@ require (
github.com/felixge/fgprof v0.9.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
github.com/gkampitakis/ciinfo v0.3.1 // indirect
github.com/gkampitakis/go-diff v1.3.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
@@ -176,13 +177,14 @@ require (
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/minio/minlz v1.0.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.0 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
@@ -191,7 +193,7 @@ require (
github.com/muesli/termenv v0.16.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 // indirect
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
@@ -240,11 +242,11 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/crypto v0.36.0 // indirect
golang.org/x/sync v0.12.0 // indirect
golang.org/x/sys v0.31.0 // indirect
golang.org/x/term v0.30.0 // indirect
golang.org/x/text v0.23.0 // indirect
golang.org/x/crypto v0.38.0 // indirect
golang.org/x/sync v0.14.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/term v0.32.0 // indirect
golang.org/x/text v0.25.0 // indirect
golang.org/x/tools v0.31.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
@@ -257,8 +259,6 @@ require (
modernc.org/memory v1.9.1 // indirect
)
require github.com/anchore/go-sync v0.0.0-20250326131806-4eda43a485b6
retract (
v0.53.2
v0.53.1 // Published accidentally with incorrect license in depdencies
+52 -42
View File
@@ -66,10 +66,8 @@ github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
@@ -122,8 +120,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.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
github.com/anchore/stereoscope v0.1.2 h1:0+Jcf7hoImYKfrH2XzN6vvbmbpm68A/woabC43gZ4eU=
github.com/anchore/stereoscope v0.1.2/go.mod h1:kU4LkiocOimxuSnlmJgilHCfDMCR5t/UroKIpzI8jNw=
github.com/anchore/stereoscope v0.1.4 h1:e+iT9UdUzLBabWGe84hn5sTHDRioY+4IHsVzJXuJlek=
github.com/anchore/stereoscope v0.1.4/go.mod h1:omWgXDEp/XfqCJlZXIByEo1c3ArZg/qTJ5LBKVLAIdw=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
@@ -149,6 +147,8 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
github.com/becheran/wildmatch-go v1.0.0/go.mod h1:gbMvj0NtVdJ15Mg/mH9uxk2R1QCistMyU7d9KFzroX4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
@@ -179,10 +179,10 @@ 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/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
github.com/charmbracelet/bubbletea v1.3.4 h1:kCg7B+jSCFPLYRA52SDZjr51kG/fMUEoPoZrkaDHyoI=
github.com/charmbracelet/bubbletea v1.3.4/go.mod h1:dtcUCyCGEX3g9tosuYiut3MXgY/Jsv9nKVdibKKRRXo=
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
github.com/charmbracelet/bubbletea v1.3.5 h1:JAMNLTbqMOhSwoELIr0qyP4VidFq72/6E9j7HHmRKQc=
github.com/charmbracelet/bubbletea v1.3.5/go.mod h1:TkCnmH+aBd4LrXhXcqrKiYwRs7qyQx5rBgH5fVY3v54=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
@@ -193,6 +193,8 @@ github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2ll
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
@@ -207,8 +209,8 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk=
github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
@@ -260,12 +262,12 @@ github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4i
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v28.0.4+incompatible h1:pBJSJeNd9QeIWPjRcV91RVJihd/TXB77q1ef64XEu4A=
github.com/docker/cli v28.0.4+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.1.1+incompatible h1:eyUemzeI45DY7eDPuwUcmDyDj1pM98oD5MdSpiItp8k=
github.com/docker/cli v28.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.0.4+incompatible h1:JNNkBctYKurkw6FrHfKqY0nKIDf5nrbxjVBtS+cdcok=
github.com/docker/docker v28.0.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.1.1+incompatible h1:49M11BFLsVO1gxY9UX9p/zwkE/rswggs8AdFmXQw51I=
github.com/docker/docker v28.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@@ -316,11 +318,11 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.3.2 h1:IfdyNHTqzs4zAJjXdVQfRnxt1XMfycXoHBE2Vsm1bjs=
github.com/github/go-spdx/v2 v2.3.2/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
github.com/github/go-spdx/v2 v2.3.3 h1:QI7evnHWEfWkT54eJwkoV/f3a0xD3gLlnVmT5wQG6LE=
github.com/github/go-spdx/v2 v2.3.3/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
github.com/gkampitakis/ciinfo v0.3.1 h1:lzjbemlGI4Q+XimPg64ss89x8Mf3xihJqy/0Mgagapo=
github.com/gkampitakis/ciinfo v0.3.1/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
@@ -337,8 +339,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.14.0 h1:/MD3lCrGjCen5WfEAzKg00MJJffKhC8gzS80ycmCi60=
github.com/go-git/go-git/v5 v5.14.0/go.mod h1:Z5Xhoia5PcWA3NF8vRLURn9E5FRhSl7dGj9ItW3Wk5k=
github.com/go-git/go-git/v5 v5.16.0 h1:k3kuOEpkc0DeY7xlL6NaaNg39xdgQbtH5mwCafHO9AQ=
github.com/go-git/go-git/v5 v5.16.0/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -569,8 +571,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
@@ -598,11 +600,13 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/archives v0.1.0 h1:FacgJyrjiuyomTuNA92X5GyRBRZjE43Y/lrzKIlF35Q=
github.com/mholt/archives v0.1.0/go.mod h1:j/Ire/jm42GN7h90F5kzj6hf6ZFzEH66de+hmjEKu+I=
github.com/mholt/archives v0.1.1 h1:c7J3qXN1FB54y0qiUXiq9Bxk4eCUc8pdXWwOhZdRzeY=
github.com/mholt/archives v0.1.1/go.mod h1:FQVz01Q2uXKB/35CXeW/QFO23xT+hSCGZHVtha78U4I=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@@ -620,10 +624,12 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
@@ -648,10 +654,14 @@ github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 h1:kpt9ZfKcm+EDG4s40hMwE//d5SBgDjUOrITReV2u4aA=
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1/go.mod h1:qgCw4bBKZX8qMgGeEZzGFVT3notl42dBjNqO2jut0M0=
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE=
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78 h1:MYzLheyVx1tJVDqfu3YnN4jtnyALNzLvwl+f58TcvQY=
github.com/nwaples/rardecode/v2 v2.0.0-beta.4.0.20241112120701-034e449c6e78/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
@@ -915,8 +925,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/exp v0.0.0-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=
@@ -1002,8 +1012,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1033,8 +1043,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw=
golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1110,13 +1120,13 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/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.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y=
golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1127,8 +1137,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY=
golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+1 -1
View File
@@ -13,5 +13,5 @@ func DefaultLocationComparer(x, y file.Location) bool {
}
func LocationComparerWithoutLayer(x, y file.Location) bool {
return cmp.Equal(x.Coordinates.RealPath, y.Coordinates.RealPath) && cmp.Equal(x.AccessPath, y.AccessPath)
return cmp.Equal(x.RealPath, y.RealPath) && cmp.Equal(x.AccessPath, y.AccessPath)
}
+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.24"
JSONSchemaVersion = "16.0.28"
)
+1 -1
View File
@@ -23,7 +23,7 @@ func ExtractGlobsFromTarToUniqueTempFile(archivePath, dir string, globs ...strin
defer file.Close()
// ignore directories
if file.FileInfo.IsDir() {
if file.IsDir() {
return nil
}
+1 -1
View File
@@ -28,7 +28,7 @@ func NewZipFileManifest(archivePath string) (ZipFileManifest, error) {
}
}()
for _, file := range zipReader.Reader.File {
for _, file := range zipReader.File {
manifest.Add(file.Name, file.FileInfo())
}
return manifest, nil
+1 -1
View File
@@ -53,7 +53,7 @@ func TraverseFilesInZip(archivePath string, visitor func(*zip.File) error, paths
}
}()
for _, file := range zipReader.Reader.File {
for _, file := range zipReader.File {
// if no paths are given then assume that all files should be traversed
if len(paths) > 0 {
if _, ok := request[file.Name]; !ok {
+9 -2
View File
@@ -6,12 +6,19 @@ import (
type licenseScannerKey struct{}
var ctxKey = licenseScannerKey{}
func SetContextLicenseScanner(ctx context.Context, s Scanner) context.Context {
return context.WithValue(ctx, licenseScannerKey{}, s)
return context.WithValue(ctx, ctxKey, s)
}
func IsContextLicenseScannerSet(ctx context.Context) bool {
_, ok := ctx.Value(ctxKey).(Scanner)
return ok
}
func ContextLicenseScanner(ctx context.Context) (Scanner, error) {
if s, ok := ctx.Value(licenseScannerKey{}).(Scanner); ok {
if s, ok := ctx.Value(ctxKey).(Scanner); ok {
return s, nil
}
return NewDefaultScanner()
+48
View File
@@ -0,0 +1,48 @@
package licenses
import (
"context"
"testing"
"github.com/stretchr/testify/require"
)
func TestSetContextLicenseScanner(t *testing.T) {
scanner := testScanner(true)
ctx := context.Background()
ctx = SetContextLicenseScanner(ctx, scanner)
val := ctx.Value(ctxKey)
require.NotNil(t, val)
s, ok := val.(Scanner)
require.True(t, ok)
require.Equal(t, scanner, s)
}
func TestIsContextLicenseScannerSet(t *testing.T) {
scanner := testScanner(true)
ctx := context.Background()
require.False(t, IsContextLicenseScannerSet(ctx))
ctx = SetContextLicenseScanner(ctx, scanner)
require.True(t, IsContextLicenseScannerSet(ctx))
}
func TestContextLicenseScanner(t *testing.T) {
t.Run("with scanner", func(t *testing.T) {
scanner := testScanner(true)
ctx := SetContextLicenseScanner(context.Background(), scanner)
s, err := ContextLicenseScanner(ctx)
if err != nil || s != scanner {
t.Fatal("expected scanner from context")
}
})
t.Run("without scanner", func(t *testing.T) {
ctx := context.Background()
s, err := ContextLicenseScanner(ctx)
if err != nil || s == nil {
t.Fatal("expected default scanner")
}
})
}
@@ -0,0 +1,5 @@
Knuth CTAN License
Full name
Knuth CTAN License
This software is copyrighted. Unlimited copying and redistribution of this package and/or its individual files are permitted as long as there are no modifications. Modifications, and redistribution of modifications, are also permitted, but only if the resulting package and/or files are renamed.
+45 -21
View File
@@ -9,6 +9,7 @@ import (
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
@@ -32,11 +33,11 @@ type ownershipByFilesMetadata struct {
Files []string `json:"files"`
}
func ByFileOwnershipOverlapWorker(accessor sbomsync.Accessor) {
func ByFileOwnershipOverlapWorker(resolver file.Resolver, accessor sbomsync.Accessor) {
var relationships []artifact.Relationship
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
relationships = byFileOwnershipOverlap(s.Artifacts.Packages)
relationships = byFileOwnershipOverlap(resolver, s.Artifacts.Packages)
})
accessor.WriteToSBOM(func(s *sbom.SBOM) {
@@ -46,8 +47,8 @@ func ByFileOwnershipOverlapWorker(accessor sbomsync.Accessor) {
// byFileOwnershipOverlap creates a package-to-package relationship based on discovering which packages have
// evidence locations that overlap with ownership claim from another package's package manager metadata.
func byFileOwnershipOverlap(catalog *pkg.Collection) []artifact.Relationship {
var relationships = findOwnershipByFilesRelationships(catalog)
func byFileOwnershipOverlap(resolver file.Resolver, catalog *pkg.Collection) []artifact.Relationship {
var relationships = findOwnershipByFilesRelationships(resolver, catalog)
var edges []artifact.Relationship
for parentID, children := range relationships {
@@ -84,7 +85,7 @@ func byFileOwnershipOverlap(catalog *pkg.Collection) []artifact.Relationship {
// findOwnershipByFilesRelationships find overlaps in file ownership with a file that defines another package. Specifically, a .Location.Path of
// a package is found to be owned by another (from the owner's .Metadata.Files[]).
func findOwnershipByFilesRelationships(catalog *pkg.Collection) map[artifact.ID]map[artifact.ID]*strset.Set {
func findOwnershipByFilesRelationships(resolver file.Resolver, catalog *pkg.Collection) map[artifact.ID]map[artifact.ID]*strset.Set { //nolint:gocognit
var relationships = make(map[artifact.ID]map[artifact.ID]*strset.Set)
if catalog == nil {
@@ -102,27 +103,31 @@ func findOwnershipByFilesRelationships(catalog *pkg.Collection) map[artifact.ID]
if !ok {
continue
}
for _, ownedFilePath := range pkgFileOwner.OwnedFiles() {
if matchesAny(ownedFilePath, globsForbiddenFromBeingOwned) {
// we skip over known exceptions to file ownership, such as the RPM package owning
// the RPM DB path, otherwise the RPM package would "own" all RPMs, which is not intended
continue
}
// look for package(s) in the catalog that may be owned by this package and mark the relationship
for _, subPackage := range catalog.PackagesByPath(ownedFilePath) {
subID := subPackage.ID()
if subID == id {
for _, ownedFilePath := range pkgFileOwner.OwnedFiles() {
// find the first path that results in a hit
for _, ownedPath := range allPaths(ownedFilePath, resolver) {
if matchesAny(ownedPath, globsForbiddenFromBeingOwned) {
// we skip over known exceptions to file ownership, such as the RPM package owning
// the RPM DB path, otherwise the RPM package would "own" all RPMs, which is not intended
continue
}
if _, exists := relationships[id]; !exists {
relationships[id] = make(map[artifact.ID]*strset.Set)
}
if _, exists := relationships[id][subID]; !exists {
relationships[id][subID] = strset.New()
// look for package(s) in the catalog that may be owned by this package and mark the relationship
for _, subPackage := range catalog.PackagesByPath(ownedPath) {
subID := subPackage.ID()
if subID == id {
continue
}
if _, exists := relationships[id]; !exists {
relationships[id] = make(map[artifact.ID]*strset.Set)
}
if _, exists := relationships[id][subID]; !exists {
relationships[id][subID] = strset.New()
}
relationships[id][subID].Add(ownedPath)
}
relationships[id][subID].Add(ownedFilePath)
}
}
}
@@ -130,6 +135,25 @@ func findOwnershipByFilesRelationships(catalog *pkg.Collection) map[artifact.ID]
return relationships
}
func allPaths(ownedFilePath string, resolver file.Resolver) []string {
// though we have a string path, we need to resolve symlinks and other filesystem oddities since we cannot assume this is a real path
var locs []file.Location
var err error
if resolver != nil {
locs, err = resolver.FilesByPath(ownedFilePath)
if err != nil {
log.WithFields("error", err, "path", ownedFilePath).Trace("unable to find path for owned file")
locs = nil
}
}
ownedFilePaths := strset.New(ownedFilePath)
for _, loc := range locs {
ownedFilePaths.Add(loc.RealPath)
}
return ownedFilePaths.List()
}
func matchesAny(s string, globs []string) bool {
for _, g := range globs {
matches, err := doublestar.Match(g, s)
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/cmptest"
"github.com/anchore/syft/syft/artifact"
@@ -12,11 +12,29 @@ import (
"github.com/anchore/syft/syft/pkg"
)
type mockFR struct {
file.Resolver
translate map[string]string
}
func (m mockFR) FilesByPath(paths ...string) ([]file.Location, error) {
var results []file.Location
for _, p := range paths {
tPath, ok := m.translate[p]
if !ok {
tPath = p
}
results = append(results, file.NewLocation(tPath))
}
return results, nil
}
func TestOwnershipByFilesRelationship(t *testing.T) {
tests := []struct {
name string
setup func(t testing.TB) ([]pkg.Package, []artifact.Relationship)
name string
resolver file.Resolver
setup func(t testing.TB) ([]pkg.Package, []artifact.Relationship)
}{
{
name: "owns-by-real-path",
@@ -60,6 +78,75 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
return []pkg.Package{parent, child}, []artifact.Relationship{relationship}
},
},
{
name: "misses-by-dead-symlink",
resolver: mockFR{
translate: map[string]string{
"/bin/gzip": "", // treat this as a dead symlink
},
},
setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) {
parent := pkg.Package{
Type: pkg.DebPkg,
Metadata: pkg.DpkgDBEntry{
Files: []pkg.DpkgFileRecord{
{Path: "/bin/gzip"}, // this symlinks to gzip via /bin -> /usr/bin
},
},
}
parent.SetID()
child := pkg.Package{
Locations: file.NewLocationSet(
file.NewVirtualLocation("/usr/bin/gzip", "/usr/bin/gzip"),
),
Type: pkg.BinaryPkg,
}
child.SetID()
return []pkg.Package{parent, child}, nil // importantly, no relationship is expected
},
},
{
name: "owns-by-symlink",
resolver: mockFR{
translate: map[string]string{
"/bin/gzip": "/usr/bin/gzip", // if there is a string path of /bin/gzip then return the real path of /usr/bin/gzip
},
},
setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) {
parent := pkg.Package{
Type: pkg.DebPkg,
Metadata: pkg.DpkgDBEntry{
Files: []pkg.DpkgFileRecord{
{Path: "/bin/gzip"}, // this symlinks to gzip via /bin -> /usr/bin
},
},
}
parent.SetID()
child := pkg.Package{
Locations: file.NewLocationSet(
file.NewVirtualLocation("/usr/bin/gzip", "/usr/bin/gzip"),
),
Type: pkg.BinaryPkg,
}
child.SetID()
relationship := artifact.Relationship{
From: parent,
To: child,
Type: artifact.OwnershipByFileOverlapRelationship,
Data: ownershipByFilesMetadata{
Files: []string{
"/usr/bin/gzip",
},
},
}
return []pkg.Package{parent, child}, []artifact.Relationship{relationship}
},
},
{
name: "owns-by-virtual-path",
setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) {
@@ -140,9 +227,9 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
t.Run(test.name, func(t *testing.T) {
pkgs, expectedRelations := test.setup(t)
c := pkg.NewCollection(pkgs...)
relationships := byFileOwnershipOverlap(c)
relationships := byFileOwnershipOverlap(test.resolver, c)
assert.Len(t, relationships, len(expectedRelations))
require.Len(t, relationships, len(expectedRelations))
for idx, expectedRelationship := range expectedRelations {
actualRelationship := relationships[idx]
if d := cmp.Diff(expectedRelationship, actualRelationship, cmptest.DefaultOptions()...); d != "" {
+1 -1
View File
@@ -22,7 +22,7 @@ func less(i, j artifact.Relationship) bool {
jFrom, ok3 := j.From.(pkg.Package)
jTo, ok4 := j.To.(pkg.Package)
if !(ok1 && ok2 && ok3 && ok4) {
if !ok1 && !ok2 && !ok3 && !ok4 {
return false
}
+2 -2
View File
@@ -245,10 +245,10 @@ func packageFileOwnershipRelationships(p pkg.Package, resolver file.PathResolver
}
for _, ref := range pathRefs {
if oldRef, ok := locations[ref.Coordinates.ID()]; ok {
if oldRef, ok := locations[ref.ID()]; ok {
log.Debugf("found path duplicate of %s", oldRef.RealPath)
}
locations[ref.Coordinates.ID()] = ref
locations[ref.ID()] = ref
}
}
+11 -3
View File
@@ -69,7 +69,7 @@ func DefaultPackageTaskFactories() Factories {
newSimplePackageTaskFactory(cpp.NewConanInfoCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "cpp", "conan"),
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(r.NewPackageCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, pkgcataloging.DirectoryTag, "r"),
newSimplePackageTaskFactory(ruby.NewInstalledGemSpecCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "ruby", "gem", "gemspec"),
newSimplePackageTaskFactory(rust.NewAuditBinaryCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "rust", "binary"),
@@ -100,7 +100,7 @@ func DefaultPackageTaskFactories() Factories {
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"),
newSimplePackageTaskFactory(php.NewPearCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, pkgcataloging.ImageTag, "php", "pear"),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return python.NewPackageCataloger(cfg.PackagesConfig.Python)
@@ -137,7 +137,12 @@ func DefaultPackageTaskFactories() Factories {
pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, Java, Maven,
),
newSimplePackageTaskFactory(java.NewNativeImageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, Java),
newSimplePackageTaskFactory(nix.NewStoreCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "nix"),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return nix.NewCataloger(cfg.PackagesConfig.Nix)
},
pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "nix",
),
newSimplePackageTaskFactory(lua.NewPackageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "lua"),
// other package catalogers ///////////////////////////////////////////////////////////////////////////
@@ -166,5 +171,8 @@ func DefaultPackageTaskFactories() Factories {
// these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible)
newSimplePackageTaskFactory(dotnet.NewDotnetDepsCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
newSimplePackageTaskFactory(php.NewPeclCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
newSimplePackageTaskFactory(nix.NewStoreCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
}
}
+1 -1
View File
@@ -46,7 +46,7 @@ func finalizeRelationships(resolver file.Resolver, builder sbomsync.Builder, cfg
// add relationships showing packages that are evident by a file which is owned by another package (package-to-package)
if cfg.PackageFileOwnershipOverlap {
relationship.ByFileOwnershipOverlapWorker(accessor)
relationship.ByFileOwnershipOverlapWorker(resolver, accessor)
}
// conditionally remove binary packages based on file ownership overlap relationships found
+63
View File
@@ -0,0 +1,63 @@
package task
import (
"context"
"fmt"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
func NewDeepSquashedScopeCleanupTask() Task {
fn := func(_ context.Context, _ file.Resolver, builder sbomsync.Builder) error {
accessor := builder.(sbomsync.Accessor)
// remove all packages that doesn't exist in the final state of the image
builder.DeletePackages(packagesToRemove(accessor)...)
return nil
}
return NewTask("deep-squashed-cleaner", fn)
}
func packagesToRemove(accessor sbomsync.Accessor) []artifact.ID {
pkgsToDelete := make([]artifact.ID, 0)
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
filterDuplicates := make(map[string]bool)
for p := range s.Artifacts.Packages.Enumerate() {
noSquashed := true
noPrimary := true
for _, l := range p.Locations.ToSlice() {
isPrimaryEvidence := l.Annotations[pkg.EvidenceAnnotationKey] == pkg.PrimaryEvidenceAnnotation
switch l.Annotations[file.VisibleAnnotationKey] {
case file.VisibleAnnotation:
if isPrimaryEvidence || p.Type == pkg.BinaryPkg {
noSquashed = false
break
}
case "":
if isPrimaryEvidence {
if exists := filterDuplicates[getKey(p, l)]; exists {
break
}
filterDuplicates[getKey(p, l)] = true
noPrimary = false
break
}
}
}
if noSquashed && noPrimary {
pkgsToDelete = append(pkgsToDelete, p.ID())
}
}
})
return pkgsToDelete
}
func getKey(pkg pkg.Package, loc file.Location) string {
return fmt.Sprintf("%s-%s-%s-%s", pkg.Name, pkg.Version, loc.RealPath, loc.AccessPath)
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+97 -4
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "anchore.io/schema/syft/json/16.0.24/document",
"$id": "anchore.io/schema/syft/json/16.0.28/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@@ -995,6 +995,20 @@
"size"
]
},
"GithubActionsUseStatement": {
"properties": {
"value": {
"type": "string"
},
"comment": {
"type": "string"
}
},
"type": "object",
"required": [
"value"
]
},
"GoModuleBuildinfoEntry": {
"properties": {
"goBuildSettings": {
@@ -1377,6 +1391,9 @@
"value": {
"type": "string"
},
"fullText": {
"type": "string"
},
"spdxExpression": {
"type": "string"
},
@@ -1402,6 +1419,7 @@
"type": "object",
"required": [
"value",
"fullText",
"spdxExpression",
"type",
"urls",
@@ -1642,14 +1660,57 @@
"kb"
]
},
"NixDerivation": {
"properties": {
"path": {
"type": "string"
},
"system": {
"type": "string"
},
"inputDerivations": {
"items": {
"$ref": "#/$defs/NixDerivationReference"
},
"type": "array"
},
"inputSources": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"NixDerivationReference": {
"properties": {
"path": {
"type": "string"
},
"outputs": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object"
},
"NixStoreEntry": {
"properties": {
"outputHash": {
"path": {
"type": "string"
},
"output": {
"type": "string"
},
"outputHash": {
"type": "string"
},
"derivation": {
"$ref": "#/$defs/NixDerivation"
},
"files": {
"items": {
"type": "string"
@@ -1659,8 +1720,7 @@
},
"type": "object",
"required": [
"outputHash",
"files"
"outputHash"
]
},
"OpamPackage": {
@@ -1804,6 +1864,9 @@
{
"$ref": "#/$defs/ErlangRebarLockEntry"
},
{
"$ref": "#/$defs/GithubActionsUseStatement"
},
{
"$ref": "#/$defs/GoModuleBuildinfoEntry"
},
@@ -1855,6 +1918,9 @@
{
"$ref": "#/$defs/PhpComposerLockEntry"
},
{
"$ref": "#/$defs/PhpPearEntry"
},
{
"$ref": "#/$defs/PhpPeclEntry"
},
@@ -2147,11 +2213,38 @@
"dist"
]
},
"PhpPearEntry": {
"properties": {
"name": {
"type": "string"
},
"channel": {
"type": "string"
},
"version": {
"type": "string"
},
"license": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
"required": [
"name",
"version"
]
},
"PhpPeclEntry": {
"properties": {
"name": {
"type": "string"
},
"channel": {
"type": "string"
},
"version": {
"type": "string"
},
+10 -2
View File
@@ -7,6 +7,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/java"
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
"github.com/anchore/syft/syft/pkg/cataloger/nix"
"github.com/anchore/syft/syft/pkg/cataloger/python"
)
@@ -17,6 +18,7 @@ type Config struct {
JavaArchive java.ArchiveCatalogerConfig `yaml:"java-archive" json:"java-archive" mapstructure:"java-archive"`
JavaScript javascript.CatalogerConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"`
LinuxKernel kernel.LinuxKernelCatalogerConfig `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"`
Nix nix.Config `yaml:"nix" json:"nix" mapstructure:"nix"`
Python python.CatalogerConfig `yaml:"python" json:"python" mapstructure:"python"`
}
@@ -25,12 +27,18 @@ func DefaultConfig() Config {
Binary: binary.DefaultClassifierCatalogerConfig(),
Dotnet: dotnet.DefaultCatalogerConfig(),
Golang: golang.DefaultCatalogerConfig(),
LinuxKernel: kernel.DefaultLinuxKernelCatalogerConfig(),
Python: python.DefaultCatalogerConfig(),
JavaArchive: java.DefaultArchiveCatalogerConfig(),
LinuxKernel: kernel.DefaultLinuxKernelCatalogerConfig(),
Nix: nix.DefaultConfig(),
Python: python.DefaultCatalogerConfig(),
}
}
func (c Config) WithNixConfig(cfg nix.Config) Config {
c.Nix = cfg
return c
}
func (c Config) WithBinaryConfig(cfg binary.ClassifierCatalogerConfig) Config {
c.Binary = cfg
return c
+11 -4
View File
@@ -96,14 +96,21 @@ func setupContext(ctx context.Context, cfg *CreateSBOMConfig) (context.Context,
ctx = setContextExecutors(ctx, cfg)
// configure license scanner
return setContextLicenseScanner(ctx, cfg)
// skip injecting a license scanner if one already set on context
if licenses.IsContextLicenseScannerSet(ctx) {
return ctx, nil
}
return SetContextLicenseScanner(ctx, cfg.Licenses)
}
func setContextLicenseScanner(ctx context.Context, cfg *CreateSBOMConfig) (context.Context, error) {
// SetContextLicenseScanner creates and sets a license scanner
// on the provided context using the provided license config.
func SetContextLicenseScanner(ctx context.Context, cfg cataloging.LicenseConfig) (context.Context, error) {
// inject a single license scanner and content config for all package cataloging tasks into context
licenseScanner, err := licenses.NewDefaultScanner(
licenses.WithIncludeLicenseContent(cfg.Licenses.IncludeUnkownLicenseContent),
licenses.WithCoverage(cfg.Licenses.Coverage),
licenses.WithIncludeLicenseContent(cfg.IncludeUnkownLicenseContent),
licenses.WithCoverage(cfg.Coverage),
)
if err != nil {
return nil, fmt.Errorf("could not build licenseScanner for cataloging: %w", err)
+17
View File
@@ -188,6 +188,7 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
// generate package and file tasks based on the configuration
environmentTasks := c.environmentTasks()
scopeTasks := c.scopeTasks()
relationshipsTasks := c.relationshipTasks(src)
unknownTasks := c.unknownsTasks()
@@ -204,6 +205,11 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
taskGroups = append(taskGroups, append(pkgTasks, fileTasks...))
}
// all scope work must be done after all nodes (files and packages) have been cataloged and before the relationship
if len(scopeTasks) > 0 {
taskGroups = append(taskGroups, scopeTasks)
}
// all relationship work must be done after all nodes (files and packages) have been cataloged
if len(relationshipsTasks) > 0 {
taskGroups = append(taskGroups, relationshipsTasks)
@@ -391,6 +397,17 @@ func (c *CreateSBOMConfig) userPackageTasks(cfg task.CatalogingFactoryConfig) ([
return persistentPackageTasks, selectablePackageTasks, nil
}
// scopeTasks returns the set of tasks that should be run to generate additional scope information
func (c *CreateSBOMConfig) scopeTasks() []task.Task {
var tsks []task.Task
if c.Search.Scope == source.DeepSquashedScope {
if t := task.NewDeepSquashedScopeCleanupTask(); t != nil {
tsks = append(tsks, t)
}
}
return tsks
}
// relationshipTasks returns the set of tasks that should be run to generate additional relationships as well as
// prune existing relationships.
func (c *CreateSBOMConfig) relationshipTasks(src source.Description) []task.Task {
+18 -4
View File
@@ -9,6 +9,17 @@ import (
"github.com/anchore/stereoscope/pkg/image"
)
const (
// VisibleAnnotationKey is the key used to indicate if the location is visible or not at runtime
VisibleAnnotationKey = "visible"
// HiddenAnnotation is the value used to indicate that the location is not visible at runtime because it was deleted
HiddenAnnotation = "false"
// VisibleAnnotation is the value used to indicate that the location is visible at runtime
VisibleAnnotation = "true"
)
// Location represents a path relative to a particular filesystem resolved to a specific file.Reference. This struct is used as a key
// in content fetching to uniquely identify a file relative to a request (the AccessPath).
type Location struct {
@@ -48,15 +59,18 @@ func (m *LocationMetadata) merge(other LocationMetadata) error {
}
func (l Location) WithAnnotation(key, value string) Location {
if l.LocationMetadata.Annotations == nil {
l.LocationMetadata.Annotations = map[string]string{}
if key == "" || value == "" {
return l
}
l.LocationMetadata.Annotations[key] = value
if l.Annotations == nil {
l.Annotations = map[string]string{}
}
l.Annotations[key] = value
return l
}
func (l Location) WithoutAnnotations() Location {
l.LocationMetadata.Annotations = map[string]string{}
l.Annotations = map[string]string{}
return l
}
@@ -759,13 +759,18 @@ func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense {
for _, id := range ids {
license := licenses[id]
value := license.Value
fullText := license.FullText
// handle cases where LicenseRef needs to be included in hasExtractedLicensingInfos
if license.Value == "" {
value, _ = strings.CutPrefix(license.ID, "LicenseRef-")
}
other := &spdx.OtherLicense{
LicenseIdentifier: license.ID,
ExtractedText: value,
}
if fullText != "" {
other.ExtractedText = fullText
} else {
other.ExtractedText = value
}
customPrefix := spdxlicense.LicenseRefPrefix + helpers.SanitizeElementID(internallicenses.UnknownLicensePrefix)
if strings.HasPrefix(license.ID, customPrefix) {
@@ -361,6 +361,7 @@ func collectDocRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out
from, fromOk := a.(pkg.Package)
toPackage, toPackageOk := b.(pkg.Package)
toLocation, toLocationOk := b.(file.Location)
//nolint:staticcheck
if !fromOk || !(toPackageOk || toLocationOk) {
log.Debugf("unable to find valid relationship mapping from SPDX, ignoring: (from: %+v) (to: %+v)", a, b)
continue
@@ -498,14 +499,15 @@ func extractPkgInfo(p *spdx.Package) pkgInfo {
func toSyftPackage(p *spdx.Package) pkg.Package {
info := extractPkgInfo(p)
sP := &pkg.Package{
Type: info.typ,
Name: p.PackageName,
Version: p.PackageVersion,
Licenses: pkg.NewLicenseSet(parseSPDXLicenses(p)...),
CPEs: extractCPEs(p),
PURL: purlValue(info.purl),
Language: info.lang,
Metadata: extractMetadata(p, info),
Type: info.typ,
Name: p.PackageName,
Version: p.PackageVersion,
Licenses: pkg.NewLicenseSet(parseSPDXLicenses(p)...),
CPEs: extractCPEs(p),
PURL: purlValue(info.purl),
Language: info.lang,
Metadata: extractMetadata(p, info),
SupplementalData: []any{artifact.ID(p.PackageSPDXIdentifier)},
}
sP.SetID()
@@ -589,12 +589,18 @@ func Test_convertToAndFromFormat(t *testing.T) {
got, err := ToSyftModel(doc)
require.NoError(t, err)
for _, p := range got.Artifacts.Packages.Sorted() {
// all decoders should be setting an ID of sorts here (another test will verify the correctness of the value)
assert.NotEmpty(t, p.SupplementalData)
}
if diff := cmp.Diff(&s, got,
cmpopts.IgnoreUnexported(artifact.Relationship{}),
cmpopts.IgnoreUnexported(file.LocationSet{}),
cmpopts.IgnoreUnexported(pkg.Collection{}),
cmpopts.IgnoreUnexported(pkg.Package{}),
cmpopts.IgnoreUnexported(pkg.LicenseSet{}),
cmpopts.IgnoreFields(pkg.Package{}, "SupplementalData"), // this is used by decoders to store additional data from the original format
cmpopts.IgnoreFields(sbom.Artifacts{}, "FileMetadata", "FileDigests"),
); diff != "" {
t.Fatalf("packages do not match:\n%s", diff)
@@ -664,7 +670,7 @@ func Test_directPackageFiles(t *testing.T) {
Packages: []*spdx.Package{
{
PackageName: "some-package",
PackageSPDXIdentifier: "1",
PackageSPDXIdentifier: "1", // important!
PackageVersion: "1.0.5",
Files: []*spdx.File{
{
@@ -686,10 +692,11 @@ func Test_directPackageFiles(t *testing.T) {
require.NoError(t, err)
p := pkg.Package{
Name: "some-package",
Version: "1.0.5",
Name: "some-package",
Version: "1.0.5",
SupplementalData: []any{artifact.ID("1")}, // set by the decoders from the original element ID
}
p.SetID()
p.OverrideID("1") // the same as the spdxID on the package element
f := file.Location{
LocationData: file.LocationData{
Coordinates: file.Coordinates{
+9 -3
View File
@@ -3,6 +3,7 @@ package cyclonedxjson
import (
"bytes"
"flag"
"regexp"
"strings"
"testing"
@@ -116,6 +117,14 @@ func TestCycloneDxImageEncoder(t *testing.T) {
func redactor(values ...string) testutil.Redactor {
return testutil.NewRedactions().
WithValuesRedacted(values...).
WithPatternRedactorSpec(
testutil.PatternReplacement{
// only the source component bom-ref (not package or other component bom-refs)
Search: regexp.MustCompile(`"component": \{[^}]*"bom-ref":\s*"(?P<redact>.+)"[^}]*}`),
Groups: []string{"redact"}, // use the regex to anchore the search, but only replace bytes within the capture group
Replace: "redacted",
},
).
WithPatternRedactors(
map[string]string{
// UUIDs
@@ -126,9 +135,6 @@ func redactor(values ...string) testutil.Redactor {
// image hashes
`sha256:[A-Fa-f0-9]{64}`: `sha256:redacted`,
// BOM refs
`"bom-ref":\s*"[^"]+"`: `"bom-ref":"redacted"`,
},
)
}
@@ -17,14 +17,14 @@
]
},
"component": {
"bom-ref":"redacted",
"bom-ref": "redacted",
"type": "file",
"name": "some/path"
}
},
"components": [
{
"bom-ref":"redacted",
"bom-ref": "4dd25c6ee16b729a",
"type": "library",
"name": "package-1",
"version": "1.0.1",
@@ -61,7 +61,7 @@
]
},
{
"bom-ref":"redacted",
"bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=39392bb5e270f669",
"type": "library",
"name": "package-2",
"version": "2.0.1",
@@ -91,7 +91,7 @@
]
},
{
"bom-ref":"redacted",
"bom-ref": "os:debian@1.2.3",
"type": "operating-system",
"name": "debian",
"version": "1.2.3",
@@ -17,7 +17,7 @@
]
},
"component": {
"bom-ref":"redacted",
"bom-ref": "redacted",
"type": "container",
"name": "user-image-input",
"version": "sha256:redacted"
@@ -25,7 +25,7 @@
},
"components": [
{
"bom-ref":"redacted",
"bom-ref": "72567175418f73f8",
"type": "library",
"name": "package-1",
"version": "1.0.1",
@@ -66,7 +66,7 @@
]
},
{
"bom-ref":"redacted",
"bom-ref": "pkg:deb/debian/package-2@2.0.1?package-id=4b756c6f6fb127a3",
"type": "library",
"name": "package-2",
"version": "2.0.1",
@@ -100,7 +100,7 @@
]
},
{
"bom-ref":"redacted",
"bom-ref": "os:debian@1.2.3",
"type": "operating-system",
"name": "debian",
"version": "1.2.3",
+11 -3
View File
@@ -90,16 +90,24 @@ func TestCycloneDxImageEncoder(t *testing.T) {
func redactor(values ...string) testutil.Redactor {
return testutil.NewRedactions().
WithValuesRedacted(values...).
WithPatternRedactorSpec(
testutil.PatternReplacement{
// only the source component bom-ref (not package or other component bom-refs)
Search: regexp.MustCompile(`<component bom-ref="(?P<redact>[^"]*)" type="file">`),
Groups: []string{"redact"}, // use the regex to anchore the search, but only replace bytes within the capture group
Replace: "redacted",
},
).
WithPatternRedactors(
map[string]string{
// dates
`([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([+|\-]([01][0-9]|2[0-3]):[0-5][0-9]))`: `redacted`,
// image hashes and BOM refs
// image hashes
`sha256:[A-Za-z0-9]{64}`: `sha256:redacted`,
// serial numbers and BOM refs
`(serialNumber|bom-ref)="[^"]+"`: `$1="redacted"`,
// serial numbers
`(serialNumber)="[^"]+"`: `$1="redacted"`,
},
)
}
@@ -16,7 +16,7 @@
</component>
</metadata>
<components>
<component bom-ref="redacted" type="library">
<component bom-ref="4dd25c6ee16b729a" type="library">
<name>package-1</name>
<version>1.0.1</version>
<licenses>
@@ -34,7 +34,7 @@
<property name="syft:location:0:path">/some/path/pkg1</property>
</properties>
</component>
<component bom-ref="redacted" type="library">
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=39392bb5e270f669" type="library">
<name>package-2</name>
<version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
@@ -47,7 +47,7 @@
<property name="syft:metadata:installedSize">0</property>
</properties>
</component>
<component bom-ref="redacted" type="operating-system">
<component bom-ref="os:debian@1.2.3" type="operating-system">
<name>debian</name>
<version>1.2.3</version>
<description>debian</description>
@@ -11,13 +11,13 @@
</component>
</components>
</tools>
<component bom-ref="redacted" type="container">
<component bom-ref="f28a4ba3ddfdddad" type="container">
<name>user-image-input</name>
<version>sha256:redacted</version>
</component>
</metadata>
<components>
<component bom-ref="redacted" type="library">
<component bom-ref="72567175418f73f8" type="library">
<name>package-1</name>
<version>1.0.1</version>
<licenses>
@@ -36,7 +36,7 @@
<property name="syft:location:0:path">/somefile-1.txt</property>
</properties>
</component>
<component bom-ref="redacted" type="library">
<component bom-ref="pkg:deb/debian/package-2@2.0.1?package-id=4b756c6f6fb127a3" type="library">
<name>package-2</name>
<version>2.0.1</version>
<cpe>cpe:2.3:*:some:package:2:*:*:*:*:*:*:*</cpe>
@@ -50,7 +50,7 @@
<property name="syft:metadata:installedSize">0</property>
</properties>
</component>
<component bom-ref="redacted" type="operating-system">
<component bom-ref="os:debian@1.2.3" type="operating-system">
<name>debian</name>
<version>1.2.3</version>
<description>debian</description>
@@ -6,6 +6,7 @@ import (
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/packagemetadata"
"github.com/anchore/syft/syft/pkg"
@@ -84,12 +85,13 @@ func decodeComponent(c *cyclonedx.Component) *pkg.Package {
}
p := &pkg.Package{
Name: c.Name,
Version: c.Version,
Locations: decodeLocations(values),
Licenses: pkg.NewLicenseSet(decodeLicenses(c)...),
CPEs: decodeCPEs(c),
PURL: c.PackageURL,
Name: c.Name,
Version: c.Version,
Locations: decodeLocations(values),
Licenses: pkg.NewLicenseSet(decodeLicenses(c)...),
CPEs: decodeCPEs(c),
PURL: c.PackageURL,
SupplementalData: []any{artifact.ID(c.BOMRef)},
}
DecodeInto(p, values, "syft:package", CycloneDXFields)
@@ -70,7 +70,6 @@ func collectPackages(component *cyclonedx.Component, s *sbom.SBOM, idMap map[str
if syftID != "" {
idMap[syftID] = p
}
// TODO there must be a better way than needing to call this manually:
p.SetID()
s.Artifacts.Packages.Add(*p)
}
@@ -213,13 +213,15 @@ func reduceOuter(expression string) string {
func isBalanced(expression string) bool {
count := 0
for _, c := range expression {
if c == '(' {
switch c {
case '(':
count++
} else if c == ')' {
case ')':
count--
if count < 0 {
return false
}
default:
}
}
return count == 0
@@ -5,7 +5,6 @@ import (
"fmt"
"strings"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/spdxlicense"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
@@ -59,39 +58,14 @@ func joinLicenses(licenses []SPDXLicense) string {
}
type SPDXLicense struct {
ID string
Value string
ID string
Value string
FullText string
}
func ParseLicenses(raw []pkg.License) (concluded, declared []SPDXLicense) {
for _, l := range raw {
if l.Value == "" {
continue
}
candidate := SPDXLicense{}
if l.SPDXExpression != "" && !strings.HasPrefix(l.SPDXExpression, licenses.UnknownLicensePrefix) {
candidate.ID = l.SPDXExpression
} else {
candidate.Value = l.Value
// we did not find a valid SPDX license ID so treat as separate license
if strings.HasPrefix(l.SPDXExpression, licenses.UnknownLicensePrefix) {
candidate.ID = spdxlicense.LicenseRefPrefix + SanitizeElementID(l.SPDXExpression)
if len(l.Contents) > 0 {
candidate.Value = l.Contents
}
} else {
if len(l.Value) <= 64 {
// if the license text is less than the size of the hash,
// just use it directly so the id is more readable
candidate.ID = spdxlicense.LicenseRefPrefix + SanitizeElementID(l.Value)
} else {
hash := sha256.Sum256([]byte(l.Value))
candidate.ID = fmt.Sprintf("%s%x", spdxlicense.LicenseRefPrefix, hash)
}
}
}
candidate := createSPDXLicense(l)
switch l.Type {
case license.Concluded:
concluded = append(concluded, candidate)
@@ -102,3 +76,33 @@ func ParseLicenses(raw []pkg.License) (concluded, declared []SPDXLicense) {
return concluded, declared
}
func createSPDXLicense(l pkg.License) SPDXLicense {
candidate := SPDXLicense{
ID: generateLicenseID(l),
FullText: l.Contents,
}
if l.SPDXExpression == "" {
candidate.Value = l.Value
}
return candidate
}
func generateLicenseID(l pkg.License) string {
if l.SPDXExpression != "" {
return l.SPDXExpression
}
if l.Value != "" {
return spdxlicense.LicenseRefPrefix + SanitizeElementID(l.Value)
}
return licenseSum(l.Contents)
}
func licenseSum(s string) string {
if len(s) <= 64 {
return spdxlicense.LicenseRefPrefix + SanitizeElementID(s)
}
hash := sha256.Sum256([]byte(s))
return fmt.Sprintf("%s%x", spdxlicense.LicenseRefPrefix, hash)
}
@@ -105,6 +105,58 @@ func Test_License(t *testing.T) {
}
}
func TestGenerateLicenseID(t *testing.T) {
tests := []struct {
name string
license pkg.License
expected string
}{
{
name: "SPDX expression is preferred",
license: pkg.License{
SPDXExpression: "Apache-2.0",
Value: "SomeValue",
Contents: "Some text",
},
expected: "Apache-2.0",
},
{
name: "Uses value if no SPDX expression",
license: pkg.License{
Value: "MIT",
},
expected: spdxlicense.LicenseRefPrefix + "MIT",
},
{
name: "Long value is sanitized correctly",
license: pkg.License{
Value: "LGPLv2+ and LGPLv2+ with exceptions and GPLv2+ and GPLv2+ with exceptions and BSD and Inner-Net and ISC and Public Domain and GFDL",
},
expected: spdxlicense.LicenseRefPrefix +
"LGPLv2--and-LGPLv2--with-exceptions-and-GPLv2--and-GPLv2--with-exceptions-and-BSD-and-Inner-Net-and-ISC-and-Public-Domain-and-GFDL",
},
{
name: "Uses hash of contents when nothing else is provided",
license: pkg.License{
Contents: "This is a very long custom license text that should be hashed because it's more than 64 characters long.",
},
expected: "", // We'll verify it starts with the correct prefix
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
id := generateLicenseID(tt.license)
if tt.expected == "" {
assert.True(t, len(id) > len(spdxlicense.LicenseRefPrefix))
assert.Contains(t, id, spdxlicense.LicenseRefPrefix)
} else {
assert.Equal(t, tt.expected, id)
}
})
}
}
func Test_joinLicenses(t *testing.T) {
tests := []struct {
name string
@@ -34,7 +34,7 @@ const (
//
// Available options are: <omit>, NOASSERTION, Person: <person>, Organization: <org>
// return values are: <type>, <value>
func Originator(p pkg.Package) (typ string, author string) { //nolint: funlen
func Originator(p pkg.Package) (typ string, author string) { //nolint: gocyclo,funlen
if !hasMetadata(p) {
return typ, author
}
@@ -57,6 +57,15 @@ func Originator(p pkg.Package) (typ string, author string) { //nolint: funlen
case pkg.DpkgArchiveEntry:
author = metadata.Maintainer
case pkg.GitHubActionsUseStatement:
typ = orgType
org := strings.Split(metadata.Value, "/")[0]
if org == "actions" {
// this is a GitHub action, so the org is GitHub
org = "GitHub"
}
author = org
case pkg.JavaArchive:
if metadata.Manifest != nil {
author = metadata.Manifest.Main.MustGet("Specification-Vendor")
@@ -34,6 +34,7 @@ func Test_OriginatorSupplier(t *testing.T) {
pkg.NixStoreEntry{},
pkg.NpmPackageLockEntry{},
pkg.PhpComposerInstalledEntry{},
pkg.PhpPearEntry{},
pkg.PhpPeclEntry{},
pkg.PortageEntry{},
pkg.PythonPipfileLockEntry{},
@@ -45,6 +46,7 @@ func Test_OriginatorSupplier(t *testing.T) {
pkg.SwiplPackEntry{},
pkg.OpamPackage{},
pkg.YarnLockEntry{},
pkg.TerraformLockProviderEntry{},
)
tests := []struct {
name string
@@ -384,20 +386,24 @@ func Test_OriginatorSupplier(t *testing.T) {
supplier: "Person: me (me@auth.com)",
},
{
name: "from ocaml opam",
name: "from github actions workflow/action",
input: pkg.Package{
Metadata: pkg.OpamPackage{},
Metadata: pkg.GitHubActionsUseStatement{
Value: "actions/checkout@v4",
},
},
originator: "",
supplier: "",
originator: "Organization: GitHub",
supplier: "Organization: GitHub",
},
{
name: "from terraform lock",
name: "from github actions workflow/action",
input: pkg.Package{
Metadata: pkg.TerraformLockProviderEntry{},
Metadata: pkg.GitHubActionsUseStatement{
Value: "google/something@v6",
},
},
originator: "",
supplier: "",
originator: "Organization: google",
supplier: "Organization: google",
},
}
for _, test := range tests {
@@ -40,6 +40,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from rust cargo manifest"
case pkg.PhpComposerPkg:
answer = "acquired package info from PHP composer manifest"
case pkg.PhpPearPkg:
answer = "acquired package info from PHP Pear manifest"
case pkg.PhpPeclPkg:
answer = "acquired package info from PHP Pecl manifest"
case pkg.CocoapodsPkg:
@@ -143,6 +143,14 @@ func Test_SourceInfo(t *testing.T) {
"from PHP Pecl manifest",
},
},
{
input: pkg.Package{
Type: pkg.PhpPearPkg,
},
expected: []string{
"from PHP Pear manifest",
},
},
{
input: pkg.Package{
Type: pkg.DartPubPkg,
@@ -8,6 +8,7 @@ var expr = regexp.MustCompile("[^a-zA-Z0-9.-]")
// SPDX spec says SPDXID must be:
// "SPDXRef-"[idstring] where [idstring] is a unique string containing letters, numbers, ., and/or -
// https://spdx.github.io/spdx-spec/v2.3/snippet-information/
func SanitizeElementID(id string) string {
return expr.ReplaceAllString(id, "-")
}
+69 -1
View File
@@ -28,6 +28,7 @@ func (r RedactorFn) Redact(b []byte) []byte {
type PatternReplacement struct {
Search *regexp.Regexp
Groups []string
Replace string
}
@@ -39,7 +40,67 @@ func NewPatternReplacement(r *regexp.Regexp) PatternReplacement {
}
func (p PatternReplacement) Redact(b []byte) []byte {
return p.Search.ReplaceAll(b, []byte(p.Replace))
if len(p.Groups) == 0 {
return p.Search.ReplaceAll(b, []byte(p.Replace))
}
return p.redactNamedGroups(b)
}
func (p PatternReplacement) redactNamedGroups(b []byte) []byte {
groupsToReplace := make(map[string]bool)
for _, g := range p.Groups {
groupsToReplace[g] = true
}
subexpNames := p.Search.SubexpNames()
return p.Search.ReplaceAllFunc(b, func(match []byte) []byte {
indexes := p.Search.FindSubmatchIndex(match)
if indexes == nil {
return match
}
result := make([]byte, len(match))
copy(result, match)
// keep track of the offset as we replace groups
offset := 0
// process each named group
for i, name := range subexpNames {
// skip the full match (i==0) and groups we don't want to replace
if i == 0 || !groupsToReplace[name] {
continue
}
// get the start and end positions of this group
startPos := indexes[2*i]
endPos := indexes[2*i+1]
// skip if the group didn't match
if startPos < 0 || endPos < 0 {
continue
}
// adjust positions based on previous replacements
startPos += offset
endPos += offset
// replace the group with our replacement text
beforeGroup := result[:startPos]
afterGroup := result[endPos:]
// calculate the new offset
oldLen := endPos - startPos
newLen := len(p.Replace)
offset += (newLen - oldLen)
result = append(beforeGroup, append([]byte(p.Replace), afterGroup...)...) //nolint:gocritic
}
return result
})
}
// Replace by value //////////////////////////////
@@ -86,6 +147,13 @@ func (r *Redactions) WithPatternRedactors(values map[string]string) *Redactions
return r
}
func (r *Redactions) WithPatternRedactorSpec(values ...PatternReplacement) *Redactions {
for _, v := range values {
r.redactors = append(r.redactors, v)
}
return r
}
func (r *Redactions) WithValueRedactors(values map[string]string) *Redactions {
for k, v := range values {
r.redactors = append(r.redactors,
@@ -0,0 +1,130 @@
{
"artifacts": [
{
"id": "25f6913140cb5286",
"name": "package-1",
"version": "1.0.1",
"type": "python",
"foundBy": "the-cataloger-1",
"locations": [
{
"path": "/somefile-1.txt",
"layerID": "sha256:100d5a55f9032faead28b7427fa3e650e4f0158f86ea89d06e1489df00cb8c6f",
"accessPath": "/somefile-1.txt"
}
],
"licenses": [
{
"value": "MIT",
"fullText": "",
"spdxExpression": "MIT",
"type": "declared",
"urls": [],
"locations": []
}
],
"language": "python",
"cpes": [
{
"cpe": "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*",
"source": "syft-generated"
}
],
"purl": "a-purl-1",
"metadataType": "python-package",
"metadata": {
"name": "package-1",
"version": "1.0.1",
"author": "",
"authorEmail": "",
"platform": "",
"sitePackagesRootPath": ""
}
},
{
"id": "4b756c6f6fb127a3",
"name": "package-2",
"version": "2.0.1",
"type": "deb",
"foundBy": "the-cataloger-2",
"locations": [
{
"path": "/somefile-2.txt",
"layerID": "sha256:000fb9200890d3a19138478b20023023c0dce1c54352007c2863716780f049eb",
"accessPath": "/somefile-2.txt"
}
],
"licenses": [],
"language": "",
"cpes": [
{
"cpe": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*",
"source": "nvd-cpe-dictionary"
}
],
"purl": "pkg:deb/debian/package-2@2.0.1",
"metadataType": "dpkg-db-entry",
"metadata": {
"package": "package-2",
"source": "",
"version": "2.0.1",
"sourceVersion": "",
"architecture": "",
"maintainer": "",
"installedSize": 0,
"files": null
}
}
],
"artifactRelationships": [],
"source": {
"id": "34d40fdc6ca13e9a3fa18415db216b50bff047716fae7d95a225c09732fe83fb",
"name": "user-image-input",
"version": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"type": "image",
"metadata": {
"userInput": "user-image-input",
"imageID": "sha256:bf783ea304a3f02b5c7d2ece521800f5e2182e65ed5bb5116f578e17d6e82be4",
"manifestDigest": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"tags": [
"stereoscope-fixture-image-simple:85066c51088bdd274f7a89e99e00490f666c49e72ffc955707cd6e18f0e22c5b"
],
"imageSize": 38,
"layers": [
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:100d5a55f9032faead28b7427fa3e650e4f0158f86ea89d06e1489df00cb8c6f",
"size": 22
},
{
"mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip",
"digest": "sha256:000fb9200890d3a19138478b20023023c0dce1c54352007c2863716780f049eb",
"size": 16
}
],
"manifest": "eyJzY2hlbWFWZXJzaW9uIjoyLCJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmRpc3RyaWJ1dGlvbi5tYW5pZmVzdC52Mitqc29uIiwiY29uZmlnIjp7Im1lZGlhVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5kb2NrZXIuY29udGFpbmVyLmltYWdlLnYxK2pzb24iLCJzaXplIjo2NzIsImRpZ2VzdCI6InNoYTI1NjpiZjc4M2VhMzA0YTNmMDJiNWM3ZDJlY2U1MjE4MDBmNWUyMTgyZTY1ZWQ1YmI1MTE2ZjU3OGUxN2Q2ZTgyYmU0In0sImxheWVycyI6W3sibWVkaWFUeXBlIjoiYXBwbGljYXRpb24vdm5kLmRvY2tlci5pbWFnZS5yb290ZnMuZGlmZi50YXIuZ3ppcCIsInNpemUiOjIwNDgsImRpZ2VzdCI6InNoYTI1NjoxMDBkNWE1NWY5MDMyZmFlYWQyOGI3NDI3ZmEzZTY1MGU0ZjAxNThmODZlYTg5ZDA2ZTE0ODlkZjAwY2I4YzZmIn0seyJtZWRpYVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuZG9ja2VyLmltYWdlLnJvb3Rmcy5kaWZmLnRhci5nemlwIiwic2l6ZSI6MjA0OCwiZGlnZXN0Ijoic2hhMjU2OjAwMGZiOTIwMDg5MGQzYTE5MTM4NDc4YjIwMDIzMDIzYzBkY2UxYzU0MzUyMDA3YzI4NjM3MTY3ODBmMDQ5ZWIifV19",
"config": "eyJhcmNoaXRlY3R1cmUiOiJhcm02NCIsImNvbmZpZyI6eyJFbnYiOlsiUEFUSD0vdXNyL2xvY2FsL3NiaW46L3Vzci9sb2NhbC9iaW46L3Vzci9zYmluOi91c3IvYmluOi9zYmluOi9iaW4iXSwiV29ya2luZ0RpciI6Ii8iLCJPbkJ1aWxkIjpudWxsfSwiY3JlYXRlZCI6IjIwMjMtMDktMjhUMTI6MjM6MzUuNDAwNjcyODg1WiIsImhpc3RvcnkiOlt7ImNyZWF0ZWQiOiIyMDIzLTA5LTI4VDEyOjIzOjM1LjM5Mzk4NjUxWiIsImNyZWF0ZWRfYnkiOiJBREQgZmlsZS0xLnR4dCAvc29tZWZpbGUtMS50eHQgIyBidWlsZGtpdCIsImNvbW1lbnQiOiJidWlsZGtpdC5kb2NrZXJmaWxlLnYwIn0seyJjcmVhdGVkIjoiMjAyMy0wOS0yOFQxMjoyMzozNS40MDA2NzI4ODVaIiwiY3JlYXRlZF9ieSI6IkFERCBmaWxlLTIudHh0IC9zb21lZmlsZS0yLnR4dCAjIGJ1aWxka2l0IiwiY29tbWVudCI6ImJ1aWxka2l0LmRvY2tlcmZpbGUudjAifV0sIm9zIjoibGludXgiLCJyb290ZnMiOnsidHlwZSI6ImxheWVycyIsImRpZmZfaWRzIjpbInNoYTI1NjoxMDBkNWE1NWY5MDMyZmFlYWQyOGI3NDI3ZmEzZTY1MGU0ZjAxNThmODZlYTg5ZDA2ZTE0ODlkZjAwY2I4YzZmIiwic2hhMjU2OjAwMGZiOTIwMDg5MGQzYTE5MTM4NDc4YjIwMDIzMDIzYzBkY2UxYzU0MzUyMDA3YzI4NjM3MTY3ODBmMDQ5ZWIiXX19",
"repoDigests": [],
"architecture": "",
"os": ""
}
},
"distro": {
"prettyName": "debian",
"name": "debian",
"id": "debian",
"idLike": [
"like!"
],
"version": "1.2.3",
"versionID": "1.2.3"
},
"descriptor": {
"name": "syft",
"version": "v0.42.0-bogus",
"configuration": {
"config-key": "config-value"
}
}
}
@@ -1,106 +0,0 @@
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "user-image-input",
"documentNamespace":"redacted",
"creationInfo": {
"licenseListVersion":"redacted",
"creators": [
"Organization: Anchore, Inc",
"Tool: syft-v0.42.0-bogus"
],
"created":"redacted"
},
"packages": [
{
"name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt",
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "MIT",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "SECURITY",
"referenceType": "cpe23Type",
"referenceLocator": "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"
},
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "a-purl-1"
}
]
},
{
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt",
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "SECURITY",
"referenceType": "cpe23Type",
"referenceLocator": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"
},
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:deb/debian/package-2@2.0.1"
}
]
},
{
"name": "user-image-input",
"SPDXID": "SPDXRef-DocumentRoot-Image-user-image-input",
"versionInfo": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"checksums": [
{
"algorithm": "SHA256",
"checksumValue": "2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
}
],
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch="
}
],
"primaryPackagePurpose": "CONTAINER"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relatedSpdxElement": "SPDXRef-DocumentRoot-Image-user-image-input",
"relationshipType": "DESCRIBES"
}
]
}
@@ -1,246 +0,0 @@
{
"spdxVersion": "SPDX-2.3",
"dataLicense": "CC0-1.0",
"SPDXID": "SPDXRef-DOCUMENT",
"name": "user-image-input",
"documentNamespace":"redacted",
"creationInfo": {
"licenseListVersion":"redacted",
"creators": [
"Organization: Anchore, Inc",
"Tool: syft-v0.42.0-bogus"
],
"created":"redacted"
},
"packages": [
{
"name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from installed python package manifest file: /somefile-1.txt",
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "MIT",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "SECURITY",
"referenceType": "cpe23Type",
"referenceLocator": "cpe:2.3:*:some:package:1:*:*:*:*:*:*:*"
},
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "a-purl-1"
}
]
},
{
"name": "package-2",
"SPDXID": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
"versionInfo": "2.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"sourceInfo": "acquired package info from DPKG DB: /somefile-2.txt",
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"copyrightText": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "SECURITY",
"referenceType": "cpe23Type",
"referenceLocator": "cpe:2.3:*:some:package:2:*:*:*:*:*:*:*"
},
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:deb/debian/package-2@2.0.1"
}
]
},
{
"name": "user-image-input",
"SPDXID": "SPDXRef-DocumentRoot-Image-user-image-input",
"versionInfo": "sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
"filesAnalyzed": false,
"checksums": [
{
"algorithm": "SHA256",
"checksumValue": "2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368"
}
],
"licenseConcluded": "NOASSERTION",
"licenseDeclared": "NOASSERTION",
"externalRefs": [
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch="
}
],
"primaryPackagePurpose": "CONTAINER"
}
],
"files": [
{
"fileName": "/a1/f6",
"SPDXID": "SPDXRef-File-a1-f6-9c2f7510199b17f6",
"fileTypes": [
"OTHER"
],
"checksums": [
{
"algorithm": "SHA1",
"checksumValue": "0000000000000000000000000000000000000000"
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": "NOASSERTION"
},
{
"fileName": "/d1/f3",
"SPDXID": "SPDXRef-File-d1-f3-c6f5b29dca12661f",
"fileTypes": [
"OTHER"
],
"checksums": [
{
"algorithm": "SHA1",
"checksumValue": "0000000000000000000000000000000000000000"
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": "NOASSERTION"
},
{
"fileName": "/d2/f4",
"SPDXID": "SPDXRef-File-d2-f4-c641caa71518099f",
"fileTypes": [
"OTHER"
],
"checksums": [
{
"algorithm": "SHA1",
"checksumValue": "0000000000000000000000000000000000000000"
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": "NOASSERTION"
},
{
"fileName": "/f1",
"SPDXID": "SPDXRef-File-f1-5265a4dde3edbf7c",
"fileTypes": [
"OTHER"
],
"checksums": [
{
"algorithm": "SHA1",
"checksumValue": "0000000000000000000000000000000000000000"
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": "NOASSERTION"
},
{
"fileName": "/f2",
"SPDXID": "SPDXRef-File-f2-f9e49132a4b96ccd",
"fileTypes": [
"OTHER"
],
"checksums": [
{
"algorithm": "SHA1",
"checksumValue": "0000000000000000000000000000000000000000"
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": "NOASSERTION"
},
{
"fileName": "/z1/f5",
"SPDXID": "SPDXRef-File-z1-f5-839d99ee67d9d174",
"fileTypes": [
"OTHER"
],
"checksums": [
{
"algorithm": "SHA1",
"checksumValue": "0000000000000000000000000000000000000000"
}
],
"licenseConcluded": "NOASSERTION",
"licenseInfoInFiles": [
"NOASSERTION"
],
"copyrightText": "NOASSERTION"
}
],
"relationships": [
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relatedSpdxElement": "SPDXRef-File-f1-5265a4dde3edbf7c",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relatedSpdxElement": "SPDXRef-File-z1-f5-839d99ee67d9d174",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relatedSpdxElement": "SPDXRef-File-a1-f6-9c2f7510199b17f6",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relatedSpdxElement": "SPDXRef-File-d2-f4-c641caa71518099f",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relatedSpdxElement": "SPDXRef-File-d1-f3-c6f5b29dca12661f",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relatedSpdxElement": "SPDXRef-File-f2-f9e49132a4b96ccd",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-deb-package-2-4b756c6f6fb127a3",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DOCUMENT",
"relatedSpdxElement": "SPDXRef-DocumentRoot-Image-user-image-input",
"relationshipType": "DESCRIBES"
}
]
}
@@ -0,0 +1,60 @@
SPDXVersion: SPDX-2.3
DataLicense: CC0-1.0
SPDXID: SPDXRef-DOCUMENT
DocumentName: user-image-input
DocumentNamespace: redacted
LicenseListVersion: redacted
Creator: Organization: Anchore, Inc
Creator: Tool: syft-v0.42.0-bogus
Created: redacted
##### Package: user-image-input
PackageName: user-image-input
SPDXID: SPDXRef-DocumentRoot-Image-user-image-input
PackageVersion: sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
PrimaryPackagePurpose: CONTAINER
FilesAnalyzed: false
PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=
##### Package: package-2
PackageName: package-2
SPDXID: SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
PackageVersion: 2.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from DPKG DB: /somefile-2.txt
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageCopyrightText: NOASSERTION
ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:2:*:*:*:*:*:*:*
ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1
PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-25f6913140cb5286
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
FilesAnalyzed: false
PackageSourceInfo: acquired package info from installed python package manifest file: /somefile-1.txt
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: MIT
PackageCopyrightText: NOASSERTION
ExternalRef: SECURITY cpe23Type cpe:2.3:*:some:package:1:*:*:*:*:*:*:*
ExternalRef: PACKAGE-MANAGER purl a-purl-1
##### Relationships
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-25f6913140cb5286
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
@@ -15,7 +15,7 @@
"packages": [
{
"name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-5a2b1ae000fcb51e",
"SPDXID": "SPDXRef-Package-python-package-1-4dd25c6ee16b729a",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -76,7 +76,7 @@
"relationships": [
{
"spdxElementId": "SPDXRef-DocumentRoot-Directory-some-path",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-5a2b1ae000fcb51e",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-4dd25c6ee16b729a",
"relationshipType": "CONTAINS"
},
{
@@ -15,7 +15,7 @@
"packages": [
{
"name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"SPDXID": "SPDXRef-Package-python-package-1-72567175418f73f8",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -90,7 +90,7 @@
"relationships": [
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relationshipType": "CONTAINS"
},
{
@@ -15,7 +15,7 @@
"packages": [
{
"name": "package-1",
"SPDXID": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"SPDXID": "SPDXRef-Package-python-package-1-72567175418f73f8",
"versionInfo": "1.0.1",
"supplier": "NOASSERTION",
"downloadLocation": "NOASSERTION",
@@ -199,38 +199,38 @@
],
"relationships": [
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-f1-5265a4dde3edbf7c",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-z1-f5-839d99ee67d9d174",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-a1-f6-9c2f7510199b17f6",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-d2-f4-c641caa71518099f",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-d1-f3-c6f5b29dca12661f",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"spdxElementId": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relatedSpdxElement": "SPDXRef-File-f2-f9e49132a4b96ccd",
"relationshipType": "CONTAINS"
},
{
"spdxElementId": "SPDXRef-DocumentRoot-Image-user-image-input",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-c5cf7ac34cbca450",
"relatedSpdxElement": "SPDXRef-Package-python-package-1-72567175418f73f8",
"relationshipType": "CONTAINS"
},
{
@@ -91,7 +91,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1
PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-c5cf7ac34cbca450
SPDXID: SPDXRef-Package-python-package-1-72567175418f73f8
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -105,13 +105,13 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
##### Relationships
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
Relationship: SPDXRef-Package-python-package-1-c5cf7ac34cbca450 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-c5cf7ac34cbca450
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-f1-5265a4dde3edbf7c
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-z1-f5-839d99ee67d9d174
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-a1-f6-9c2f7510199b17f6
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-d2-f4-c641caa71518099f
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-d1-f3-c6f5b29dca12661f
Relationship: SPDXRef-Package-python-package-1-72567175418f73f8 CONTAINS SPDXRef-File-f2-f9e49132a4b96ccd
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-72567175418f73f8
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
@@ -38,7 +38,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1
PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-5a2b1ae000fcb51e
SPDXID: SPDXRef-Package-python-package-1-4dd25c6ee16b729a
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -52,7 +52,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-2
##### Relationships
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-5a2b1ae000fcb51e
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-python-package-1-4dd25c6ee16b729a
Relationship: SPDXRef-DocumentRoot-Directory-some-path CONTAINS SPDXRef-Package-deb-package-2-39392bb5e270f669
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Directory-some-path
@@ -41,7 +41,7 @@ ExternalRef: PACKAGE-MANAGER purl pkg:deb/debian/package-2@2.0.1
##### Package: package-1
PackageName: package-1
SPDXID: SPDXRef-Package-python-package-1-c5cf7ac34cbca450
SPDXID: SPDXRef-Package-python-package-1-72567175418f73f8
PackageVersion: 1.0.1
PackageSupplier: NOASSERTION
PackageDownloadLocation: NOASSERTION
@@ -55,7 +55,7 @@ ExternalRef: PACKAGE-MANAGER purl a-purl-1
##### Relationships
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-c5cf7ac34cbca450
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-python-package-1-72567175418f73f8
Relationship: SPDXRef-DocumentRoot-Image-user-image-input CONTAINS SPDXRef-Package-deb-package-2-4b756c6f6fb127a3
Relationship: SPDXRef-DOCUMENT DESCRIBES SPDXRef-DocumentRoot-Image-user-image-input
+1 -1
View File
@@ -6,7 +6,7 @@ import (
"io"
"strings"
"github.com/Masterminds/semver"
"github.com/Masterminds/semver/v3"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
+1
View File
@@ -47,6 +47,7 @@ type licenses []License
type License struct {
Value string `json:"value"`
FullText string `json:"fullText"`
SPDXExpression string `json:"spdxExpression"`
Type license.Type `json:"type"`
URLs []string `json:"urls"`
+2 -1
View File
@@ -58,7 +58,8 @@ func (s *Source) UnmarshalJSON(b []byte) error {
func unpackSrcMetadata(s *Source, unpacker sourceUnpacker) error {
rt := sourcemetadata.ReflectTypeFromJSONName(s.Type)
if rt == nil {
return fmt.Errorf("unable to find source metadata type=%q", s.Type)
// in cases where we are converting from an SBOM without any source information, we don't want this to be fatal
return nil
}
val := reflect.New(rt).Interface()
@@ -22,6 +22,15 @@ func TestSource_UnmarshalJSON(t *testing.T) {
expected *Source
wantErr require.ErrorAssertionFunc
}{
{
name: "empty",
input: []byte(`{
"id": "",
"type": "",
"metadata": null
}`),
expected: &Source{},
},
{
name: "directory",
input: []byte(`{
@@ -1,7 +1,7 @@
{
"artifacts": [
{
"id": "5a2b1ae000fcb51e",
"id": "4dd25c6ee16b729a",
"name": "package-1",
"version": "1.0.1",
"type": "python",
@@ -15,6 +15,7 @@
"licenses": [
{
"value": "MIT",
"fullText": "",
"spdxExpression": "MIT",
"type": "declared",
"urls": [],
@@ -1,7 +1,7 @@
{
"artifacts": [
{
"id": "ad3ecac55fe1c30f",
"id": "fba4ca04d4906f25",
"name": "package-1",
"version": "1.0.1",
"type": "python",
@@ -15,6 +15,7 @@
"licenses": [
{
"value": "MIT",
"fullText": "",
"spdxExpression": "MIT",
"type": "declared",
"urls": [],
@@ -1,7 +1,7 @@
{
"artifacts": [
{
"id": "c5cf7ac34cbca450",
"id": "72567175418f73f8",
"name": "package-1",
"version": "1.0.1",
"type": "python",
@@ -16,6 +16,7 @@
"licenses": [
{
"value": "MIT",
"fullText": "",
"spdxExpression": "MIT",
"type": "declared",
"urls": [],
+1 -1
View File
@@ -231,10 +231,10 @@ func toLicenseModel(pkgLicenses []pkg.License) (modelLicenses []model.License) {
modelLicenses = append(modelLicenses, model.License{
Value: l.Value,
SPDXExpression: l.SPDXExpression,
Contents: l.Contents,
Type: l.Type,
URLs: urls,
Locations: locations,
Contents: l.Contents,
})
}
return
+1 -1
View File
@@ -228,7 +228,7 @@ func toSyftRelationships(doc *model.Document, catalog *pkg.Collection, relations
idMap[string(p.ID())] = p
locations := p.Locations.ToSlice()
for _, l := range locations {
idMap[string(l.Coordinates.ID())] = l.Coordinates
idMap[string(l.ID())] = l.Coordinates
}
}
+5 -1
View File
@@ -169,7 +169,11 @@ func (r ChrootContext) ToNativeGlob(chrootPath string) (string, error) {
return "", err
}
parts[0] = strings.TrimRight(responsePath, "/") + "/"
hasSlash := strings.HasSuffix(parts[0], "/")
parts[0] = responsePath
if hasSlash {
parts[0] = strings.TrimRight(parts[0], "/") + "/"
}
return strings.Join(parts, "*"), nil
}
@@ -558,6 +558,16 @@ func TestToNativeGlob(t *testing.T) {
expectedResult: "/cwd/relative/path/**/file*.txt",
expectedError: nil,
},
{
name: "ending with glob",
chrootContext: ChrootContext{
root: "/root",
cwdRelativeToRoot: "/cwd",
},
chrootPath: "/var/lib/dpkg/info/name.*",
expectedResult: "/root/var/lib/dpkg/info/name.*",
expectedError: nil,
},
}
for _, tt := range tests {
@@ -16,8 +16,9 @@ var _ file.Resolver = (*ContainerImageAllLayers)(nil)
// ContainerImageAllLayers implements path and content access for the AllLayers source option for container image data sources.
type ContainerImageAllLayers struct {
img *image.Image
layers []int
img *image.Image
layers []int
markVisibility bool
}
// NewFromContainerImageAllLayers returns a new resolver from the perspective of all image layers for the given image.
@@ -33,6 +34,10 @@ func NewFromContainerImageAllLayers(img *image.Image) (*ContainerImageAllLayers,
return &ContainerImageAllLayers{
img: img,
layers: layers,
// This is the entrypoint for the user-facing implementation, which should always annotate locations.
// We have other resolvers that use this implementation that are already responsible
// for marking visibility, so we don't need to do it all of the time (a small performance optimization).
markVisibility: true,
}, nil
}
@@ -57,7 +62,7 @@ func (r *ContainerImageAllLayers) fileByRef(ref stereoscopeFile.Reference, uniqu
return nil, fmt.Errorf("unable to fetch metadata (ref=%+v): %w", ref, err)
}
if entry.Metadata.Type == stereoscopeFile.TypeHardLink || entry.Metadata.Type == stereoscopeFile.TypeSymLink {
if entry.Type == stereoscopeFile.TypeHardLink || entry.Type == stereoscopeFile.TypeSymLink {
// a link may resolve in this layer or higher, assuming a squashed tree is used to search
// we should search all possible resolutions within the valid source
for _, subLayerIdx := range r.layers[layerIdx:] {
@@ -102,7 +107,7 @@ func (r *ContainerImageAllLayers) FilesByPath(paths ...string) ([]file.Location,
if err != nil {
return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.RealPath, err)
}
if metadata.Metadata.IsDir() {
if metadata.IsDir() {
continue
}
}
@@ -112,7 +117,9 @@ func (r *ContainerImageAllLayers) FilesByPath(paths ...string) ([]file.Location,
return nil, err
}
for _, result := range results {
uniqueLocations = append(uniqueLocations, file.NewLocationFromImage(path, result, r.img))
l := file.NewLocationFromImage(path, result, r.img)
r.annotateLocation(&l)
uniqueLocations = append(uniqueLocations, l)
}
}
}
@@ -146,7 +153,7 @@ func (r *ContainerImageAllLayers) FilesByGlob(patterns ...string) ([]file.Locati
return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", result.RequestPath, err)
}
// don't consider directories
if metadata.Metadata.IsDir() {
if metadata.IsDir() {
continue
}
}
@@ -156,7 +163,9 @@ func (r *ContainerImageAllLayers) FilesByGlob(patterns ...string) ([]file.Locati
return nil, err
}
for _, refResult := range refResults {
uniqueLocations = append(uniqueLocations, file.NewLocationFromImage(string(result.RequestPath), refResult, r.img))
l := file.NewLocationFromImage(string(result.RequestPath), refResult, r.img)
r.annotateLocation(&l)
uniqueLocations = append(uniqueLocations, l)
}
}
}
@@ -172,7 +181,7 @@ func (r *ContainerImageAllLayers) RelativeFileByPath(location file.Location, pat
exists, relativeRef, err := layer.SquashedTree.File(stereoscopeFile.Path(path), filetree.FollowBasenameLinks)
if err != nil {
log.Errorf("failed to find path=%q in squash: %+w", path, err)
log.Errorf("failed to find path=%q in squash: %+v", path, err)
return nil
}
if !exists && !relativeRef.HasReference() {
@@ -180,6 +189,7 @@ func (r *ContainerImageAllLayers) RelativeFileByPath(location file.Location, pat
}
relativeLocation := file.NewLocationFromImage(path, *relativeRef.Reference, r.img)
r.annotateLocation(&relativeLocation)
return &relativeLocation
}
@@ -192,7 +202,7 @@ func (r *ContainerImageAllLayers) FileContentsByLocation(location file.Location)
return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err)
}
switch entry.Metadata.Type {
switch entry.Type {
case stereoscopeFile.TypeSymLink, stereoscopeFile.TypeHardLink:
// the location we are searching may be a symlink, we should always work with the resolved file
newLocation := r.RelativeFileByPath(location, location.AccessPath)
@@ -228,7 +238,9 @@ func (r *ContainerImageAllLayers) FilesByMIMEType(types ...string) ([]file.Locat
return nil, err
}
for _, refResult := range refResults {
uniqueLocations = append(uniqueLocations, file.NewLocationFromImage(string(ref.RequestPath), refResult, r.img))
l := file.NewLocationFromImage(string(ref.RequestPath), refResult, r.img)
r.annotateLocation(&l)
uniqueLocations = append(uniqueLocations, l)
}
}
}
@@ -243,10 +255,12 @@ func (r *ContainerImageAllLayers) AllLocations(ctx context.Context) <-chan file.
for _, layerIdx := range r.layers {
tree := r.img.Layers[layerIdx].Tree
for _, ref := range tree.AllFiles(stereoscopeFile.AllTypes()...) {
l := file.NewLocationFromImage(string(ref.RealPath), ref, r.img)
r.annotateLocation(&l)
select {
case <-ctx.Done():
return
case results <- file.NewLocationFromImage(string(ref.RealPath), ref, r.img):
case results <- l:
continue
}
}
@@ -258,3 +272,36 @@ func (r *ContainerImageAllLayers) AllLocations(ctx context.Context) <-chan file.
func (r *ContainerImageAllLayers) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
return fileMetadataByLocation(r.img, location)
}
func (r *ContainerImageAllLayers) annotateLocation(l *file.Location) {
if !r.markVisibility || l == nil {
return
}
givenRef := l.Reference()
annotation := file.VisibleAnnotation
// if we find a location for a path that matches the query (e.g. **/node_modules) but is not present in the squashed tree, skip it
ref, err := r.img.SquashedSearchContext.SearchByPath(l.RealPath, filetree.DoNotFollowDeadBasenameLinks)
if err != nil || !ref.HasReference() {
annotation = file.HiddenAnnotation
} else if ref.ID() != givenRef.ID() {
// we may have the path in the squashed tree, but this must not be in the same layer
annotation = file.HiddenAnnotation
}
// not only should the real path to the file exist, but the way we took to get there should also exist
// (e.g. if we are looking for /etc/passwd, but the real path is /etc/passwd -> /etc/passwd-1, then we should
// make certain that /etc/passwd-1 exists)
if annotation == file.VisibleAnnotation && l.AccessPath != "" {
ref, err := r.img.SquashedSearchContext.SearchByPath(l.AccessPath, filetree.DoNotFollowDeadBasenameLinks)
if err != nil || !ref.HasReference() {
annotation = file.HiddenAnnotation
} else if ref.ID() != givenRef.ID() {
// we may have the path in the squashed tree, but this must not be in the same layer
annotation = file.HiddenAnnotation
}
}
l.Annotations[file.VisibleAnnotationKey] = annotation
}
@@ -522,16 +522,26 @@ func TestAllLayersResolver_AllLocations(t *testing.T) {
paths := strset.New()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
visibleSet := strset.New()
hiddenSet := strset.New()
for loc := range resolver.AllLocations(ctx) {
paths.Add(loc.RealPath)
switch loc.Annotations[file.VisibleAnnotationKey] {
case file.VisibleAnnotation:
visibleSet.Add(loc.RealPath)
case file.HiddenAnnotation:
hiddenSet.Add(loc.RealPath)
case "":
t.Errorf("expected visibility annotation for location: %+v", loc)
}
}
expected := []string{
visible := []string{
"/Dockerfile",
"/file-1.txt",
"/file-3.txt",
"/file-3.txt", // this is a deadlink pointing to /file-1.txt (which has been deleted)
"/target",
"/target/file-2.txt",
}
hidden := []string{
"/.wh.bin",
"/.wh.file-1.txt",
"/.wh.lib",
@@ -619,6 +629,7 @@ func TestAllLayersResolver_AllLocations(t *testing.T) {
"/bin/usleep",
"/bin/watch",
"/bin/zcat",
"/file-1.txt",
"/lib",
"/lib/apk",
"/lib/apk/db",
@@ -641,15 +652,41 @@ func TestAllLayersResolver_AllLocations(t *testing.T) {
"/lib/sysctl.d/00-alpine.conf",
}
// depending on how the image is built (either from linux or mac), sys and proc might accidentally be added to the image.
// this isn't important for the test, so we remove them.
paths.Remove("/proc", "/sys", "/dev", "/etc")
var expected []string
expected = append(expected, visible...)
expected = append(expected, hidden...)
sort.Strings(expected)
// Remove cache created by Mac Rosetta when emulating different arches
paths.Remove("/.cache/rosetta", "/.cache")
cleanPaths := func(s *strset.Set) {
// depending on how the image is built (either from linux or mac), sys and proc might accidentally be added to the image.
// this isn't important for the test, so we remove them.
s.Remove("/proc", "/sys", "/dev", "/etc")
// Remove cache created by Mac Rosetta when emulating different arches
s.Remove("/.cache/rosetta", "/.cache")
}
cleanPaths(paths)
cleanPaths(visibleSet)
cleanPaths(hiddenSet)
pathsList := paths.List()
sort.Strings(pathsList)
visibleSetList := visibleSet.List()
sort.Strings(visibleSetList)
hiddenSetList := hiddenSet.List()
sort.Strings(hiddenSetList)
if d := cmp.Diff(expected, pathsList); d != "" {
t.Errorf("unexpected paths (-want +got):\n%s", d)
}
if d := cmp.Diff(visible, visibleSetList); d != "" {
t.Errorf("unexpected visible paths (-want +got):\n%s", d)
}
if d := cmp.Diff(hidden, hiddenSetList); d != "" {
t.Errorf("unexpected hidden paths (-want +got):\n%s", d)
}
assert.ElementsMatchf(t, expected, pathsList, "expected all paths to be indexed, but found different paths: \n%s", cmp.Diff(expected, paths.List()))
}
@@ -0,0 +1,255 @@
package fileresolver
import (
"context"
"io"
"github.com/anchore/stereoscope/pkg/image"
"github.com/anchore/syft/syft/file"
)
var _ file.Resolver = (*ContainerImageDeepSquash)(nil)
// ContainerImageDeepSquash implements path and content access for the paths in the squashed tree, but with additional
// depth from all layers. The goal of this is to allow for producing results where the first layer which the material
// was added can be annotated in the SBOM (as opposed to the last [visible] layer for the path like with the squashed
// file resolver).
type ContainerImageDeepSquash struct {
squashed file.Resolver
allLayers file.Resolver
}
// NewFromContainerImageDeepSquash returns a new resolver from the perspective of all image layers for the given image.
func NewFromContainerImageDeepSquash(img *image.Image) (*ContainerImageDeepSquash, error) {
squashed, err := NewFromContainerImageSquash(img)
if err != nil {
return nil, err
}
allLayers, err := NewFromContainerImageAllLayers(img)
if err != nil {
return nil, err
}
// we will do the work here to mark visibility with results from two resolvers (don't do the work twice!)
allLayers.markVisibility = false
return &ContainerImageDeepSquash{
squashed: squashed,
allLayers: allLayers,
}, nil
}
// HasPath indicates if the given path exists in the underlying source.
func (i *ContainerImageDeepSquash) HasPath(path string) bool {
// there is no need to merge results from all layers since path-based results should always be adjusted relative to the squashed tree (which is different when considering layers)
return i.squashed.HasPath(path)
}
// FilesByPath returns all file.References that match the given paths from any layer in the image.
func (i *ContainerImageDeepSquash) FilesByPath(paths ...string) ([]file.Location, error) {
squashedLocations, err := i.squashed.FilesByPath(paths...)
if err != nil {
return nil, err
}
if len(squashedLocations) == 0 {
// this is meant to return all files in all layers only for paths that are present in the squashed tree. If
// there are no results from the squashed tree then there are no paths to raise up.
return nil, nil
}
allLayersLocations, err := i.allLayers.FilesByPath(paths...)
if err != nil {
return nil, err
}
return i.mergeLocations(squashedLocations, allLayersLocations), nil
}
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
func (i *ContainerImageDeepSquash) FilesByGlob(patterns ...string) ([]file.Location, error) {
squashedLocations, err := i.squashed.FilesByGlob(patterns...)
if err != nil {
return nil, err
}
if len(squashedLocations) == 0 {
// this is meant to return all files in all layers only for paths that are present in the squashed tree. If
// there are no results from the squashed tree then there are no paths to raise up.
return nil, nil
}
allLayersLocations, err := i.allLayers.FilesByGlob(patterns...)
if err != nil {
return nil, err
}
return i.mergeLocations(squashedLocations, allLayersLocations), nil
}
// RelativeFileByPath fetches a single file at the given path relative to the layer squash of the given reference.
// This is helpful when attempting to find a file that is in the same layer or lower as another file.
func (i *ContainerImageDeepSquash) RelativeFileByPath(location file.Location, path string) *file.Location {
if !i.squashed.HasPath(path) {
return nil
}
l := i.squashed.RelativeFileByPath(location, path)
if l != nil {
loc := l.WithAnnotation(file.VisibleAnnotationKey, file.VisibleAnnotation)
return &loc
}
l = i.allLayers.RelativeFileByPath(location, path)
if l != nil {
loc := l.WithAnnotation(file.VisibleAnnotationKey, file.HiddenAnnotation)
return &loc
}
return nil
}
// FileContentsByLocation fetches file contents for a single file reference.
// If the path does not exist an error is returned.
func (i *ContainerImageDeepSquash) FileContentsByLocation(location file.Location) (io.ReadCloser, error) {
// regardless of the layer or scope, if the user gives us a specific path+layer location, then we should always
// return the contents for that specific location (thus all-layers scope must always be used)
return i.allLayers.FileContentsByLocation(location)
}
func (i *ContainerImageDeepSquash) FilesByMIMEType(types ...string) ([]file.Location, error) {
squashedLocations, err := i.squashed.FilesByMIMEType(types...)
if err != nil {
return nil, err
}
if len(squashedLocations) == 0 {
// this is meant to return all files in all layers only for paths that are present in the squashed tree. If
// there are no results from the squashed tree then there are no paths to raise up.
return nil, nil
}
allLayersLocations, err := i.allLayers.FilesByMIMEType(types...)
if err != nil {
return nil, err
}
return i.mergeLocations(squashedLocations, allLayersLocations), nil
}
func (i *ContainerImageDeepSquash) AllLocations(ctx context.Context) <-chan file.Location {
return i.mergeLocationStreams(ctx, i.squashed.AllLocations(ctx), i.allLayers.AllLocations(ctx))
}
func (i *ContainerImageDeepSquash) FileMetadataByLocation(location file.Location) (file.Metadata, error) {
// regardless of the layer or scope, if the user gives us a specific path+layer location, then we should always
// return the metadata for that specific location (thus all-layers scope must always be used)
return i.allLayers.FileMetadataByLocation(location)
}
func (i *ContainerImageDeepSquash) mergeLocations(squashedLocations, allLayersLocations []file.Location) []file.Location {
var result []file.Location
if len(squashedLocations) == 0 {
// this is meant to return all files in all layers only for paths that are present in the squashed tree. If
// there are no results from the squashed tree then there are no paths to raise up.
return nil
}
// we are using a location set to deduplicate locations, but we don't use it for the returned
// results in order to preserve the order of the locations from the underlying filetree query
squashedCoords := file.NewLocationSet()
for _, l := range squashedLocations {
result = append(result, l.WithAnnotation(file.VisibleAnnotationKey, file.VisibleAnnotation))
squashedCoords.Add(l)
}
for _, l := range allLayersLocations {
if squashedCoords.Contains(l) {
// this path + layer is already in the squashed tree results, skip it (deduplicate location results)
continue
}
if !i.squashed.HasPath(l.RealPath) {
// if we find a location for a path that matches the query (e.g. **/node_modules) but is not present in the squashed tree, skip it
continue
}
// not only should the real path to the file exist, but the way we took to get there should also exist
// (e.g. if we are looking for /etc/passwd, but the real path is /etc/passwd -> /etc/passwd-1, then we should
// make certain that /etc/passwd-1 exists)
if l.AccessPath != "" && !i.squashed.HasPath(l.AccessPath) {
continue
}
result = append(result, l.WithAnnotation(file.VisibleAnnotationKey, file.HiddenAnnotation))
}
return result
}
func (i *ContainerImageDeepSquash) mergeLocationStreams(ctx context.Context, squashedLocations, allLayersLocations <-chan file.Location) <-chan file.Location {
result := make(chan file.Location)
go func() {
defer close(result)
// we are using a location set to deduplicate locations, but we don't use it for the returned
// results in order to preserve the order of the locations from the underlying filetree query
squashedCoords := file.NewLocationSet()
var isDone bool
for l := range squashedLocations {
if isDone {
// bleed off the rest of the results from the squashed stream and not leak a goroutine
continue
}
select {
case <-ctx.Done():
isDone = true
default:
result <- l.WithAnnotation(file.VisibleAnnotationKey, file.VisibleAnnotation)
squashedCoords.Add(l)
}
}
for l := range allLayersLocations {
if isDone {
// bleed off the rest of the results from the squashed stream and not leak a goroutine
continue
}
if squashedCoords.Empty() {
// this is meant to return all files in all layers only for paths that are present in the squashed tree.
// If there are no results from the squashed tree, then there are no paths to raise up.
// That being said, we need to bleed off the rest of the results from the allLayersLocations stream
// and not leak a goroutine.
continue
}
if squashedCoords.Contains(l) {
// we've already seen this location from the squashed stream, skip it
continue
}
if !i.squashed.HasPath(l.RealPath) {
// if we find a location for a path that matches the query (e.g. **/node_modules) but is not present in the squashed tree, skip it
continue
}
// not only should the real path to the file exist, but the way we took to get there should also exist
// (e.g. if we are looking for /etc/passwd, but the real path is /etc/passwd -> /etc/passwd-1, then we should
// make certain that /etc/passwd-1 exists)
if l.AccessPath != "" && !i.squashed.HasPath(l.AccessPath) {
continue
}
select {
case <-ctx.Done():
isDone = true
default:
result <- l.WithAnnotation(file.VisibleAnnotationKey, file.HiddenAnnotation)
}
}
}()
return result
}
File diff suppressed because it is too large Load Diff
@@ -58,7 +58,7 @@ func (r *ContainerImageSquash) FilesByPath(paths ...string) ([]file.Location, er
return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", ref.RealPath, err)
}
// don't consider directories
if metadata.Metadata.IsDir() {
if metadata.IsDir() {
continue
}
}
@@ -106,7 +106,7 @@ func (r *ContainerImageSquash) FilesByGlob(patterns ...string) ([]file.Location,
return nil, fmt.Errorf("unable to get file metadata for path=%q: %w", result.RequestPath, err)
}
// don't consider directories
if metadata.Metadata.IsDir() {
if metadata.IsDir() {
continue
}
}
@@ -151,7 +151,7 @@ func (r *ContainerImageSquash) FileContentsByLocation(location file.Location) (i
return nil, fmt.Errorf("unable to get metadata for path=%q from file catalog: %w", location.RealPath, err)
}
switch entry.Metadata.Type {
switch entry.Type {
case stereoscopeFile.TypeSymLink, stereoscopeFile.TypeHardLink:
// the location we are searching may be a symlink, we should always work with the resolved file
locations, err := r.FilesByPath(location.RealPath)
+1 -1
View File
@@ -59,7 +59,7 @@ func (r *Directory) buildIndex() error {
r.tree = tree
r.index = index
r.filetreeResolver.searchContext = filetree.NewSearchContext(tree, index)
r.searchContext = filetree.NewSearchContext(tree, index)
return nil
}

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