Compare commits

...
Author SHA1 Message Date
Keith Zantow 46522bcc5d chore: update packageurl-go (#3678)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2025-02-21 15:31:16 -05:00
RogueAIandKeith Zantow aeea170b19 fix: disable cert validation in dotnet-portable-executable-cataloger by default (#3677)
Signed-off-by: rogueai <rogueai@users.noreply.github.com>
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Co-authored-by: Keith Zantow <kzantow@gmail.com>
2025-02-21 15:08:06 -05:00
William Murphy dd2ee2bbf7 fix: find bitnami files even when no relationships (#3676)
The bitnami cataloger assigns files under /opt/bitnami/PACKAGE to be
owned by PACKAGE unless they are otherwise owned. Previously, this main
package was identified only by relationships, leading to an edge case
where if there was a bitnami SBOM with a single package in it, there
were no relationships, and so there would be no main package to assign
the files to, leading to deduplication failures.

Instead, when encountering a bitnami SBOM with exactly one package in
it, assume that package is the main package of that SBOM.

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2025-02-21 14:22:37 +00:00
anchore-actions-token-generator[bot]andspiffcs edcfbe2f0d chore(deps): update tools to latest versions (#3652)
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-02-20 14:59:23 -05:00
anchore-actions-token-generator[bot]andwagoodman aff025ba35 chore(deps): update CPE dictionary index (#3666)
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-02-20 14:59:10 -05:00
dependabot[bot] 97a99e1132 chore(deps): bump actions/cache from 4.2.0 to 4.2.1 (#3670)
Bumps [actions/cache](https://github.com/actions/cache) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/1bd1e32a3bdc45362d1e726936510720a7c30a57...0c907a75c2c80ebcb7f088228285e798b750cf8f)

---
updated-dependencies:
- dependency-name: actions/cache
  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-02-20 10:45:27 -05:00
dependabot[bot] edc361c0c7 chore(deps): bump actions/cache in /.github/actions/bootstrap (#3671)
Bumps [actions/cache](https://github.com/actions/cache) from 4.2.0 to 4.2.1.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/1bd1e32a3bdc45362d1e726936510720a7c30a57...0c907a75c2c80ebcb7f088228285e798b750cf8f)

---
updated-dependencies:
- dependency-name: actions/cache
  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-02-20 10:44:58 -05:00
dependabot[bot] 2317c5acfc chore(deps): bump github.com/docker/docker (#3673)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 27.5.1+incompatible to 28.0.0+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v27.5.1...v28.0.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-20 10:41:49 -05:00
idhyt 52bd4acd23 fix: correctly trim conanfile line breaks (#3672)
Signed-off-by: idhyt <idhyt3r@gmail.com>
2025-02-20 09:18:39 -05:00
dependabot[bot] 59b84f3ffd chore(deps): bump github.com/spf13/cobra from 1.8.1 to 1.9.1 (#3667)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.8.1 to 1.9.1.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.8.1...v1.9.1)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  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-02-18 13:48:18 -05:00
dependabot[bot] fb7444cb02 chore(deps): bump github.com/sanity-io/litter from 1.5.6 to 1.5.8 (#3668)
Bumps [github.com/sanity-io/litter](https://github.com/sanity-io/litter) from 1.5.6 to 1.5.8.
- [Changelog](https://github.com/sanity-io/litter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sanity-io/litter/compare/v1.5.6...v1.5.8)

---
updated-dependencies:
- dependency-name: github.com/sanity-io/litter
  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-02-18 13:48:08 -05:00
dependabot[bot] e8a4667db2 chore(deps): bump github.com/gkampitakis/go-snaps from 0.5.10 to 0.5.11 (#3669)
Bumps [github.com/gkampitakis/go-snaps](https://github.com/gkampitakis/go-snaps) from 0.5.10 to 0.5.11.
- [Release notes](https://github.com/gkampitakis/go-snaps/releases)
- [Commits](https://github.com/gkampitakis/go-snaps/compare/v0.5.10...v0.5.11)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-18 13:47:54 -05:00
bffe26bcc5 feat: add support for Bitnami cataloguer (#3341)
* prototype: start bitnami cataloger

Bitnami images have spdx SBOMs at predictable paths, and Syft could more
accurately identify the software in these images by scanning those
SBOMs. Start work on this by forking the sbom-cataloger as a new
bitnami-cataloger.

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* wire up bitnami cataloger to run on images by default

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>

* feat: add support for Bitnami cataloguer

Signed-off-by: juan131 <jariza@vmware.com>

* feat: use a better SPDX sample for unit tests

Signed-off-by: juan131 <jariza@vmware.com>

* bugfix: only report bitnami pkgs

Signed-off-by: juan131 <jariza@vmware.com>

* feat: adapt JSON schema, spdxutil and packagemetadata

Signed-off-by: juan131 <jariza@vmware.com>

* bugfix: integration tests

Signed-off-by: juan131 <jariza@vmware.com>

* feat: implement FileOwner interface

Signed-off-by: juan131 <jariza@vmware.com>

* bugfix: update json schema

Signed-off-by: juan131 <jariza@vmware.com>

* [wip] add bitnami owned files and fix binary package ownership filtering

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

* feat: obtain bitnami pkg files based on SPDX relationships tree

Signed-off-by: juan131 <jariza@vmware.com>

* preserve type switches

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

* rename bitnami entry metadata type

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

* restrict find main pkg logic

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

* add missing graalvm source info

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

* bugfix: integration tests

Signed-off-by: juan131 <jariza@vmware.com>

* bugfix: mod tidy

Signed-off-by: juan131 <jariza@vmware.com>

---------

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
Signed-off-by: juan131 <jariza@vmware.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Will Murphy <willmurphyscode@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-02-18 09:07:47 -05:00
dependabot[bot] 869908ece1 chore(deps): bump modernc.org/sqlite from 1.34.5 to 1.35.0 (#3664)
Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.34.5 to 1.35.0.
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.34.5...v1.35.0)

---
updated-dependencies:
- dependency-name: modernc.org/sqlite
  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-02-14 08:40:30 -05:00
dependabot[bot] 91b7592a2f chore(deps): bump github.com/jedib0t/go-pretty/v6 from 6.6.5 to 6.6.6 (#3653)
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.6.5 to 6.6.6.
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.6.5...v6.6.6)

---
updated-dependencies:
- dependency-name: github.com/jedib0t/go-pretty/v6
  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-02-13 10:08:20 -05:00
Weston Steimel dce99543b2 feat: update to go 1.24.x (#3660)
* feat: update to go 1.24.x

Update to building with go 1.24.x so that the main module version gets
set during `go build`

Signed-off-by: Weston Steimel <author@code.w.steimel.me.uk>

* chore: bump golangci-lint for go 1.24.x support

Signed-off-by: Weston Steimel <author@code.w.steimel.me.uk>

* chore: appease the updated linter

Signed-off-by: Weston Steimel <author@code.w.steimel.me.uk>

* chore: fix test logging for go 1.24

Signed-off-by: Weston Steimel <author@code.w.steimel.me.uk>

---------

Signed-off-by: Weston Steimel <author@code.w.steimel.me.uk>
2025-02-13 10:08:10 -05:00
Keith Zantow a17fe480a0 chore: update runners to ubuntu-24.04 (#3657)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2025-02-11 21:34:12 +00:00
dependabot[bot] d6fe2b08cb chore(deps): bump golang.org/x/net from 0.34.0 to 0.35.0 (#3655)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.34.0 to 0.35.0.
- [Commits](https://github.com/golang/net/compare/v0.34.0...v0.35.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-11 13:15:50 -05:00
dependabot[bot] 51780fba16 chore(deps): bump github.com/gkampitakis/go-snaps from 0.5.9 to 0.5.10 (#3650)
Bumps [github.com/gkampitakis/go-snaps](https://github.com/gkampitakis/go-snaps) from 0.5.9 to 0.5.10.
- [Release notes](https://github.com/gkampitakis/go-snaps/releases)
- [Commits](https://github.com/gkampitakis/go-snaps/compare/v0.5.9...v0.5.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-10 10:19:22 -05:00
anchore-actions-token-generator[bot]andwagoodman 46f9bf4e4d chore(deps): update CPE dictionary index (#3649)
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-02-09 22:45:31 -05:00
Keith Zantow 2328b20082 fix: reduce warn levels to debug for non-actionable errors (#3645)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2025-02-07 13:22:55 -05:00
dependabot[bot] 52d543f3c1 chore(deps): bump github/codeql-action from 3.28.8 to 3.28.9 (#3648)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.8 to 3.28.9.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/dd746615b3b9d728a6a37ca2045b68ca76d4841a...9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-07 11:18:56 -05:00
Christopher Angelo Phillips e584c9f416 feat: 3626 add option enable license content; disable by default (#3631)
---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-02-05 20:41:03 +00:00
anchore-actions-token-generator[bot]andspiffcs 7bab6e9851 chore(deps): update tools to latest versions (#3641)
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-02-05 14:23:36 -05:00
dependabot[bot] 10ba5aa9e6 chore(deps): bump sigstore/cosign-installer from 3.7.0 to 3.8.0 (#3642)
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.7.0 to 3.8.0.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/v3.7.0...v3.8.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-05 14:22:21 -05:00
dependabot[bot] 79ea956f18 chore(deps): bump golang.org/x/mod from 0.22.0 to 0.23.0 (#3644)
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.22.0 to 0.23.0.
- [Commits](https://github.com/golang/mod/compare/v0.22.0...v0.23.0)

---
updated-dependencies:
- dependency-name: golang.org/x/mod
  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-02-05 14:22:02 -05:00
Alex Goodman 684b6e3f98 Add file catalogers to selection configuration (#3505)
* add file catalogers to selection configuration

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

* fix typos

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

* warn when there is conflicting file cataloging configuration

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

* allow for explicit removal of all package and file tasks

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

* address PR feedback

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-02-03 19:10:17 +00:00
Christopher Angelo Phillips 5e2ba43328 chore: replace all shorthand tags of mapstruct -> mapstructure (#3633)
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-02-03 10:43:06 -05:00
anchore-actions-token-generator[bot]andspiffcs 4dc86a06c7 chore(deps): update tools to latest versions (#3637)
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-02-03 09:44:56 -05:00
anchore-actions-token-generator[bot]andwagoodman 7a69f6fdd7 chore(deps): update CPE dictionary index (#3638)
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-02-03 09:44:44 -05:00
Christopher Angelo Phillips 9a9195e5c4 feat: syft 3435 - add file components to cyclonedx bom output when file metadata is available (#3539)
---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-01-31 15:09:15 -05:00
anchore-actions-token-generator[bot]andspiffcs a16e374a50 chore(deps): update tools to latest versions (#3635)
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-01-31 11:14:59 -05:00
dependabot[bot] bdf680426d chore(deps): bump github/codeql-action from 3.28.7 to 3.28.8 (#3634)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.7 to 3.28.8.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/6e5455904168f98c75d8e5ad848b4dc4ab3ae77e...dd746615b3b9d728a6a37ca2045b68ca76d4841a)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-30 11:07:01 -05:00
Christopher Angelo Phillips d5e52bc2ce docs: update descriptions with correct options (#3630)
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-01-29 15:47:04 +00:00
dependabot[bot] b89304d9b5 chore(deps): bump github.com/gkampitakis/go-snaps from 0.5.8 to 0.5.9 (#3627)
Bumps [github.com/gkampitakis/go-snaps](https://github.com/gkampitakis/go-snaps) from 0.5.8 to 0.5.9.
- [Release notes](https://github.com/gkampitakis/go-snaps/releases)
- [Commits](https://github.com/gkampitakis/go-snaps/compare/v0.5.8...v0.5.9)

---
updated-dependencies:
- dependency-name: github.com/gkampitakis/go-snaps
  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-01-29 09:58:51 -05:00
dependabot[bot] 3fc0e0485e chore(deps): bump github/codeql-action from 3.28.6 to 3.28.7 (#3628)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.6 to 3.28.7.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/17a820bf2e43b47be2c72b39cc905417bc1ab6d0...6e5455904168f98c75d8e5ad848b4dc4ab3ae77e)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-29 09:58:32 -05:00
HeyeOpenSourceandChristopher Phillips f7e767fc25 feat: update licenses to including license content when SPDX expressions are unable to be determined (#3366)
---------
Signed-off-by: HeyeOpenSource <opensource@heye-international.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-01-28 18:05:27 +00:00
Martin Prpič 58dc43de86 fix: update namespace value for OpenSUSE distros (#3615)
Instead of namespacing them to the specific distro version, such as
Leap or Tumbleweed, the namespace value is set to the vendor itself:
"opensuse".

Resolves #3534

Signed-off-by: Martin Prpič <mprpic@redhat.com>
2025-01-28 12:56:55 -05:00
dependabot[bot] cc80e6106c chore(deps): bump github/codeql-action from 3.28.5 to 3.28.6 (#3625)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.5 to 3.28.6.
- [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/f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4...17a820bf2e43b47be2c72b39cc905417bc1ab6d0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-28 12:13:18 -05:00
anchore-actions-token-generator[bot]andwagoodman 5b009dbc7b chore(deps): update CPE dictionary index (#3620)
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-01-27 10:56:58 -05:00
dependabot[bot] 1a2a7cb59f chore(deps): bump github.com/bmatcuk/doublestar/v4 from 4.8.0 to 4.8.1 (#3621)
Bumps [github.com/bmatcuk/doublestar/v4](https://github.com/bmatcuk/doublestar) from 4.8.0 to 4.8.1.
- [Release notes](https://github.com/bmatcuk/doublestar/releases)
- [Commits](https://github.com/bmatcuk/doublestar/compare/v4.8.0...v4.8.1)

---
updated-dependencies:
- dependency-name: github.com/bmatcuk/doublestar/v4
  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-01-27 09:57:04 -05:00
dependabot[bot] 27b8296630 chore(deps): bump github/codeql-action from 3.28.4 to 3.28.5 (#3622)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.4 to 3.28.5.
- [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/ee117c905ab18f32fa0f66c2fe40ecc8013f3e04...f6091c0113d1dcf9b98e269ee48e8a7e51b7bdd4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-27 09:56:53 -05:00
dependabot[bot] 5ea952e5b2 chore(deps): bump github/codeql-action from 3.28.3 to 3.28.4 (#3618)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.3 to 3.28.4.
- [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/dd196fa9ce80b6bacc74ca1c32bd5b0ba22efca7...ee117c905ab18f32fa0f66c2fe40ecc8013f3e04)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-24 09:49:54 -05:00
dependabot[bot] a5a2b83fe1 chore(deps): bump anchore/sbom-action from 0.17.9 to 0.18.0 (#3619)
Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.17.9 to 0.18.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/df80a981bc6edbc4e220a492d3cbe9f5547a6e75...f325610c9f50a54015d37c8d16cb3b0e2c8f4de0)

---
updated-dependencies:
- dependency-name: anchore/sbom-action
  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-01-24 09:49:46 -05:00
anchore-actions-token-generator[bot]andspiffcs 1c4743fca2 chore(deps): update tools to latest versions (#3607)
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-01-23 11:28:20 -05:00
dependabot[bot] a6d7ff69a9 chore(deps): bump github/codeql-action from 3.28.2 to 3.28.3 (#3608)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.2 to 3.28.3.
- [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/d68b2d4edb4189fd2a5366ac14e72027bd4b37dd...dd196fa9ce80b6bacc74ca1c32bd5b0ba22efca7)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 11:27:47 -05:00
dependabot[bot] ad83f7c2cb chore(deps): bump github.com/go-git/go-git/v5 from 5.13.1 to 5.13.2 (#3609)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.13.1 to 5.13.2.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.13.1...v5.13.2)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  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-01-23 11:27:01 -05:00
dependabot[bot] dffa52f950 chore(deps): bump github.com/docker/docker (#3610)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 27.5.0+incompatible to 27.5.1+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v27.5.0...v27.5.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 11:26:46 -05:00
dependabot[bot] 2abfa4eb36 chore(deps): bump actions/setup-go in /.github/actions/bootstrap (#3612)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.0.1 to 5.3.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/cdcb36043654635271a94b9a6d1392de5bb323a7...f111f3307d8850f501ac008e886eec1fd1932a34)

---
updated-dependencies:
- dependency-name: actions/setup-go
  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-01-23 11:26:05 -05:00
dependabot[bot] 1814a0aff5 chore(deps): bump actions/cache in /.github/actions/bootstrap (#3613)
Bumps [actions/cache](https://github.com/actions/cache) from 3.3.2 to 4.2.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/704facf57e6136b1bc63b828d79edcd491f0ee84...1bd1e32a3bdc45362d1e726936510720a7c30a57)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-23 11:25:47 -05:00
Weston Steimel b7f78a6a8b chore(ci): fix composite GitHub action path in dependabot config (#3611)
Signed-off-by: Weston Steimel <commits@weston.slmail.me>
2025-01-23 14:33:07 +00:00
anchore-actions-token-generator[bot]andspiffcs 222e6548a9 chore(deps): update tools to latest versions (#3602)
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-01-22 14:44:54 -05:00
dependabot[bot] 0969453dbe chore(deps): bump github/codeql-action from 3.28.1 to 3.28.2 (#3604)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.28.1 to 3.28.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b6a472f63d85b9c78a3ac5e89422239fc15e9b3c...d68b2d4edb4189fd2a5366ac14e72027bd4b37dd)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-22 10:47:57 -05:00
dependabot[bot] a5ef8167a1 chore(deps): bump github.com/hashicorp/hcl/v2 from 2.22.0 to 2.23.0 (#3605)
Bumps [github.com/hashicorp/hcl/v2](https://github.com/hashicorp/hcl) from 2.22.0 to 2.23.0.
- [Release notes](https://github.com/hashicorp/hcl/releases)
- [Changelog](https://github.com/hashicorp/hcl/blob/main/CHANGELOG.md)
- [Commits](https://github.com/hashicorp/hcl/compare/v2.22.0...v2.23.0)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/hcl/v2
  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-01-22 10:47:47 -05:00
dependabot[bot] 07f0658843 chore(deps): bump github.com/aquasecurity/go-pep440-version (#3606)
Bumps [github.com/aquasecurity/go-pep440-version](https://github.com/aquasecurity/go-pep440-version) from 0.0.0-20210121094942-22b2f8951d46 to 0.0.1.
- [Release notes](https://github.com/aquasecurity/go-pep440-version/releases)
- [Commits](https://github.com/aquasecurity/go-pep440-version/commits/v0.0.1)

---
updated-dependencies:
- dependency-name: github.com/aquasecurity/go-pep440-version
  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-01-22 10:47:38 -05:00
Christopher Angelo Phillips 7f5dbf9872 chore: bump stereoscope to v0.0.13 (#3601)
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-01-21 15:50:42 -05:00
c10e904c28 feat(cataloger): add a terraform provider cataloger (#3378)
* feat(cataloger): add a terraform provider cataloger
* chore: bump schema from 16.0.19 -> 16.0.20
------
Signed-off-by: Thomas Gosteli <thomas.gosteli@protonmail.ch>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Signed-off-by: Christopher Angelo Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-01-21 14:44:54 -05:00
anchore-actions-token-generator[bot]andspiffcs 1906c179d0 chore(deps): update tools to latest versions (#3597)
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-01-21 11:44:27 -05:00
anchore-actions-token-generator[bot]andwagoodman 4edfa4d138 chore(deps): update CPE dictionary index (#3599)
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-01-21 11:43:59 -05:00
dependabot[bot] a6557d7cec chore(deps): bump actions/setup-go from 5.2.0 to 5.3.0 (#3600)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 5.2.0 to 5.3.0.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/3041bf56c941b39c61721a86cd11f3bb1338122a...f111f3307d8850f501ac008e886eec1fd1932a34)

---
updated-dependencies:
- dependency-name: actions/setup-go
  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-01-21 11:41:53 -05:00
Dominik SchmidtandKeith Zantow 19a75fe504 feat(golang): add license parsing from vendor dirs (#3522)
Signed-off-by: Dominik Schmidt <dev@dominik-schmidt.de>
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Co-authored-by: Keith Zantow <kzantow@gmail.com>
2025-01-17 20:25:05 -05:00
Christopher Angelo Phillips 8198a706d6 chore: bump packageurl-go with new parsing rules (#3596)
* chore: bump packageurl-go with new parsing rules
* test: update expectedPURL in unit tests to match new % encoding
---------

Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-01-17 16:20:14 -05:00
179 changed files with 14191 additions and 832 deletions
+6 -6
View File
@@ -18,7 +18,7 @@ tools:
# used to sign mac binaries at release
- name: quill
version:
want: v0.5.0
want: v0.5.1
method: github-release
with:
repo: anchore/quill
@@ -26,7 +26,7 @@ tools:
# used for linting
- name: golangci-lint
version:
want: v1.63.4
want: v1.64.5
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.1
want: v2.4.3
method: github-release
with:
repo: sigstore/cosign
@@ -58,7 +58,7 @@ tools:
# used to release all artifacts
- name: goreleaser
version:
want: v2.5.1
want: v2.7.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.40.1
want: v3.41.0
method: github-release
with:
repo: go-task/task
@@ -111,7 +111,7 @@ tools:
# used for triggering a release
- name: gh
version:
want: v2.65.0
want: v2.67.0
method: github-release
with:
repo: cli/cli
+4 -4
View File
@@ -5,7 +5,7 @@ inputs:
go-version:
description: "Go version to install"
required: true
default: "1.23.x"
default: "1.24.x"
go-dependencies:
description: "Download go dependencies"
required: true
@@ -29,7 +29,7 @@ runs:
using: "composite"
steps:
# note: go mod and build is automatically cached on default with v4+
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
if: inputs.go-version != ''
with:
go-version: ${{ inputs.go-version }}
@@ -37,7 +37,7 @@ runs:
- name: Restore tool cache
if: inputs.tools == 'true'
id: tool-cache
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
with:
path: ${{ github.workspace }}/.tool
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
@@ -60,7 +60,7 @@ runs:
- name: Restore ORAS cache from github actions
if: inputs.download-test-fixture-cache == 'true'
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
with:
path: ${{ github.workspace }}/.tmp/oras-cache
key: ${{ inputs.cache-key-prefix }}-oras-cache
+1 -1
View File
@@ -19,7 +19,7 @@ updates:
- "dependencies"
- package-ecosystem: "github-actions"
directory: "/.github/actions/boostrap"
directory: "/.github/actions/bootstrap"
schedule:
interval: "daily"
open-pull-requests-limit: 10
+2 -2
View File
@@ -11,7 +11,7 @@ jobs:
Benchmark-Test:
name: "Benchmark tests"
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
# note: we want benchmarks to run on pull_request events in order to publish results to a sticky comment, and
# we also want to run on push such that merges to main are recorded to the cache. For this reason we don't filter
# the job by event.
@@ -23,7 +23,7 @@ jobs:
uses: ./.github/actions/bootstrap
- name: Restore base benchmark result
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 #v4.2.0
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
with:
path: test/results/benchmark-main.txt
# use base sha for PR or new commit hash for main push in benchmark result key
+4 -4
View File
@@ -39,13 +39,13 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
- name: Install Go
uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a #v5.2.0
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 #v5.3.0
with:
go-version-file: go.mod
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c #v3.28.1
uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 #v3.28.9
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c #v3.28.1
uses: github/codeql-action/autobuild@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 #v3.28.9
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -70,4 +70,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c #v3.28.1
uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 #v3.28.9
+1 -1
View File
@@ -18,7 +18,7 @@ on:
jobs:
release:
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
+3 -3
View File
@@ -13,7 +13,7 @@ on:
jobs:
quality-gate:
environment: release
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
@@ -98,7 +98,7 @@ jobs:
release:
needs: [quality-gate]
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
permissions:
contents: write
packages: write
@@ -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@df80a981bc6edbc4e220a492d3cbe9f5547a6e75 #v0.17.9
- uses: anchore/sbom-action@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 #v0.18.0
continue-on-error: true
with:
file: go.mod
+12 -12
View File
@@ -15,7 +15,7 @@ jobs:
Static-Analysis:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
name: "Static analysis"
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
@@ -29,7 +29,7 @@ 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-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
@@ -45,7 +45,7 @@ jobs:
Integration-Test:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
name: "Integration tests"
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
@@ -63,7 +63,7 @@ jobs:
Build-Snapshot-Artifacts:
name: "Build snapshot artifacts"
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
@@ -78,7 +78,7 @@ jobs:
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
# see https://github.com/actions/upload-artifact/issues/199 for more info
- name: Upload snapshot artifacts
uses: actions/cache/save@1bd1e32a3bdc45362d1e726936510720a7c30a57 #v4.2.0
uses: actions/cache/save@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
with:
# we need to preserve the snapshot data itself as well as the task data that confirms if the
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
@@ -93,7 +93,7 @@ jobs:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
name: "Acceptance tests (Linux)"
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
@@ -104,7 +104,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 #v4.2.0
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
with:
path: |
snapshot
@@ -139,7 +139,7 @@ jobs:
runs-on: macos-latest
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@v3.7.0
uses: sigstore/cosign-installer@v3.8.0
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
@@ -152,7 +152,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 #v4.2.0
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
with:
path: |
snapshot
@@ -176,7 +176,7 @@ jobs:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
name: "CLI tests (Linux)"
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
@@ -187,7 +187,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@1bd1e32a3bdc45362d1e726936510720a7c30a57 #v4.2.0
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
with:
path: |
snapshot
@@ -207,7 +207,7 @@ jobs:
Cleanup-Cache:
name: "Cleanup snapshot cache"
if: github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-20.04
runs-on: ubuntu-24.04
permissions:
actions: write
needs:
+5 -1
View File
@@ -8,4 +8,8 @@ A cataloger contributed by Oracle Corporation that extracts packages given withi
## Swift Package Manager
A cataloger contributed by Axis Communications that catalogs packages resolved by Swift Package Manager.
A cataloger contributed by Axis Communications that catalogs packages resolved by Swift Package Manager.
## Bitnami Packages
A cataloger contributed by Bitnami developer that catalogs packages described in Bitnami SBOMs.
+2
View File
@@ -123,6 +123,7 @@ Note that flags using the @<version> can be used for earlier versions of each sp
### Supported Ecosystems
- Alpine (apk)
- Bitnami packages
- C (conan)
- C++ (conan)
- Dart (pubs)
@@ -146,6 +147,7 @@ Note that flags using the @<version> can be used for earlier versions of each sp
- Rust (cargo.lock)
- Swift (cocoapods, swift-package-manager)
- Wordpress plugins
- Terraform providers (.terraform.lock.hcl)
## Documentation
+1 -1
View File
@@ -61,7 +61,7 @@ type backgroundLineReader struct {
func (m *Handler) handleAttestationStarted(e partybus.Event) []tea.Model {
reader, prog, taskInfo, err := syftEventParsers.ParseAttestationStartedEvent(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
log.WithFields("error", err).Debug("unable to parse event")
return nil
}
+1 -1
View File
@@ -89,7 +89,7 @@ func (cts catalogerTaskModel) View() string {
func (m *Handler) handleCatalogerTaskStarted(e partybus.Event) ([]tea.Model, tea.Cmd) {
mon, info, err := syftEventParsers.ParseCatalogerTaskStarted(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
log.WithFields("error", err).Debug("unable to parse event")
return nil, nil
}
+1 -1
View File
@@ -12,7 +12,7 @@ import (
func (m *Handler) handleFetchImage(e partybus.Event) []tea.Model {
imgName, prog, err := stereoEventParsers.ParseFetchImage(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
log.WithFields("error", err).Debug("unable to parse event")
return nil
}
+1 -1
View File
@@ -12,7 +12,7 @@ import (
func (m *Handler) handleFileIndexingStarted(e partybus.Event) []tea.Model {
path, prog, err := syftEventParsers.ParseFileIndexingStarted(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
log.WithFields("error", err).Debug("unable to parse event")
return nil
}
@@ -43,7 +43,7 @@ type containerdPullStatusFormatter struct {
func (m *Handler) handlePullContainerdImage(e partybus.Event) []tea.Model {
_, pullStatus, err := stereoscopeParsers.ParsePullContainerdImage(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
log.WithFields("error", err).Debug("unable to parse event")
return nil
}
+1 -1
View File
@@ -44,7 +44,7 @@ type dockerPullStatusFormatter struct {
func (m *Handler) handlePullDockerImage(e partybus.Event) []tea.Model {
_, pullStatus, err := stereoscopeParsers.ParsePullDockerImage(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
log.WithFields("error", err).Debug("unable to parse event")
return nil
}
+1 -1
View File
@@ -12,7 +12,7 @@ import (
func (m *Handler) handleReadImage(e partybus.Event) []tea.Model {
imgMetadata, prog, err := stereoEventParsers.ParseReadImage(e)
if err != nil {
log.WithFields("error", err).Warn("unable to parse event")
log.WithFields("error", err).Debug("unable to parse event")
return nil
}
+2 -2
View File
@@ -6,7 +6,7 @@ import (
"github.com/anchore/clio"
"github.com/anchore/stereoscope"
ui2 "github.com/anchore/syft/cmd/syft/cli/ui"
handler "github.com/anchore/syft/cmd/syft/cli/ui"
"github.com/anchore/syft/cmd/syft/internal/ui"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
@@ -28,7 +28,7 @@ func AppClioSetupConfig(id clio.Identification, out io.Writer) *clio.SetupConfig
return clio.NewUICollection(
ui.New(out, cfg.Log.Quiet,
ui2.New(ui2.DefaultHandlerConfig()),
handler.New(handler.DefaultHandlerConfig()),
),
noUI,
), nil
+106 -46
View File
@@ -3,6 +3,7 @@ package commands
import (
"encoding/json"
"fmt"
"os"
"sort"
"strings"
@@ -14,7 +15,15 @@ import (
"github.com/anchore/clio"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/task"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/cataloging"
)
var (
activelyAddedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("10")) // hi green
deselectedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8")) // dark grey
activelyRemovedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("9")) // high red
defaultStyle = lipgloss.NewStyle().Underline(true)
deselectedDefaultStyle = lipgloss.NewStyle().Inherit(deselectedStyle).Underline(true)
)
type catalogerListOptions struct {
@@ -44,8 +53,9 @@ func CatalogerList(app clio.Application) *cobra.Command {
opts := defaultCatalogerListOptions()
return app.SetupCommand(&cobra.Command{
Use: "list [OPTIONS]",
Short: "List available catalogers",
Use: "list [OPTIONS]",
Short: "List available catalogers",
PreRunE: disableUI(app, os.Stdout),
RunE: func(_ *cobra.Command, _ []string) error {
return runCatalogerList(opts)
},
@@ -53,13 +63,19 @@ func CatalogerList(app clio.Application) *cobra.Command {
}
func runCatalogerList(opts *catalogerListOptions) error {
factories := task.DefaultPackageTaskFactories()
allTasks, err := factories.Tasks(task.DefaultCatalogingFactoryConfig())
pkgTaskFactories := task.DefaultPackageTaskFactories()
fileTaskFactories := task.DefaultFileTaskFactories()
allPkgTasks, err := pkgTaskFactories.Tasks(task.DefaultCatalogingFactoryConfig())
if err != nil {
return fmt.Errorf("unable to create cataloger tasks: %w", err)
return fmt.Errorf("unable to create pkg cataloger tasks: %w", err)
}
report, err := catalogerListReport(opts, allTasks)
allFileTasks, err := fileTaskFactories.Tasks(task.DefaultCatalogingFactoryConfig())
if err != nil {
return fmt.Errorf("unable to create file cataloger tasks: %w", err)
}
report, err := catalogerListReport(opts, [][]task.Task{allPkgTasks, allFileTasks})
if err != nil {
return fmt.Errorf("unable to generate cataloger list report: %w", err)
}
@@ -69,9 +85,10 @@ func runCatalogerList(opts *catalogerListOptions) error {
return nil
}
func catalogerListReport(opts *catalogerListOptions, allTasks []task.Task) (string, error) {
selectedTasks, selectionEvidence, err := task.Select(allTasks,
pkgcataloging.NewSelectionRequest().
func catalogerListReport(opts *catalogerListOptions, allTaskGroups [][]task.Task) (string, error) {
selectedTaskGroups, selectionEvidence, err := task.SelectInGroups(
allTaskGroups,
cataloging.NewSelectionRequest().
WithDefaults(opts.DefaultCatalogers...).
WithExpression(opts.SelectCatalogers...),
)
@@ -82,12 +99,12 @@ func catalogerListReport(opts *catalogerListOptions, allTasks []task.Task) (stri
switch opts.Output {
case "json":
report, err = renderCatalogerListJSON(selectedTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
report, err = renderCatalogerListJSON(flattenTaskGroups(selectedTaskGroups), selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
case "table", "":
if opts.ShowHidden {
report = renderCatalogerListTable(allTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
report = renderCatalogerListTables(allTaskGroups, selectionEvidence)
} else {
report = renderCatalogerListTable(selectedTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
report = renderCatalogerListTables(selectedTaskGroups, selectionEvidence)
}
}
@@ -98,6 +115,14 @@ func catalogerListReport(opts *catalogerListOptions, allTasks []task.Task) (stri
return report, nil
}
func flattenTaskGroups(taskGroups [][]task.Task) []task.Task {
var allTasks []task.Task
for _, tasks := range taskGroups {
allTasks = append(allTasks, tasks...)
}
return allTasks
}
func renderCatalogerListJSON(tasks []task.Task, selection task.Selection, defaultSelections, selections []string) (string, error) {
type node struct {
Name string `json:"name"`
@@ -109,7 +134,12 @@ func renderCatalogerListJSON(tasks []task.Task, selection task.Selection, defaul
nodesByName := make(map[string]node)
for name := range tagsByName {
tagsSelected := selection.TokensByTask[name].SelectedOn.List()
tokensByTask, ok := selection.TokensByTask[name]
var tagsSelected []string
if ok {
tagsSelected = tokensByTask.SelectedOn.List()
}
if len(tagsSelected) == 1 && tagsSelected[0] == "all" {
tagsSelected = tagsByName[name]
@@ -153,10 +183,56 @@ func renderCatalogerListJSON(tasks []task.Task, selection task.Selection, defaul
return string(by), err
}
func renderCatalogerListTable(tasks []task.Task, selection task.Selection, defaultSelections, selections []string) string {
func renderCatalogerListTables(taskGroups [][]task.Task, selection task.Selection) string {
pkgCatalogerTable := renderCatalogerListTable(taskGroups[0], selection, "Package Cataloger")
fileCatalogerTable := renderCatalogerListTable(taskGroups[1], selection, "File Cataloger")
report := fileCatalogerTable + "\n" + pkgCatalogerTable + "\n"
hasAdditions := len(selection.Request.AddNames) > 0
hasDefaults := len(selection.Request.DefaultNamesOrTags) > 0
hasRemovals := len(selection.Request.RemoveNamesOrTags) > 0
hasSubSelections := len(selection.Request.SubSelectTags) > 0
expressions := len(selection.Request.SubSelectTags) + len(selection.Request.AddNames) + len(selection.Request.RemoveNamesOrTags)
var header string
header += fmt.Sprintf("Default selections: %d\n", len(selection.Request.DefaultNamesOrTags))
if hasDefaults {
for _, expr := range selection.Request.DefaultNamesOrTags {
header += fmt.Sprintf(" • '%s'\n", expr)
}
}
header += fmt.Sprintf("Selection expressions: %d\n", expressions)
if hasSubSelections {
for _, n := range selection.Request.SubSelectTags {
header += fmt.Sprintf(" • '%s' (intersect)\n", n)
}
}
if hasRemovals {
for _, n := range selection.Request.RemoveNamesOrTags {
header += fmt.Sprintf(" • '-%s' (remove)\n", n)
}
}
if hasAdditions {
for _, n := range selection.Request.AddNames {
header += fmt.Sprintf(" • '+%s' (add)\n", n)
}
}
return header + report
}
func renderCatalogerListTable(tasks []task.Task, selection task.Selection, kindTitle string) string {
if len(tasks) == 0 {
return activelyRemovedStyle.Render(fmt.Sprintf("No %ss selected", strings.ToLower(kindTitle)))
}
t := table.NewWriter()
t.SetStyle(table.StyleLight)
t.AppendHeader(table.Row{"Cataloger", "Tags"})
t.AppendHeader(table.Row{kindTitle, "Tags"})
names, tagsByName := extractTaskInfo(tasks)
@@ -172,27 +248,12 @@ func renderCatalogerListTable(tasks []task.Task, selection task.Selection, defau
report := t.Render()
if len(selections) > 0 {
header := "Selected by expressions:\n"
for _, expr := range selections {
header += fmt.Sprintf(" - %q\n", expr)
}
report = header + report
}
if len(defaultSelections) > 0 {
header := "Default selections:\n"
for _, expr := range defaultSelections {
header += fmt.Sprintf(" - %q\n", expr)
}
report = header + report
}
return report
}
func formatRow(name string, tags []string, selection task.Selection) table.Row {
isIncluded := selection.Result.Has(name)
defaults := strset.New(selection.Request.DefaultNamesOrTags...)
var selections *task.TokenSelection
if s, exists := selection.TokensByTask[name]; exists {
selections = &s
@@ -200,35 +261,32 @@ func formatRow(name string, tags []string, selection task.Selection) table.Row {
var formattedTags []string
for _, tag := range tags {
formattedTags = append(formattedTags, formatToken(tag, selections, isIncluded))
formattedTags = append(formattedTags, formatToken(tag, selections, isIncluded, defaults))
}
var tagStr string
if isIncluded {
tagStr = strings.Join(formattedTags, ", ")
} else {
tagStr = strings.Join(formattedTags, grey.Render(", "))
tagStr = strings.Join(formattedTags, deselectedStyle.Render(", "))
}
// TODO: selection should keep warnings (non-selections) in struct
return table.Row{
formatToken(name, selections, isIncluded),
formatToken(name, selections, isIncluded, defaults),
tagStr,
}
}
var (
green = lipgloss.NewStyle().Foreground(lipgloss.Color("10")) // hi green
grey = lipgloss.NewStyle().Foreground(lipgloss.Color("8")) // dark grey
red = lipgloss.NewStyle().Foreground(lipgloss.Color("9")) // high red
)
func formatToken(token string, selection *task.TokenSelection, included bool) string {
func formatToken(token string, selection *task.TokenSelection, included bool, defaults *strset.Set) string {
if included && selection != nil {
// format all tokens in selection in green
if selection.SelectedOn.Has(token) {
return green.Render(token)
if defaults.Has(token) {
return defaultStyle.Render(token)
}
return activelyAddedStyle.Render(token)
}
return token
@@ -236,10 +294,12 @@ func formatToken(token string, selection *task.TokenSelection, included bool) st
// format all tokens in selection in red, all others in grey
if selection != nil && selection.DeselectedOn.Has(token) {
return red.Render(token)
return activelyRemovedStyle.Render(token)
}
return grey.Render(token)
if defaults.Has(token) {
return deselectedDefaultStyle.Render(token)
}
return deselectedStyle.Render(token)
}
func extractTaskInfo(tasks []task.Task) ([]string, map[string][]string) {
@@ -40,23 +40,35 @@ func (d dummyTask) Execute(_ context.Context, _ file.Resolver, _ sbomsync.Builde
panic("implement me")
}
func testTasks() []task.Task {
return []task.Task{
dummyTask{
name: "task1",
selectors: []string{"image", "a", "b", "1"},
func testTasks() [][]task.Task {
return [][]task.Task{
{
dummyTask{
name: "task1",
selectors: []string{"image", "a", "b", "1"},
},
dummyTask{
name: "task2",
selectors: []string{"image", "b", "c", "2"},
},
dummyTask{
name: "task3",
selectors: []string{"directory", "c", "d", "3"},
},
dummyTask{
name: "task4",
selectors: []string{"directory", "d", "e", "4"},
},
},
dummyTask{
name: "task2",
selectors: []string{"image", "b", "c", "2"},
},
dummyTask{
name: "task3",
selectors: []string{"directory", "c", "d", "3"},
},
dummyTask{
name: "task4",
selectors: []string{"directory", "d", "e", "4"},
{
dummyTask{
name: "file-task1",
selectors: []string{"file", "ft", "ft-1-b"},
},
dummyTask{
name: "file-task2",
selectors: []string{"file", "ft", "ft-2-b"},
},
},
}
}
@@ -76,16 +88,23 @@ func Test_catalogerListReport(t *testing.T) {
return c
}(),
want: `
Default selections:
- "all"
┌───────────┬────────────────────┐
│ CATALOGER │ TAGS │
├───────────┼────────────────────┤
│ task1 │ 1, a, b, image │
│ task2 │ 2, b, c, image │
│ task3 │ 3, c, d, directory │
│ task4 │ 4, d, directory, e │
└───────────┴────────────────────┘
Default selections: 1
• 'all'
Selection expressions: 0
┌────────────────┬──────────────────┐
│ 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 │
│ task2 │ 2, b, c, image │
│ task3 │ 3, c, d, directory │
│ task4 │ 4, d, directory, e │
└───────────────────┴────────────────────┘
`,
},
{
@@ -96,7 +115,7 @@ Default selections:
return c
}(),
want: `
{"default":["all"],"selection":[],"catalogers":[{"name":"task1","tags":["1","a","b","image"]},{"name":"task2","tags":["2","b","c","image"]},{"name":"task3","tags":["3","c","d","directory"]},{"name":"task4","tags":["4","d","directory","e"]}]}
{"default":["all"],"selection":[],"catalogers":[{"name":"file-task1","tags":["file","ft","ft-1-b"]},{"name":"file-task2","tags":["file","ft","ft-2-b"]},{"name":"task1","tags":["1","a","b","image"]},{"name":"task2","tags":["2","b","c","image"]},{"name":"task3","tags":["3","c","d","directory"]},{"name":"task4","tags":["4","d","directory","e"]}]}
`,
},
{
@@ -105,19 +124,27 @@ Default selections:
c := defaultCatalogerListOptions()
c.Output = "table"
c.DefaultCatalogers = []string{
"image",
"image", // note: for backwards compatibility file will automatically be added
}
return c
}(),
want: `
Default selections:
- "image"
┌───────────┬────────────────┐
│ CATALOGER │ TAGS │
├───────────┼────────────────┤
│ task1 │ 1, a, b, image │
│ task2 │ 2, b, c, image │
└───────────┴────────────────┘
Default selections: 2
• 'image'
• 'file'
Selection expressions: 0
┌────────────────┬──────────────────┐
│ 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 │
│ task2 │ 2, b, c, image │
└───────────────────┴────────────────┘
`,
},
{
@@ -131,7 +158,7 @@ Default selections:
return c
}(),
want: `
{"default":["image"],"selection":[],"catalogers":[{"name":"task1","tags":["image"]},{"name":"task2","tags":["image"]}]}
{"default":["image"],"selection":[],"catalogers":[{"name":"file-task1","tags":["file"]},{"name":"file-task2","tags":["file"]},{"name":"task1","tags":["image"]},{"name":"task2","tags":["image"]}]}
`,
},
{
@@ -147,23 +174,32 @@ Default selections:
"+task3",
"-c",
"b",
"-file",
"+file-task1",
}
return c
}(),
want: `
Default selections:
- "image"
Selected by expressions:
- "-directory"
- "+task3"
- "-c"
- "b"
┌───────────┬────────────────────┐
│ CATALOGER │ TAGS │
├───────────┼────────────────────┤
│ task1 │ 1, a, b, image │
│ task3 │ 3, c, d, directory │
└───────────┴────────────────────┘
Default selections: 1
• 'image'
Selection expressions: 6
• 'b' (intersect)
• '-directory' (remove)
• '-c' (remove)
• '-file' (remove)
• '+task3' (add)
• '+file-task1' (add)
┌────────────────┬──────────────────┐
│ FILE CATALOGER │ TAGS │
├────────────────┼──────────────────┤
│ file-task1 │ file, ft, ft-1-b │
└────────────────┴──────────────────┘
┌───────────────────┬────────────────────┐
│ PACKAGE CATALOGER │ TAGS │
├───────────────────┼────────────────────┤
│ task1 │ 1, a, b, image │
│ task3 │ 3, c, d, directory │
└───────────────────┴────────────────────┘
`,
},
{
@@ -183,7 +219,7 @@ Selected by expressions:
return c
}(),
want: `
{"default":["image"],"selection":["-directory","+task3","-c","b"],"catalogers":[{"name":"task1","tags":["b","image"]},{"name":"task3","tags":["task3"]}]}
{"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"]}]}
`,
},
}
+23
View File
@@ -0,0 +1,23 @@
package commands
import (
"io"
"github.com/spf13/cobra"
"github.com/anchore/clio"
"github.com/anchore/syft/cmd/syft/internal/ui"
)
func disableUI(app clio.Application, out io.Writer) func(*cobra.Command, []string) error {
return func(_ *cobra.Command, _ []string) error {
type Stater interface {
State() *clio.State
}
state := app.(Stater).State()
state.UI = clio.NewUICollection(ui.None(out, state.Config.Log.Quiet))
return nil
}
}
+18 -1
View File
@@ -19,6 +19,7 @@ import (
"github.com/anchore/syft/syft/file/cataloger/executable"
"github.com/anchore/syft/syft/file/cataloger/filecontent"
"github.com/anchore/syft/syft/pkg/cataloger/binary"
"github.com/anchore/syft/syft/pkg/cataloger/dotnet"
"github.com/anchore/syft/syft/pkg/cataloger/golang"
"github.com/anchore/syft/syft/pkg/cataloger/java"
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
@@ -33,6 +34,7 @@ type Catalog struct {
DefaultCatalogers []string `yaml:"default-catalogers" json:"default-catalogers" mapstructure:"default-catalogers"`
SelectCatalogers []string `yaml:"select-catalogers" json:"select-catalogers" mapstructure:"select-catalogers"`
Package packageConfig `yaml:"package" json:"package" mapstructure:"package"`
License licenseConfig `yaml:"license" json:"license" mapstructure:"license"`
File fileConfig `yaml:"file" json:"file" mapstructure:"file"`
Scope string `yaml:"scope" json:"scope" mapstructure:"scope"`
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
@@ -41,6 +43,7 @@ type Catalog struct {
Enrich []string `yaml:"enrich" json:"enrich" mapstructure:"enrich"`
// ecosystem-specific cataloger configuration
Dotnet dotnetConfig `yaml:"dotnet" json:"dotnet" mapstructure:"dotnet"`
Golang golangConfig `yaml:"golang" json:"golang" mapstructure:"golang"`
Java javaConfig `yaml:"java" json:"java" mapstructure:"java"`
JavaScript javaScriptConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"`
@@ -69,7 +72,9 @@ func DefaultCatalog() Catalog {
Compliance: defaultComplianceConfig(),
Scope: source.SquashedScope.String(),
Package: defaultPackageConfig(),
License: defaultLicenseConfig(),
LinuxKernel: defaultLinuxKernelConfig(),
Dotnet: defaultDotnetConfig(),
Golang: defaultGolangConfig(),
Java: defaultJavaConfig(),
File: defaultFileConfig(),
@@ -89,9 +94,10 @@ func (cfg Catalog) ToSBOMConfig(id clio.Identification) *syft.CreateSBOMConfig {
WithUnknownsConfig(cfg.ToUnknownsConfig()).
WithSearchConfig(cfg.ToSearchConfig()).
WithPackagesConfig(cfg.ToPackagesConfig()).
WithLicenseConfig(cfg.ToLicenseConfig()).
WithFilesConfig(cfg.ToFilesConfig()).
WithCatalogerSelection(
pkgcataloging.NewSelectionRequest().
cataloging.NewSelectionRequest().
WithDefaults(cfg.DefaultCatalogers...).
WithExpression(cfg.SelectCatalogers...),
)
@@ -146,6 +152,13 @@ func (cfg Catalog) ToFilesConfig() filecataloging.Config {
}
}
func (cfg Catalog) ToLicenseConfig() cataloging.LicenseConfig {
return cataloging.LicenseConfig{
IncludeUnkownLicenseContent: cfg.License.IncludeUnknownLicenseContent,
Coverage: cfg.License.LicenseCoverage,
}
}
func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
archiveSearch := cataloging.ArchiveSearchConfig{
IncludeIndexedArchives: cfg.Package.SearchIndexedArchives,
@@ -153,9 +166,13 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
}
return pkgcataloging.Config{
Binary: binary.DefaultClassifierCatalogerConfig(),
Dotnet: dotnet.DefaultCatalogerConfig().
WithCertificateValidation(cfg.Dotnet.EnableCertificateValidation),
Golang: golang.DefaultCatalogerConfig().
WithSearchLocalModCacheLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchLocalModCacheLicenses)).
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
WithSearchLocalVendorLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchLocalVendorLicenses)).
WithLocalVendorDir(cfg.Golang.LocalVendorDir).
WithSearchRemoteLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchRemoteLicenses)).
WithProxy(cfg.Golang.Proxy).
WithNoProxy(cfg.Golang.NoProxy).
+25
View File
@@ -0,0 +1,25 @@
package options
import (
"github.com/anchore/clio"
"github.com/anchore/syft/syft/pkg/cataloger/dotnet"
)
type dotnetConfig struct {
EnableCertificateValidation bool `json:"enable-certificate-validation" yaml:"enable-certificate-validation" mapstructure:"enable-certificate-validation"`
}
var _ interface {
clio.FieldDescriber
} = (*dotnetConfig)(nil)
func (o *dotnetConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.EnableCertificateValidation, `enable certificate validation -- this requires an active internet connection to download certificates and CRLs`)
}
func defaultDotnetConfig() dotnetConfig {
def := dotnet.DefaultCatalogerConfig()
return dotnetConfig{
EnableCertificateValidation: def.EnableCertificateValidation,
}
}
+7
View File
@@ -10,6 +10,8 @@ import (
type golangConfig struct {
SearchLocalModCacheLicenses *bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
SearchLocalVendorLicenses *bool `json:"search-local-vendor-licenses" yaml:"search-local-vendor-licenses" mapstructure:"search-local-vendor-licenses"`
LocalVendorDir string `json:"local-vendor-dir" yaml:"local-vendor-dir" mapstructure:"local-vendor-dir"`
SearchRemoteLicenses *bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
@@ -24,6 +26,9 @@ func (o *golangConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.SearchLocalModCacheLicenses, `search for go package licences in the GOPATH of the system running Syft, note that this is outside the
container filesystem and potentially outside the root of a local directory scan`)
descriptions.Add(&o.LocalModCacheDir, `specify an explicit go mod cache directory, if unset this defaults to $GOPATH/pkg/mod or $HOME/go/pkg/mod`)
descriptions.Add(&o.SearchLocalVendorLicenses, `search for go package licences in the vendor folder on the system running Syft, note that this is outside the
container filesystem and potentially outside the root of a local directory scan`)
descriptions.Add(&o.LocalVendorDir, `specify an explicit go vendor directory, if unset this defaults to ./vendor`)
descriptions.Add(&o.SearchRemoteLicenses, `search for go package licences by retrieving the package from a network proxy`)
descriptions.Add(&o.Proxy, `remote proxy to use when retrieving go packages from the network,
if unset this defaults to $GOPROXY followed by https://proxy.golang.org`)
@@ -49,6 +54,8 @@ func defaultGolangConfig() golangConfig {
return golangConfig{
SearchLocalModCacheLicenses: nil, // this defaults to false, which is the API default
LocalModCacheDir: def.LocalModCacheDir,
SearchLocalVendorLicenses: nil, // this defaults to false, which is the API default
LocalVendorDir: def.LocalVendorDir,
SearchRemoteLicenses: nil, // this defaults to false, which is the API default
Proxy: strings.Join(def.Proxies, ","),
NoProxy: strings.Join(def.NoProxy, ","),
+28
View File
@@ -0,0 +1,28 @@
package options
import (
"github.com/anchore/clio"
)
type licenseConfig struct {
IncludeUnknownLicenseContent bool `yaml:"include-unknown-license-content" json:"include-unknown-license-content" mapstructure:"include-unknown-license-content"`
LicenseCoverage float64 `yaml:"license-coverage" json:"license-coverage" mapstructure:"license-coverage"`
}
var _ interface {
clio.FieldDescriber
} = (*licenseConfig)(nil)
func (o *licenseConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.IncludeUnknownLicenseContent, `include the content of a license in the SBOM when syft
cannot determine a valid SPDX ID for the given license`)
descriptions.Add(&o.LicenseCoverage, `adjust the percent as a fraction of the total text, in normalized words, that
matches any valid license for the given inputs, expressed as a percentage across all of the licenses matched.`)
}
func defaultLicenseConfig() licenseConfig {
return licenseConfig{
IncludeUnknownLicenseContent: false,
LicenseCoverage: 75,
}
}
+2 -2
View File
@@ -16,9 +16,9 @@ var _ interface {
} = (*packageConfig)(nil)
func (o *packageConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.SearchUnindexedArchives, `search within archives that do contain a file index to search against (zip)
descriptions.Add(&o.SearchIndexedArchives, `search within archives that do contain a file index to search against (zip)
note: for now this only applies to the java package cataloger`)
descriptions.Add(&o.SearchIndexedArchives, `search within archives that do not contain a file index to search against (tar, tar.gz, tar.bz2, etc)
descriptions.Add(&o.SearchUnindexedArchives, `search within archives that do not contain a file index to search against (tar, tar.gz, tar.bz2, etc)
note: enabling this may result in a performance impact since all discovered compressed tars will be decompressed
note: for now this only applies to the java package cataloger`)
descriptions.Add(&o.ExcludeBinaryOverlapByOwnership, `allows users to exclude synthetic binary packages from the sbom
@@ -418,6 +418,14 @@ var dirOnlyTestCases = []testCase{
"ocaml-base-compiler": "4.14.0",
},
},
{
name: "find terraform packages",
pkgType: pkg.TerraformPkg,
pkgLanguage: pkg.Go,
pkgInfo: map[string]string{
"registry.terraform.io/hashicorp/aws": "5.72.1",
},
},
}
var commonTestCases = []testCase{
@@ -74,6 +74,8 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Remove(string(pkg.ConanPkg))
definedPkgs.Remove(string(pkg.HackagePkg))
definedPkgs.Remove(string(pkg.BinaryPkg))
definedPkgs.Remove(string(pkg.BitnamiPkg))
definedPkgs.Remove(string(pkg.GraalVMNativeImagePkg))
definedPkgs.Remove(string(pkg.HexPkg))
definedPkgs.Remove(string(pkg.LinuxKernelPkg))
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
@@ -82,6 +84,7 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Remove(string(pkg.OpamPkg))
definedPkgs.Remove(string(pkg.GithubActionPkg))
definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg))
definedPkgs.Remove(string(pkg.TerraformPkg))
var cases []testCase
cases = append(cases, commonTestCases...)
@@ -218,6 +221,8 @@ func TestPkgCoverageDirectory(t *testing.T) {
definedLanguages.Remove(pkg.R.String())
observedPkgs.Remove(string(pkg.UnknownPkg))
definedPkgs.Remove(string(pkg.BinaryPkg))
definedPkgs.Remove(string(pkg.BitnamiPkg))
definedPkgs.Remove(string(pkg.GraalVMNativeImagePkg))
definedPkgs.Remove(string(pkg.LinuxKernelPkg))
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
definedPkgs.Remove(string(pkg.Rpkg))
@@ -0,0 +1,25 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "5.72.1"
constraints = ">= 5.72.0"
hashes = [
"h1:jhd5O5o0CfZCNEwwN0EiDAzb7ApuFrtxJqa6HXW4EKE=",
"zh:0dea6843836e926d33469b48b948744079023816d16a2ff7666bcfb6aa3522d4",
"zh:195fa9513f75800a0d62797ebec75ee73e9b8c28d713fe9b63d3b1d1eec129b3",
"zh:1ed92f3961715bf0e024bcde3c12dfbdc50b00c1f8a43cc00802cfc45a256208",
"zh:2ac687e3a52606466cae4a6813e81d923042488df88d2424e28d3f8530f091bb",
"zh:32e7ca75f9314557daada3c44628fe1f3bf964a4f833bfb4b2295d833fe64b6f",
"zh:374ee0e6b4327cc6ef666908ce5d6450a3a56e90cd2b785e83c2bcfc100021d2",
"zh:5500fd6fdac44f96411fcf9c6d01691159ec35455ed127eb4c3a498e1cc92a64",
"zh:723a2dc4b064c12e7ee62ad4fbfd72fa5e025206ea47b735994ef53f3c373152",
"zh:89d97b87605f1d734f27e642567cbecf785b521af8ea81dac55c77ccde876221",
"zh:951ee1e5731e8d65d521d71b95927e55055b3c4656eef6d46fa580a63328befc",
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
"zh:9b2b362470b64ec227b2da64762ab8bc4111c6b80365fd9d82fc5e1e33f44038",
"zh:aa6e57d0cb974ff0da5dee5d43ad2745cbbc4a2b507d4c799839b9fa96daf688",
"zh:ba0d14c4a6b7aa844a830d47c0bf995b632e37f0795394b5b60c638b62b7fc03",
"zh:c9764065a9c5d324db0b02bd201b9e3a2118e49c4960884acdeea377173302e9",
]
}
@@ -10,6 +10,7 @@ import (
"github.com/anchore/stereoscope/pkg/imagetest"
"github.com/anchore/syft/cmd/syft/internal/options"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
@@ -20,7 +21,7 @@ func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Sco
Name: "syft-tester",
Version: "v0.99.0",
}).WithCatalogerSelection(
pkgcataloging.NewSelectionRequest().
cataloging.NewSelectionRequest().
WithExpression(catalogerSelection...),
)
cfg.Search.Scope = scope
@@ -55,7 +56,7 @@ func catalogDirectory(t *testing.T, dir string, catalogerSelection ...string) (s
Name: "syft-tester",
Version: "v0.99.0",
}).WithCatalogerSelection(
pkgcataloging.NewSelectionRequest().
cataloging.NewSelectionRequest().
WithExpression(catalogerSelection...),
)
+1 -1
View File
@@ -150,7 +150,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
case partybus.Event:
log.WithFields("component", "ui").Tracef("event: %q", msg.Type)
log.WithFields("component", "ui", "event", msg.Type).Trace("event")
switch msg.Type {
case event.CLIReport, event.CLINotification, event.CLIAppUpdateAvailable:
+1 -1
View File
@@ -74,7 +74,7 @@ func getSBOM(src source.Source) sbom.SBOM {
// only use OS related catalogers that would have been used with the kind of
// source type (container image or directory), but also add a specific python cataloger
WithCatalogerSelection(
pkgcataloging.NewSelectionRequest().
cataloging.NewSelectionRequest().
WithSubSelections("os").
WithAdditions("python-package-cataloger"),
).
+2 -1
View File
@@ -6,6 +6,7 @@ import (
"os"
"github.com/anchore/syft/syft"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
@@ -55,7 +56,7 @@ func getSBOM(src source.Source, defaultTags ...string) sbom.SBOM {
WithCatalogerSelection(
// here you can sub-select, add, remove catalogers from the default selection...
// or replace the default selection entirely!
pkgcataloging.NewSelectionRequest().
cataloging.NewSelectionRequest().
WithDefaults(defaultTags...),
)
+36 -28
View File
@@ -1,6 +1,6 @@
module github.com/anchore/syft
go 1.23.2
go 1.24.0
require (
github.com/CycloneDX/cyclonedx-go v0.9.2
@@ -16,27 +16,27 @@ require (
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f
github.com/anchore/stereoscope v0.0.12
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
github.com/anchore/stereoscope v0.0.13
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
// we are hinting brotli to latest due to warning when installing archiver v3:
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/bmatcuk/doublestar/v4 v4.8.0
github.com/aquasecurity/go-pep440-version v0.0.1
github.com/bmatcuk/doublestar/v4 v4.8.1
github.com/charmbracelet/bubbles v0.20.0
github.com/charmbracelet/bubbletea v1.2.4
github.com/charmbracelet/lipgloss v1.0.0
github.com/dave/jennifer v1.7.1
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/distribution/reference v0.6.0
github.com/docker/docker v27.5.0+incompatible
github.com/docker/docker v28.0.0+incompatible
github.com/dustin/go-humanize v1.0.1
github.com/elliotchance/phpserialize v1.4.0
github.com/facebookincubator/nvdtools v0.1.5
github.com/github/go-spdx/v2 v2.3.2
github.com/gkampitakis/go-snaps v0.5.8
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.13.1
github.com/go-git/go-git/v5 v5.13.2
github.com/go-test/deep v1.1.1
github.com/google/go-cmp v0.6.0
github.com/google/go-containerregistry v0.20.3
@@ -46,7 +46,7 @@ require (
github.com/hashicorp/go-multierror v1.1.1
github.com/iancoleman/strcase v0.3.0
github.com/invopop/jsonschema v0.7.0
github.com/jedib0t/go-pretty/v6 v6.6.5
github.com/jedib0t/go-pretty/v6 v6.6.6
github.com/jinzhu/copier v0.4.0
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/knqyf263/go-rpmdb v0.1.1
@@ -61,14 +61,14 @@ require (
github.com/quasilyte/go-ruleguard/dsl v0.3.22
github.com/saferwall/pe v1.5.6
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
github.com/sanity-io/litter v1.5.6
github.com/sanity-io/litter v1.5.8
github.com/sassoftware/go-rpmutils v0.4.0
// pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/spdx/tools-golang v0.5.5
github.com/spf13/afero v1.12.0
github.com/spf13/cobra v1.8.1
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/vbatts/go-mtree v0.5.4
github.com/vifraa/gopom v1.0.0
@@ -77,10 +77,10 @@ require (
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
go.uber.org/goleak v1.3.0
golang.org/x/mod v0.22.0
golang.org/x/net v0.34.0
golang.org/x/mod v0.23.0
golang.org/x/net v0.35.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.34.5
modernc.org/sqlite v1.35.0
)
require google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
@@ -90,6 +90,8 @@ require (
github.com/OneOfOne/xxhash v1.2.8
github.com/adrg/xdg v0.5.3
github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
github.com/hashicorp/hcl/v2 v2.23.0
github.com/magiconair/properties v1.8.9
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
)
@@ -103,10 +105,13 @@ require (
github.com/Masterminds/semver/v3 v3.3.0 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.11.7 // indirect
github.com/ProtonMail/go-crypto v1.1.3 // indirect
github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/agext/levenshtein v1.2.1 // indirect; indirectt
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/andybalholm/brotli v1.1.1 // indirect
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aquasecurity/go-version v0.0.1 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
@@ -137,7 +142,7 @@ require (
github.com/edsrzf/mmap-go v1.1.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/fgprof v0.9.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
@@ -172,6 +177,7 @@ require (
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // 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
@@ -190,7 +196,7 @@ require (
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
@@ -206,7 +212,7 @@ require (
github.com/skeema/knownhosts v1.3.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/pflag v1.0.6 // indirect
github.com/spf13/viper v1.19.0 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/sylabs/sif/v2 v2.20.2 // indirect
@@ -223,6 +229,7 @@ require (
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/zclconf/go-cty v1.13.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
@@ -231,20 +238,21 @@ require (
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.29.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/tools v0.29.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
google.golang.org/grpc v1.67.3 // indirect
google.golang.org/protobuf v1.36.3 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
modernc.org/libc v1.55.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/libc v1.61.13 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.8.2 // indirect
)
retract (
+101 -71
View File
@@ -80,14 +80,16 @@ github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/ProtonMail/go-crypto v1.1.3 h1:nRBOetoydLeUb4nHajyO2bKqMLfWQ/ZPwkXqXxPxCFk=
github.com/ProtonMail/go-crypto v1.1.3/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -112,20 +114,24 @@ github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0v
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
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.20241018175412-5c22e6360c4f h1:dAQPIrQ3a5PBqZeZ+B9NGZsGmodk4NO9OjDIsQmQyQM=
github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
github.com/anchore/stereoscope v0.0.12 h1:ovUWeyeZGml6pTGiu/uha/rCbToANFPu+cnhLbeperY=
github.com/anchore/stereoscope v0.0.12/go.mod h1:cmb/MGya7ccOd6fZZEREuhdSH2kFALBMrkY/66Sfv1o=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
github.com/anchore/stereoscope v0.0.13 h1:9Ivkh7k+vOeG3JHrt44jOg/8UdZrCvMsSjLQ7trHBig=
github.com/anchore/stereoscope v0.0.13/go.mod h1:QfhhFc2pezp5aX/dVJ5qnBFpBUv5+KUTphwaQLxMUig=
github.com/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=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 h1:vmXNl+HDfqqXgr0uY1UgK1GAhps8nbAAtqHNBcgyf+4=
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46/go.mod h1:olhPNdiiAAMiSujemd1O/sc6GcyePr23f/6uGKtthNg=
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 h1:rcEG5HI490FF0a7zuvxOxen52ddygCfNVjP0XOCMl+M=
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU=
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/aquasecurity/go-pep440-version v0.0.1 h1:8VKKQtH2aV61+0hovZS3T//rUF+6GDn18paFTVS0h0M=
github.com/aquasecurity/go-pep440-version v0.0.1/go.mod h1:3naPe+Bp6wi3n4l5iBFCZgS0JG8vY6FT0H4NGhFJ+i4=
github.com/aquasecurity/go-version v0.0.1 h1:4cNl516agK0TCn5F7mmYN+xVs1E3S45LkgZk3cbaW2E=
github.com/aquasecurity/go-version v0.0.1/go.mod h1:s1UU6/v2hctXcOa3OLwfj5d9yoXHa3ahf+ipSwEvGT0=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
@@ -143,8 +149,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bmatcuk/doublestar/v4 v4.8.0 h1:DSXtrypQddoug1459viM9X9D3dp1Z7993fw36I2kNcQ=
github.com/bmatcuk/doublestar/v4 v4.8.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef h1:TSFnfbbu2oAOuWbeDDTtwXWE6z+PmpgbSsMBeV7l0ww=
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo=
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
@@ -166,9 +174,15 @@ github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSe
github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
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=
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
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=
@@ -208,9 +222,8 @@ github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9Fqctt
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
@@ -230,8 +243,8 @@ github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvD
github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U=
github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.0.0+incompatible h1:Olh0KS820sJ7nPsBKChVhk5pzqcwDR15fumfAd/p9hM=
github.com/docker/docker v28.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
@@ -247,8 +260,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/elazarl/goproxy v1.2.3 h1:xwIyKHbaP5yfT6O9KIeYJR5549MXRQkoQMRXGztz8YQ=
github.com/elazarl/goproxy v1.2.3/go.mod h1:YfEbZtqP4AetfO6d40vWchF3znWX7C7Vd6ZMfdL8z64=
github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY=
github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
@@ -274,8 +287,9 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -292,8 +306,8 @@ github.com/gkampitakis/ciinfo v0.3.1 h1:lzjbemlGI4Q+XimPg64ss89x8Mf3xihJqy/0Mgag
github.com/gkampitakis/ciinfo v0.3.1/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.8 h1:BB4ihcyXgJEVO/Pj/P+4bs7pFzsLcEjsfU2+mFdJh1c=
github.com/gkampitakis/go-snaps v0.5.8/go.mod h1:PcKmy8q5Se7p48ywpogN5Td13reipz1Iivah4wrTIvY=
github.com/gkampitakis/go-snaps v0.5.11 h1:LFG0ggUKR+KEiiaOvFCmLgJ5NO2zf93AxxddkBn3LdQ=
github.com/gkampitakis/go-snaps v0.5.11/go.mod h1:PcKmy8q5Se7p48ywpogN5Td13reipz1Iivah4wrTIvY=
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0=
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
@@ -304,8 +318,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.13.1 h1:DAQ9APonnlvSWpvolXWIuV6Q6zXy2wHbN4cVlNR5Q+M=
github.com/go-git/go-git/v5 v5.13.1/go.mod h1:qryJB4cSBoq3FRoBRf5A77joojuBcmPJ0qu3XXXVixc=
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
github.com/go-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=
@@ -324,6 +338,9 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-yaml v1.15.13 h1:Xd87Yddmr2rC1SLLTm2MNDcTjeO/GYo0JGiww6gSTDg=
github.com/goccy/go-yaml v1.15.13/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -406,6 +423,7 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
@@ -451,6 +469,8 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
@@ -468,6 +488,7 @@ github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
@@ -475,10 +496,11 @@ github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy77
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jedib0t/go-pretty/v6 v6.6.5 h1:9PgMJOVBedpgYLI56jQRJYqngxYAAzfEUua+3NgSqAo=
github.com/jedib0t/go-pretty/v6 v6.6.5/go.mod h1:Uq/HrbhuFty5WSVNfjpQQe47x16RwVGXIveNGEyGtHs=
github.com/jedib0t/go-pretty/v6 v6.6.6 h1:LyezkL+1SuqH2z47e5IMQkYUIcs2BD+MnpdPRiRcN0c=
github.com/jedib0t/go-pretty/v6 v6.6.6/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -513,6 +535,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
@@ -522,6 +545,7 @@ github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc8
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/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=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
@@ -559,6 +583,8 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
@@ -614,6 +640,7 @@ github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bl
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
@@ -625,8 +652,8 @@ github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -667,7 +694,6 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/saferwall/pe v1.5.6 h1:DrRLnoQFxHWJ5lJUmrH7X2L0xeUu6SUS95Dc61eW2Yc=
@@ -681,8 +707,8 @@ github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/sanity-io/litter v1.5.6 h1:hCFycYzhRnW4niFbbmR7QKdmds69PbVa/sNmEN5euSU=
github.com/sanity-io/litter v1.5.6/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg=
github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
@@ -698,7 +724,6 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@@ -720,11 +745,12 @@ github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
@@ -774,7 +800,6 @@ github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqri
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vbatts/go-mtree v0.5.4 h1:OMAb8jaCyiFA7zXj0Zc/oARcxBDBoeu2LizjB8BVJl0=
github.com/vbatts/go-mtree v0.5.4/go.mod h1:5GqJbVhm9BBiCc4K5uc/c42FPgXulHaQs4sFUEfIWMo=
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
@@ -806,6 +831,10 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0=
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 h1:V+UsotZpAVvfj3X/LMoEytoLzSiP6Lg0F7wdVyu9gGg=
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis=
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
@@ -860,8 +889,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.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
golang.org/x/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=
@@ -901,8 +930,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -946,8 +975,8 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
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.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/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=
@@ -977,8 +1006,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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/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=
@@ -1048,17 +1077,18 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/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.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/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=
@@ -1069,8 +1099,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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/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=
@@ -1137,8 +1167,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@@ -1324,28 +1354,28 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.34.5 h1:Bb6SR13/fjp15jt70CL4f18JIN7p7dnMExd+UFnF15g=
modernc.org/sqlite v1.34.5/go.mod h1:YLuNmX9NKs8wRNK2ko1LW1NGYcc9FkBO69JOt1AR9JE=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
modernc.org/memory v1.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.35.0 h1:yQps4fegMnZFdphtzlfQTCNBWtS0CZv48pRpW3RFHRw=
modernc.org/sqlite v1.35.0/go.mod h1:9cr2sicr7jIaWTBKQmAxQLfBv9LL0su4ZTEV+utt3ic=
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
+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.19"
JSONSchemaVersion = "16.0.22"
)
+1 -1
View File
@@ -12,7 +12,7 @@ import (
// CloseAndLogError closes the given io.Closer and reports any errors found as a warning in the log
func CloseAndLogError(closer io.Closer, location string) {
if err := closer.Close(); err != nil {
log.Warnf("unable to close file for location=%q: %+v", location, err)
log.Debugf("unable to close file for location=%q: %+v", location, err)
}
}
+2 -2
View File
@@ -10,9 +10,9 @@ func SetContextLicenseScanner(ctx context.Context, s Scanner) context.Context {
return context.WithValue(ctx, licenseScannerKey{}, s)
}
func ContextLicenseScanner(ctx context.Context) Scanner {
func ContextLicenseScanner(ctx context.Context) (Scanner, error) {
if s, ok := ctx.Value(licenseScannerKey{}).(Scanner); ok {
return s
return s, nil
}
return NewDefaultScanner()
}
+58 -41
View File
@@ -2,67 +2,84 @@ package licenses
import (
"context"
"fmt"
"io"
"github.com/google/licensecheck"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
const coverageThreshold = 75 // determined by experimentation
const (
DefaultCoverageThreshold = 75 // determined by experimentation
DefaultIncludeLicenseContent = false
)
type Scanner interface {
IdentifyLicenseIDs(context.Context, io.Reader) ([]string, error)
IdentifyLicenseIDs(context.Context, io.Reader) ([]string, []byte, error)
FileSearch(context.Context, file.LocationReadCloser) ([]file.License, error)
PkgSearch(context.Context, file.LocationReadCloser) ([]pkg.License, error)
}
var _ Scanner = (*scanner)(nil)
type scanner struct {
coverageThreshold float64 // between 0 and 100
scanner func([]byte) licensecheck.Coverage
coverageThreshold float64 // between 0 and 100
includeLicenseContent bool
scanner func([]byte) licensecheck.Coverage
}
type ScannerConfig struct {
CoverageThreshold float64
IncludeLicenseContent bool
Scanner func([]byte) licensecheck.Coverage
}
type Option func(*scanner)
func WithCoverage(coverage float64) Option {
return func(s *scanner) {
s.coverageThreshold = coverage
}
}
func WithIncludeLicenseContent(includeLicenseContent bool) Option {
return func(s *scanner) {
s.includeLicenseContent = includeLicenseContent
}
}
// NewDefaultScanner returns a scanner that uses a new instance of the default licensecheck package scanner.
func NewDefaultScanner() Scanner {
func NewDefaultScanner(o ...Option) (Scanner, error) {
s, err := licensecheck.NewScanner(licensecheck.BuiltinLicenses())
if err != nil {
log.WithFields("error", err).Trace("unable to create default license scanner")
s = nil
return nil, fmt.Errorf("unable to create default license scanner: %w", err)
}
newScanner := &scanner{
coverageThreshold: DefaultCoverageThreshold,
includeLicenseContent: DefaultIncludeLicenseContent,
scanner: s.Scan,
}
for _, opt := range o {
opt(newScanner)
}
return newScanner, nil
}
// NewScanner generates a license Scanner with the given ScannerConfig
// if config is nil NewDefaultScanner is used
func NewScanner(c *ScannerConfig) (Scanner, error) {
if c == nil {
return NewDefaultScanner()
}
return &scanner{
coverageThreshold: coverageThreshold,
scanner: s.Scan,
}
}
// TestingOnlyScanner returns a scanner that uses the built-in license scanner from the licensecheck package.
// THIS IS ONLY MEANT FOR TEST CODE, NOT PRODUCTION CODE.
func TestingOnlyScanner() Scanner {
return &scanner{
coverageThreshold: coverageThreshold,
scanner: licensecheck.Scan,
}
}
func (s scanner) IdentifyLicenseIDs(_ context.Context, reader io.Reader) ([]string, error) {
if s.scanner == nil {
return nil, nil
}
content, err := io.ReadAll(reader)
if err != nil {
return nil, err
}
cov := s.scanner(content)
if cov.Percent < s.coverageThreshold {
// unknown or no licenses here?
return nil, nil
}
var ids []string
for _, m := range cov.Match {
ids = append(ids, m.ID)
}
return ids, nil
coverageThreshold: c.CoverageThreshold,
includeLicenseContent: c.IncludeLicenseContent,
scanner: c.Scanner,
}, nil
}
+84
View File
@@ -0,0 +1,84 @@
package licenses
import (
"bytes"
"context"
"os"
"testing"
"github.com/google/licensecheck"
"github.com/stretchr/testify/require"
)
func TestIdentifyLicenseIDs(t *testing.T) {
type expectation struct {
yieldError bool
ids []string
content []byte
}
tests := []struct {
name string
in string
expected expectation
}{
{
name: "apache license 2.0",
in: `test-fixtures/apache-license-2.0`,
expected: expectation{
yieldError: false,
ids: []string{"Apache-2.0"},
content: nil,
},
},
{
name: "custom license includes content for IdentifyLicenseIDs",
in: "test-fixtures/nvidia-software-and-cuda-supplement",
expected: expectation{
yieldError: false,
ids: []string{},
content: mustOpen("test-fixtures/nvidia-software-and-cuda-supplement"),
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
content, err := os.ReadFile(test.in)
require.NoError(t, err)
ids, content, err := testScanner(false).IdentifyLicenseIDs(context.TODO(), bytes.NewReader(content))
if test.expected.yieldError {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Len(t, ids, len(test.expected.ids))
require.Len(t, content, len(test.expected.content))
if len(test.expected.ids) > 0 {
require.Equal(t, ids, test.expected.ids)
}
if len(test.expected.content) > 0 {
require.Equal(t, content, test.expected.content)
}
}
})
}
}
func testScanner(includeLicenseContent bool) Scanner {
return &scanner{
coverageThreshold: DefaultCoverageThreshold,
includeLicenseContent: includeLicenseContent,
scanner: licensecheck.Scan,
}
}
func mustOpen(fixture string) []byte {
content, err := os.ReadFile(fixture)
if err != nil {
panic(err)
}
return content
}
+101 -6
View File
@@ -2,24 +2,119 @@ package licenses
import (
"context"
"crypto/sha256"
"fmt"
"io"
"strings"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
)
// Search scans the contents of a license file to attempt to determine the type of license it is
func Search(ctx context.Context, scanner Scanner, reader file.LocationReadCloser) (licenses []pkg.License, err error) {
const (
unknownLicenseType = "UNKNOWN"
UnknownLicensePrefix = unknownLicenseType + "_"
)
func getCustomLicenseContentHash(contents []byte) string {
hash := sha256.Sum256(contents)
return fmt.Sprintf("%x", hash[:])
}
func (s *scanner) IdentifyLicenseIDs(_ context.Context, reader io.Reader) ([]string, []byte, error) {
if s.scanner == nil {
return nil, nil, nil
}
content, err := io.ReadAll(reader)
if err != nil {
return nil, nil, err
}
cov := s.scanner(content)
if cov.Percent < s.coverageThreshold {
// unknown or no licenses here
// => check return content to Search to process
return nil, content, nil
}
var ids []string
for _, m := range cov.Match {
ids = append(ids, m.ID)
}
return ids, nil, nil
}
// PkgSearch scans the contents of a license file to attempt to determine the type of license it is
func (s *scanner) PkgSearch(ctx context.Context, reader file.LocationReadCloser) (licenses []pkg.License, err error) {
licenses = make([]pkg.License, 0)
ids, err := scanner.IdentifyLicenseIDs(ctx, reader)
ids, content, err := s.IdentifyLicenseIDs(ctx, reader)
if err != nil {
return nil, err
}
for _, id := range ids {
lic := pkg.NewLicenseFromLocations(id, reader.Location)
lic.Type = license.Concluded
// IdentifyLicenseIDs can only return a list of ID or content
// These return values are mutually exclusive.
// If the scanner threshold for matching scores < 75% then we return the license full content
if len(ids) > 0 {
for _, id := range ids {
lic := pkg.NewLicenseFromLocations(id, reader.Location)
lic.Type = license.Concluded
licenses = append(licenses, lic)
}
} else if len(content) > 0 {
// harmonize line endings to unix compatible first:
// 1. \r\n => \n (Windows => UNIX)
// 2. \r => \n (Macintosh => UNIX)
content = []byte(strings.ReplaceAll(strings.ReplaceAll(string(content), "\r\n", "\n"), "\r", "\n"))
lic := pkg.NewLicenseFromLocations(unknownLicenseType, reader.Location)
lic.SPDXExpression = UnknownLicensePrefix + getCustomLicenseContentHash(content)
if s.includeLicenseContent {
lic.Contents = string(content)
}
lic.Type = license.Declared
licenses = append(licenses, lic)
}
return licenses, nil
}
// FileSearch scans the contents of a license file to attempt to determine the type of license it is
func (s *scanner) FileSearch(ctx context.Context, reader file.LocationReadCloser) (licenses []file.License, err error) {
licenses = make([]file.License, 0)
ids, content, err := s.IdentifyLicenseIDs(ctx, reader)
if err != nil {
return nil, err
}
// IdentifyLicenseIDs can only return a list of ID or content
// These return values are mutually exclusive.
// If the scanner threshold for matching scores < 75% then we return the license full content
if len(ids) > 0 {
for _, id := range ids {
lic := file.NewLicense(id)
lic.Type = license.Concluded
licenses = append(licenses, lic)
}
} else if len(content) > 0 {
// harmonize line endings to unix compatible first:
// 1. \r\n => \n (Windows => UNIX)
// 2. \r => \n (Macintosh => UNIX)
content = []byte(strings.ReplaceAll(strings.ReplaceAll(string(content), "\r\n", "\n"), "\r", "\n"))
lic := file.NewLicense(unknownLicenseType)
lic.SPDXExpression = UnknownLicensePrefix + getCustomLicenseContentHash(content)
if s.includeLicenseContent {
lic.Contents = string(content)
}
lic.Type = license.Declared
licenses = append(licenses, lic)
}
+166
View File
@@ -0,0 +1,166 @@
package licenses
import (
"bytes"
"context"
"io"
"os"
"testing"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
type bytesReadCloser struct {
bytes.Buffer
}
func (brc *bytesReadCloser) Close() error {
return nil
}
func newBytesReadCloser(data []byte) *bytesReadCloser {
return &bytesReadCloser{
Buffer: *bytes.NewBuffer(data),
}
}
func TestSearchFileLicenses(t *testing.T) {
type expectation struct {
yieldError bool
licenses []file.License
}
tests := []struct {
name string
in string
includeUnkownLicenseContent bool
expected expectation
}{
{
name: "apache license 2.0",
in: "test-fixtures/apache-license-2.0",
expected: expectation{
yieldError: false,
licenses: []file.License{
{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: "concluded",
},
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := context.TODO()
content, err := os.ReadFile(test.in)
require.NoError(t, err)
s := testScanner(false)
result, err := s.FileSearch(ctx, file.NewLocationReadCloser(file.NewLocation("LICENSE"), io.NopCloser(bytes.NewReader(content))))
if test.expected.yieldError {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Len(t, result, len(test.expected.licenses))
if len(test.expected.licenses) > 0 {
require.Equal(t, test.expected.licenses, result)
}
}
})
}
}
func TestSearchPkgLicenses(t *testing.T) {
type expectation struct {
wantErr require.ErrorAssertionFunc
licenses []pkg.License
}
testLocation := file.NewLocation("LICENSE")
tests := []struct {
name string
in string
includeUnkownLicenseContent bool
expected expectation
}{
{
name: "apache license 2.0",
in: "test-fixtures/apache-license-2.0",
expected: expectation{
licenses: []pkg.License{
{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: "concluded",
URLs: nil,
Locations: file.NewLocationSet(testLocation),
Contents: "",
},
},
wantErr: nil,
},
},
{
name: "custom license no content by default",
in: "test-fixtures/nvidia-software-and-cuda-supplement",
expected: expectation{
licenses: []pkg.License{
{
Value: "UNKNOWN",
SPDXExpression: "UNKNOWN_eebcea3ab1d1a28e671de90119ffcfb35fe86951e4af1b17af52b7a82fcf7d0a",
Type: "declared",
URLs: nil,
Locations: file.NewLocationSet(testLocation),
Contents: "",
},
},
wantErr: nil,
},
},
{
name: "custom license with content when scanner has content config",
in: "test-fixtures/nvidia-software-and-cuda-supplement",
includeUnkownLicenseContent: true,
expected: expectation{
licenses: []pkg.License{
{
Value: "UNKNOWN",
SPDXExpression: "UNKNOWN_eebcea3ab1d1a28e671de90119ffcfb35fe86951e4af1b17af52b7a82fcf7d0a",
Type: "declared",
URLs: nil,
Locations: file.NewLocationSet(testLocation),
Contents: string(mustOpen("test-fixtures/nvidia-software-and-cuda-supplement")),
},
},
wantErr: nil,
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
ctx := context.TODO()
content, err := os.ReadFile(test.in)
require.NoError(t, err)
s := testScanner(test.includeUnkownLicenseContent)
result, err := s.PkgSearch(ctx, file.NewLocationReadCloser(file.NewLocation("LICENSE"), io.NopCloser(bytes.NewReader(content))))
if test.expected.wantErr != nil {
test.expected.wantErr(t, err)
}
require.NoError(t, err)
require.Len(t, result, len(test.expected.licenses))
if len(test.expected.licenses) > 0 {
require.Equal(t, test.expected.licenses, result)
}
})
}
}
@@ -0,0 +1,169 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
@@ -0,0 +1,5 @@
End User License Agreement
--------------------------
NVIDIA Software License Agreement and CUDA Supplement to
Software License Agreement. Last updated: October 8, 2021
@@ -58,11 +58,10 @@ func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, i
// PackagesToRemove returns a list of binary packages (resolved by the ELF cataloger) that should be removed from the SBOM
// These packages are removed because they are already represented by a higher order packages in the SBOM.
func PackagesToRemove(resolver file.Resolver, accessor sbomsync.Accessor) []artifact.ID {
func PackagesToRemove(accessor sbomsync.Accessor) []artifact.ID {
pkgsToDelete := make([]artifact.ID, 0)
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
// OTHER package type > ELF package type > Binary package type
pkgsToDelete = append(pkgsToDelete, getBinaryPackagesToDelete(resolver, s)...)
// ELF package type > Binary package type
pkgsToDelete = append(pkgsToDelete, compareElfBinaryPackages(s)...)
})
return pkgsToDelete
@@ -114,33 +113,6 @@ func isElfPackage(p pkg.Package) bool {
return ok
}
func getBinaryPackagesToDelete(resolver file.Resolver, s *sbom.SBOM) []artifact.ID {
pkgsToDelete := make([]artifact.ID, 0)
for p := range s.Artifacts.Packages.Enumerate() {
if p.Type == pkg.BinaryPkg {
continue
}
fileOwner, ok := p.Metadata.(pkg.FileOwner)
if !ok {
continue
}
ownedFiles := fileOwner.OwnedFiles()
locations, err := resolver.FilesByPath(ownedFiles...)
if err != nil {
log.WithFields("error", err).Trace("unable to find path for owned file")
continue
}
for _, loc := range locations {
for _, pathPkg := range s.Artifacts.Packages.PackagesByPath(loc.RealPath) {
if pathPkg.Type == pkg.BinaryPkg {
pkgsToDelete = append(pkgsToDelete, pathPkg.ID())
}
}
}
}
return pkgsToDelete
}
func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver file.Resolver, addRelationship func(artifact.Relationship), index *sharedLibraryIndex) {
for _, libReference := range exec.ImportedLibraries {
// for each library reference, check s.Artifacts.Packages.Sorted(pkg.BinaryPkg) for a binary package that represents that library
@@ -21,7 +21,7 @@ func TestPackagesToRemove(t *testing.T) {
Name: "glibc",
Version: "2.28-236.el8_9.12",
Locations: file.NewLocationSet(
file.NewLocation(glibcCoordinate.RealPath),
file.NewLocation("path/to/rpmdb"),
),
Type: pkg.RpmPkg,
Metadata: pkg.RpmDBEntry{
@@ -88,50 +88,45 @@ func TestPackagesToRemove(t *testing.T) {
tests := []struct {
name string
resolver file.Resolver
accessor sbomsync.Accessor
want []artifact.ID
}{
{
name: "remove packages that are overlapping rpm --> binary",
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
accessor: newAccessor([]pkg.Package{glibCPackage, glibCBinaryELFPackage}, map[file.Coordinates]file.Executable{}, nil),
want: []artifact.ID{glibCBinaryELFPackage.ID()},
// this is surprising, right? the calling function reasons about if any generic binary package (regardless of it being an ELF package or not)
// should be deleted or kept based on the user configuration to do so.
want: []artifact.ID{},
},
{
name: "keep packages that are overlapping rpm --> binary when the binary self identifies as an RPM",
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
accessor: newAccessor([]pkg.Package{glibCPackage, glibCBinaryELFPackageAsRPM}, map[file.Coordinates]file.Executable{}, nil),
want: []artifact.ID{},
},
{
name: "remove no packages when there is a single binary package (or self identifying RPM)",
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
accessor: newAccessor([]pkg.Package{glibCBinaryELFPackage, glibCBinaryELFPackageAsRPM}, map[file.Coordinates]file.Executable{}, nil),
want: []artifact.ID{},
},
{
name: "remove packages when there is a single binary package and a classifier package",
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
accessor: newAccessor([]pkg.Package{glibCBinaryELFPackage, glibCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
want: []artifact.ID{glibCBinaryClassifierPackage.ID()},
},
{
name: "ensure we're considering ELF packages, not just binary packages (supporting evidence)",
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
accessor: newAccessor([]pkg.Package{glibCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
want: []artifact.ID{},
},
{
name: "ensure we're considering ELF packages, not just binary packages (primary evidence)",
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
accessor: newAccessor([]pkg.Package{libCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
want: []artifact.ID{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pkgsToDelete := PackagesToRemove(tt.resolver, tt.accessor)
pkgsToDelete := PackagesToRemove(tt.accessor)
if diff := cmp.Diff(tt.want, pkgsToDelete); diff != "" {
t.Errorf("unexpected packages to delete (-want, +got): %s", diff)
}
@@ -22,6 +22,9 @@ var (
binaryCatalogerTypes = []pkg.Type{
pkg.BinaryPkg,
}
bitnamiCatalogerTypes = []pkg.Type{
pkg.BitnamiPkg,
}
binaryMetadataTypes = []string{
reflect.TypeOf(pkg.ELFBinaryPackageNoteJSONPayload{}).Name(),
reflect.TypeOf(pkg.BinarySignature{}).Name(),
@@ -65,6 +68,10 @@ func excludeByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) a
return idToRemove
}
if idToRemove := identifyOverlappingBitnamiRelationship(parent, child); idToRemove != "" {
return idToRemove
}
return ""
}
@@ -124,3 +131,24 @@ func identifyOverlappingOSRelationship(parent *pkg.Package, child *pkg.Package)
return child.ID()
}
// identifyOverlappingBitnamiRelationship indicates the package ID to remove if this is a Bitnami pkg -> bin pkg relationship.
func identifyOverlappingBitnamiRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID {
if !slices.Contains(bitnamiCatalogerTypes, parent.Type) {
return ""
}
if slices.Contains(binaryCatalogerTypes, child.Type) {
return child.ID()
}
if child.Metadata == nil {
return ""
}
if !slices.Contains(binaryMetadataTypes, reflect.TypeOf(child.Metadata).Name()) {
return ""
}
return child.ID()
}
@@ -13,7 +13,8 @@ func TestExcludeByFileOwnershipOverlap(t *testing.T) {
packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg}
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg, Metadata: pkg.JavaVMInstallation{}}
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{Type: "rpm"}}
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC} {
packageD := pkg.Package{Name: "package-d", Type: pkg.BitnamiPkg}
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD} {
p := p
p.SetID()
}
@@ -46,6 +47,17 @@ func TestExcludeByFileOwnershipOverlap(t *testing.T) {
packages: pkg.NewCollection(packageC, packageB),
shouldExclude: true,
},
{
// prove that Bitnami -> bin exclusions are wired
name: "exclusions from bitnami -> binary",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageD, // Bitnami
To: packageC, // ELF binary
},
packages: pkg.NewCollection(packageD, packageC),
shouldExclude: true,
},
}
for _, test := range tests {
@@ -117,6 +129,63 @@ func TestIdentifyOverlappingOSRelationship(t *testing.T) {
}
}
func TestIdentifyOverlappingBitnamiRelationship(t *testing.T) {
packageA := pkg.Package{Name: "package-a", Type: pkg.BitnamiPkg} // Bitnami package
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg}
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}}
packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg} // Language package
packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{}}
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} {
p.SetID()
}
tests := []struct {
name string
parent *pkg.Package
child *pkg.Package
expectedID artifact.ID
}{
{
name: "Bitnami -> binary without metadata",
parent: &packageA,
child: &packageB,
expectedID: packageB.ID(), // Bitnami package to binary package, should return child ID
},
{
name: "Bitnami -> binary with binary metadata",
parent: &packageA,
child: &packageC,
expectedID: packageC.ID(), // Bitnami package to binary package with binary metadata, should return child ID
},
{
name: "Bitnami -> non-binary package",
parent: &packageA,
child: &packageD,
expectedID: "", // Bitnami package to non-binary package, no exclusion
},
{
name: "Bitnami -> binary with ELF metadata",
parent: &packageA,
child: &packageE,
expectedID: packageE.ID(), // Bitnami package to binary package with ELF metadata, should return child ID
},
{
name: "non-Bitnami parent",
parent: &packageD, // non-Bitnami package
child: &packageC,
expectedID: "", // non-Bitnami parent, no exclusion
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
resultID := identifyOverlappingBitnamiRelationship(tt.parent, tt.child)
assert.Equal(t, tt.expectedID, resultID)
})
}
}
func TestIdentifyOverlappingJVMRelationship(t *testing.T) {
packageA := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg}
+1 -1
View File
@@ -1,5 +1,5 @@
// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at 2024-08-20 11:33:49.349625 -0400 EDT m=+0.383911876
// This file was generated by robots at 2024-11-29 09:22:05.594787 +0100 CET m=+2.414400043
// using data from https://spdx.org/licenses/licenses.json
package spdxlicense
+29
View File
@@ -0,0 +1,29 @@
package task
import (
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cataloging/filecataloging"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
)
type CatalogingFactoryConfig struct {
ComplianceConfig cataloging.ComplianceConfig
SearchConfig cataloging.SearchConfig
RelationshipsConfig cataloging.RelationshipsConfig
DataGenerationConfig cataloging.DataGenerationConfig
LicenseConfig cataloging.LicenseConfig
PackagesConfig pkgcataloging.Config
FilesConfig filecataloging.Config
}
func DefaultCatalogingFactoryConfig() CatalogingFactoryConfig {
return CatalogingFactoryConfig{
ComplianceConfig: cataloging.DefaultComplianceConfig(),
SearchConfig: cataloging.DefaultSearchConfig(),
RelationshipsConfig: cataloging.DefaultRelationshipsConfig(),
DataGenerationConfig: cataloging.DefaultDataGenerationConfig(),
LicenseConfig: cataloging.DefaultLicenseConfig(),
PackagesConfig: pkgcataloging.DefaultConfig(),
FilesConfig: filecataloging.DefaultConfig(),
}
}
+2 -2
View File
@@ -8,7 +8,7 @@ import (
"github.com/hashicorp/go-multierror"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/cataloging"
)
var expressionNodePattern = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9-+]*)+$`)
@@ -142,7 +142,7 @@ func (ec expressionContext) newExpression(exp string, operation Operation, token
}
}
func newExpressionsFromSelectionRequest(nc *expressionContext, selectionRequest pkgcataloging.SelectionRequest) Expressions {
func newExpressionsFromSelectionRequest(nc *expressionContext, selectionRequest cataloging.SelectionRequest) Expressions {
var all Expressions
for _, exp := range selectionRequest.DefaultNamesOrTags {
+2 -2
View File
@@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/cataloging"
)
func Test_newExpressionsFromSelectionRequest(t *testing.T) {
@@ -135,7 +135,7 @@ func Test_newExpressionsFromSelectionRequest(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
req := pkgcataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
req := cataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
result := newExpressionsFromSelectionRequest(nc, req)
if tt.expectedErrors != nil {
+40
View File
@@ -0,0 +1,40 @@
package task
import (
"fmt"
"sort"
"strings"
"github.com/scylladb/go-set/strset"
)
type factory func(cfg CatalogingFactoryConfig) Task
type Factories []factory
func (f Factories) Tasks(cfg CatalogingFactoryConfig) ([]Task, error) {
var allTasks []Task
taskNames := strset.New()
duplicateTaskNames := strset.New()
var err error
for _, fact := range f {
tsk := fact(cfg)
if tsk == nil {
continue
}
tskName := tsk.Name()
if taskNames.Has(tskName) {
duplicateTaskNames.Add(tskName)
}
allTasks = append(allTasks, tsk)
taskNames.Add(tskName)
}
if duplicateTaskNames.Size() > 0 {
names := duplicateTaskNames.List()
sort.Strings(names)
err = fmt.Errorf("duplicate cataloger task names: %v", strings.Join(names, ", "))
}
return allTasks, err
}
+58 -27
View File
@@ -6,6 +6,7 @@ import (
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cataloging/filecataloging"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/file/cataloger/executable"
"github.com/anchore/syft/syft/file/cataloger/filecontent"
@@ -15,14 +16,27 @@ import (
"github.com/anchore/syft/syft/sbom"
)
func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash) Task {
if selection == file.NoFilesSelection || len(hashers) == 0 {
return nil
func DefaultFileTaskFactories() Factories {
return Factories{
newFileDigestCatalogerTaskFactory("digest"),
newFileMetadataCatalogerTaskFactory("file-metadata"),
newFileContentCatalogerTaskFactory("content"),
newExecutableCatalogerTaskFactory("binary-metadata"),
}
}
digestsCataloger := filedigest.NewCataloger(hashers)
func newFileDigestCatalogerTaskFactory(tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return newFileDigestCatalogerTask(cfg.FilesConfig.Selection, cfg.FilesConfig.Hashers, tags...)
}
}
func newFileDigestCatalogerTask(selection file.Selection, hashers []crypto.Hash, tags ...string) Task {
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
if selection == file.NoFilesSelection || len(hashers) == 0 {
return nil
}
accessor := builder.(sbomsync.Accessor)
coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor))
@@ -30,7 +44,7 @@ func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash
return nil
}
result, err := digestsCataloger.Catalog(ctx, resolver, coordinates...)
result, err := filedigest.NewCataloger(hashers).Catalog(ctx, resolver, coordinates...)
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.FileDigests = result
@@ -39,17 +53,21 @@ func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash
return err
}
return NewTask("file-digest-cataloger", fn)
return NewTask("file-digest-cataloger", fn, commonFileTags(tags)...)
}
func NewFileMetadataCatalogerTask(selection file.Selection) Task {
if selection == file.NoFilesSelection {
return nil
func newFileMetadataCatalogerTaskFactory(tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return newFileMetadataCatalogerTask(cfg.FilesConfig.Selection, tags...)
}
}
metadataCataloger := filemetadata.NewCataloger()
func newFileMetadataCatalogerTask(selection file.Selection, tags ...string) Task {
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
if selection == file.NoFilesSelection {
return nil
}
accessor := builder.(sbomsync.Accessor)
coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor))
@@ -57,7 +75,7 @@ func NewFileMetadataCatalogerTask(selection file.Selection) Task {
return nil
}
result, err := metadataCataloger.Catalog(ctx, resolver, coordinates...)
result, err := filemetadata.NewCataloger().Catalog(ctx, resolver, coordinates...)
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.FileMetadata = result
@@ -66,20 +84,24 @@ func NewFileMetadataCatalogerTask(selection file.Selection) Task {
return err
}
return NewTask("file-metadata-cataloger", fn)
return NewTask("file-metadata-cataloger", fn, commonFileTags(tags)...)
}
func NewFileContentCatalogerTask(cfg filecontent.Config) Task {
if len(cfg.Globs) == 0 {
return nil
func newFileContentCatalogerTaskFactory(tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return newFileContentCatalogerTask(cfg.FilesConfig.Content, tags...)
}
}
cat := filecontent.NewCataloger(cfg)
func newFileContentCatalogerTask(cfg filecontent.Config, tags ...string) Task {
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
if len(cfg.Globs) == 0 {
return nil
}
accessor := builder.(sbomsync.Accessor)
result, err := cat.Catalog(ctx, resolver)
result, err := filecontent.NewCataloger(cfg).Catalog(ctx, resolver)
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.FileContents = result
@@ -88,20 +110,24 @@ func NewFileContentCatalogerTask(cfg filecontent.Config) Task {
return err
}
return NewTask("file-content-cataloger", fn)
return NewTask("file-content-cataloger", fn, commonFileTags(tags)...)
}
func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config) Task {
if selection == file.NoFilesSelection {
return nil
func newExecutableCatalogerTaskFactory(tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return newExecutableCatalogerTask(cfg.FilesConfig.Selection, cfg.FilesConfig.Executable, tags...)
}
}
cat := executable.NewCataloger(cfg)
func newExecutableCatalogerTask(selection file.Selection, cfg executable.Config, tags ...string) Task {
fn := func(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
if selection == file.NoFilesSelection {
return nil
}
accessor := builder.(sbomsync.Accessor)
result, err := cat.Catalog(resolver)
result, err := executable.NewCataloger(cfg).Catalog(resolver)
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
sbom.Artifacts.Executables = result
@@ -110,7 +136,7 @@ func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config)
return err
}
return NewTask("file-executable-cataloger", fn)
return NewTask("file-executable-cataloger", fn, commonFileTags(tags)...)
}
// TODO: this should be replaced with a fix that allows passing a coordinate or location iterator to the cataloger
@@ -154,3 +180,8 @@ func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accesso
return nil, false
}
func commonFileTags(tags []string) []string {
tags = append(tags, filecataloging.FileTag)
return tags
}
+4 -56
View File
@@ -3,12 +3,9 @@ package task
import (
"context"
"fmt"
"sort"
"strings"
"unicode"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/relationship"
@@ -23,67 +20,18 @@ import (
cpeutils "github.com/anchore/syft/syft/pkg/cataloger/common/cpe"
)
type packageTaskFactory func(cfg CatalogingFactoryConfig) Task
type PackageTaskFactories []packageTaskFactory
type CatalogingFactoryConfig struct {
ComplianceConfig cataloging.ComplianceConfig
SearchConfig cataloging.SearchConfig
RelationshipsConfig cataloging.RelationshipsConfig
DataGenerationConfig cataloging.DataGenerationConfig
PackagesConfig pkgcataloging.Config
}
func DefaultCatalogingFactoryConfig() CatalogingFactoryConfig {
return CatalogingFactoryConfig{
ComplianceConfig: cataloging.DefaultComplianceConfig(),
SearchConfig: cataloging.DefaultSearchConfig(),
RelationshipsConfig: cataloging.DefaultRelationshipsConfig(),
DataGenerationConfig: cataloging.DefaultDataGenerationConfig(),
PackagesConfig: pkgcataloging.DefaultConfig(),
}
}
func newPackageTaskFactory(catalogerFactory func(CatalogingFactoryConfig) pkg.Cataloger, tags ...string) packageTaskFactory {
func newPackageTaskFactory(catalogerFactory func(CatalogingFactoryConfig) pkg.Cataloger, tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return NewPackageTask(cfg, catalogerFactory(cfg), tags...)
}
}
func newSimplePackageTaskFactory(catalogerFactory func() pkg.Cataloger, tags ...string) packageTaskFactory {
func newSimplePackageTaskFactory(catalogerFactory func() pkg.Cataloger, tags ...string) factory {
return func(cfg CatalogingFactoryConfig) Task {
return NewPackageTask(cfg, catalogerFactory(), tags...)
}
}
func (f PackageTaskFactories) Tasks(cfg CatalogingFactoryConfig) ([]Task, error) {
var allTasks []Task
taskNames := strset.New()
duplicateTaskNames := strset.New()
var err error
for _, factory := range f {
tsk := factory(cfg)
if tsk == nil {
continue
}
tskName := tsk.Name()
if taskNames.Has(tskName) {
duplicateTaskNames.Add(tskName)
}
allTasks = append(allTasks, tsk)
taskNames.Add(tskName)
}
if duplicateTaskNames.Size() > 0 {
names := duplicateTaskNames.List()
sort.Strings(names)
err = fmt.Errorf("duplicate cataloger task names: %v", strings.Join(names, ", "))
}
return allTasks, err
}
// NewPackageTask creates a Task function for a generic pkg.Cataloger, honoring the common configuration options.
func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string) Task {
fn := func(ctx context.Context, resolver file.Resolver, sbom sbomsync.Builder) error {
@@ -153,7 +101,7 @@ func finalizePkgCatalogerResults(cfg CatalogingFactoryConfig, resolver file.Path
// create file-to-package relationships for files owned by the package
owningRelationships, err := packageFileOwnershipRelationships(p, resolver)
if err != nil {
log.Warnf("unable to create any package-file relationships for package name=%q type=%q: %v", p.Name, p.Type, err)
log.Debugf("unable to create any package-file relationships for package name=%q type=%q: %v", p.Name, p.Type, err)
} else {
relationships = append(relationships, owningRelationships...)
}
@@ -226,7 +174,7 @@ func applyComplianceRules(p *pkg.Package, cfg cataloging.ComplianceConfig) (bool
return true
case cataloging.ComplianceActionKeep:
log.WithFields("pkg", p.String(), "location", loc).Tracef("package with missing %s, taking no action", fieldName)
log.WithFields("pkg", p.String(), "location", loc, "field", fieldName).Trace("package with missing field, taking no action")
}
return false
}
+10 -3
View File
@@ -6,6 +6,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/alpine"
"github.com/anchore/syft/syft/pkg/cataloger/arch"
"github.com/anchore/syft/syft/pkg/cataloger/binary"
bitnamiSbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/bitnami"
"github.com/anchore/syft/syft/pkg/cataloger/cpp"
"github.com/anchore/syft/syft/pkg/cataloger/dart"
"github.com/anchore/syft/syft/pkg/cataloger/debian"
@@ -31,6 +32,7 @@ import (
sbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/sbom"
"github.com/anchore/syft/syft/pkg/cataloger/swift"
"github.com/anchore/syft/syft/pkg/cataloger/swipl"
"github.com/anchore/syft/syft/pkg/cataloger/terraform"
"github.com/anchore/syft/syft/pkg/cataloger/wordpress"
)
@@ -50,8 +52,8 @@ const (
)
//nolint:funlen
func DefaultPackageTaskFactories() PackageTaskFactories {
return []packageTaskFactory{
func DefaultPackageTaskFactories() Factories {
return []factory{
// OS package installed catalogers ///////////////////////////////////////////////////////////////////////////
newSimplePackageTaskFactory(arch.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "alpm", "archlinux"),
newSimplePackageTaskFactory(alpine.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "apk", "alpine"),
@@ -115,7 +117,10 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
// language-specific package for both image and directory scans (but not necessarily declared) ////////////////////////////////////////
newSimplePackageTaskFactory(dotnet.NewDotnetPackagesLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.ImageTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "dotnet", "c#"),
newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "dotnet", "c#", "binary"),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return dotnet.NewDotnetPortableExecutableCataloger(cfg.PackagesConfig.Dotnet)
}, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "dotnet", "c#", "binary"),
newSimplePackageTaskFactory(python.NewInstalledPackageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "python"),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
@@ -151,6 +156,8 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "linux", "kernel",
),
newSimplePackageTaskFactory(sbomCataloger.NewCataloger, "sbom"), // note: not evidence of installed packages
newSimplePackageTaskFactory(bitnamiSbomCataloger.NewCataloger, "bitnami", pkgcataloging.InstalledTag, pkgcataloging.ImageTag),
newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"),
newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"),
}
}
+1 -1
View File
@@ -42,7 +42,7 @@ func finalizeRelationships(resolver file.Resolver, builder sbomsync.Builder, cfg
// remove ELF packages and Binary packages that are already
// represented by a source package (e.g. a package that is evident by some package manager)
builder.DeletePackages(binary.PackagesToRemove(resolver, accessor)...)
builder.DeletePackages(binary.PackagesToRemove(accessor)...)
// add relationships showing packages that are evident by a file which is owned by another package (package-to-package)
if cfg.PackageFileOwnershipOverlap {
+152 -3
View File
@@ -3,17 +3,19 @@ package task
import (
"fmt"
"sort"
"strings"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/cataloging/filecataloging"
)
// Selection represents the users request for a subset of tasks to run and the resulting set of task names that were
// selected. Additionally, all tokens that were matched on to reach the returned conclusion are also provided.
type Selection struct {
Request pkgcataloging.SelectionRequest
Request cataloging.SelectionRequest
Result *strset.Set
TokensByTask map[string]TokenSelection
}
@@ -52,7 +54,18 @@ func newSelection() Selection {
// Select parses the given expressions as two sets: expressions that represent a "set" operation, and expressions that
// represent all other operations. The parsed expressions are then evaluated against the given tasks to return
// a subset (or the same) set of tasks.
func Select(allTasks []Task, selectionRequest pkgcataloging.SelectionRequest) ([]Task, Selection, error) {
func Select(allTasks []Task, selectionRequest cataloging.SelectionRequest) ([]Task, Selection, error) {
ensureDefaultSelectionHasFiles(&selectionRequest, allTasks)
return _select(allTasks, selectionRequest)
}
func _select(allTasks []Task, selectionRequest cataloging.SelectionRequest) ([]Task, Selection, error) {
if selectionRequest.IsEmpty() {
selection := newSelection()
selection.Request = selectionRequest
return nil, selection, nil
}
nodes := newExpressionsFromSelectionRequest(newExpressionContext(allTasks), selectionRequest)
finalTasks, selection := selectByExpressions(allTasks, nodes)
@@ -62,6 +75,142 @@ func Select(allTasks []Task, selectionRequest pkgcataloging.SelectionRequest) ([
return finalTasks, selection, nodes.Validate()
}
// ensureDefaultSelectionHasFiles ensures that the default selection request has the "file" tag, as this is a required
// for backwards compatibility (when catalogers were only for packages and not for separate groups of tasks).
func ensureDefaultSelectionHasFiles(selectionRequest *cataloging.SelectionRequest, allTasks ...[]Task) {
for _, ts := range allTasks {
_, leftOver := tagsOrNamesThatTaskGroupRespondsTo(ts, strset.New(filecataloging.FileTag))
if leftOver.Has(filecataloging.FileTag) {
// the given set of tasks do not respond to file, so don't include it in the default selection
continue
}
defaultNamesOrTags := strset.New(selectionRequest.DefaultNamesOrTags...)
removals := strset.New(selectionRequest.RemoveNamesOrTags...)
missingFileIshTag := !defaultNamesOrTags.Has(filecataloging.FileTag) && !defaultNamesOrTags.Has("all") && !defaultNamesOrTags.Has("default")
if missingFileIshTag && !removals.Has(filecataloging.FileTag) {
log.Warnf("adding '%s' tag to the default cataloger selection, to override add '-%s' to the cataloger selection request", filecataloging.FileTag, filecataloging.FileTag)
selectionRequest.DefaultNamesOrTags = append(selectionRequest.DefaultNamesOrTags, filecataloging.FileTag)
}
}
}
// SelectInGroups is a convenience function that allows for selecting tasks from multiple groups of tasks. The original
// request is split into sub-requests, where only tokens that are relevant to the given group of tasks are considered.
// If tokens are passed that are not relevant to any group of tasks, an error is returned.
func SelectInGroups(taskGroups [][]Task, selectionRequest cataloging.SelectionRequest) ([][]Task, Selection, error) {
ensureDefaultSelectionHasFiles(&selectionRequest, taskGroups...)
reqs, errs := splitCatalogerSelectionRequest(selectionRequest, taskGroups)
if errs != nil {
return nil, Selection{
Request: selectionRequest,
}, errs
}
var finalTasks [][]Task
var selections []Selection
for idx, req := range reqs {
tskGroup := taskGroups[idx]
subFinalTasks, subSelection, err := _select(tskGroup, req)
if err != nil {
return nil, Selection{
Request: selectionRequest,
}, err
}
finalTasks = append(finalTasks, subFinalTasks)
selections = append(selections, subSelection)
}
return finalTasks, mergeSelections(selections, selectionRequest), nil
}
func mergeSelections(selections []Selection, ogRequest cataloging.SelectionRequest) Selection {
finalSelection := newSelection()
for _, s := range selections {
finalSelection.Result.Add(s.Result.List()...)
for name, tokenSelection := range s.TokensByTask {
if existing, exists := finalSelection.TokensByTask[name]; exists {
existing.merge(tokenSelection)
finalSelection.TokensByTask[name] = existing
} else {
finalSelection.TokensByTask[name] = tokenSelection
}
}
}
finalSelection.Request = ogRequest
return finalSelection
}
func splitCatalogerSelectionRequest(req cataloging.SelectionRequest, selectablePkgTaskGroups [][]Task) ([]cataloging.SelectionRequest, error) {
requestTagsOrNames := allRequestReferences(req)
leftoverTags := strset.New()
usedTagsAndNames := strset.New()
var usedTagGroups []*strset.Set
for _, taskGroup := range selectablePkgTaskGroups {
selectedTagOrNames, remainingTagsOrNames := tagsOrNamesThatTaskGroupRespondsTo(taskGroup, requestTagsOrNames)
leftoverTags = strset.Union(leftoverTags, remainingTagsOrNames)
usedTagGroups = append(usedTagGroups, selectedTagOrNames)
usedTagsAndNames.Add(selectedTagOrNames.List()...)
}
leftoverTags = strset.Difference(leftoverTags, usedTagsAndNames)
leftoverTags.Remove("all")
if leftoverTags.Size() > 0 {
l := leftoverTags.List()
sort.Strings(l)
return nil, fmt.Errorf("no cataloger tasks respond to the following selections: %v", strings.Join(l, ", "))
}
var newSelections []cataloging.SelectionRequest
for _, tags := range usedTagGroups {
newSelections = append(newSelections, newSelectionWithTags(req, tags))
}
return newSelections, nil
}
func newSelectionWithTags(req cataloging.SelectionRequest, tags *strset.Set) cataloging.SelectionRequest {
return cataloging.SelectionRequest{
DefaultNamesOrTags: filterTags(req.DefaultNamesOrTags, tags),
SubSelectTags: filterTags(req.SubSelectTags, tags),
AddNames: filterTags(req.AddNames, tags),
RemoveNamesOrTags: filterTags(req.RemoveNamesOrTags, tags),
}
}
func filterTags(reqTags []string, filterTags *strset.Set) []string {
var filtered []string
for _, tag := range reqTags {
if filterTags.Has(tag) {
filtered = append(filtered, tag)
}
}
return filtered
}
func tagsOrNamesThatTaskGroupRespondsTo(tasks []Task, requestTagsOrNames *strset.Set) (*strset.Set, *strset.Set) {
positiveRefs := strset.New()
for _, t := range tasks {
if sel, ok := t.(Selector); ok {
positiveRefs.Add("all") // everything responds to "all"
positiveRefs.Add(strset.Intersection(requestTagsOrNames, strset.New(sel.Selectors()...)).List()...)
}
positiveRefs.Add(t.Name())
}
return positiveRefs, strset.Difference(requestTagsOrNames, positiveRefs)
}
func allRequestReferences(s cataloging.SelectionRequest) *strset.Set {
st := strset.New()
st.Add(s.DefaultNamesOrTags...)
st.Add(s.SubSelectTags...)
st.Add(s.AddNames...)
st.Add(s.RemoveNamesOrTags...)
return st
}
// selectByExpressions the set of tasks to run based on the given expression(s).
func selectByExpressions(ts tasks, nodes Expressions) (tasks, Selection) {
if len(nodes) == 0 {
+459 -41
View File
@@ -10,7 +10,7 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/file"
)
@@ -21,45 +21,54 @@ func dummyTask(name string, tags ...string) Task {
}
// note: this test fixture does not need to be kept up to date here, but makes a great test subject
func createDummyTasks() tasks {
func createDummyPackageTasks() tasks {
return []Task{
// OS package installed catalogers
dummyTask("alpm-db-cataloger", "directory", "installed", "image", "os", "alpm", "archlinux"),
dummyTask("apk-db-cataloger", "directory", "installed", "image", "os", "apk", "alpine"),
dummyTask("dpkg-db-cataloger", "directory", "installed", "image", "os", "dpkg", "debian"),
dummyTask("portage-cataloger", "directory", "installed", "image", "os", "portage", "gentoo"),
dummyTask("rpm-db-cataloger", "directory", "installed", "image", "os", "rpm", "redhat"),
dummyTask("alpm-db-cataloger", "package", "directory", "installed", "image", "os", "alpm", "archlinux"),
dummyTask("apk-db-cataloger", "package", "directory", "installed", "image", "os", "apk", "alpine"),
dummyTask("dpkg-db-cataloger", "package", "directory", "installed", "image", "os", "dpkg", "debian"),
dummyTask("portage-cataloger", "package", "directory", "installed", "image", "os", "portage", "gentoo"),
dummyTask("rpm-db-cataloger", "package", "directory", "installed", "image", "os", "rpm", "redhat"),
// OS package declared catalogers
dummyTask("rpm-archive-cataloger", "declared", "directory", "os", "rpm", "redhat"),
dummyTask("rpm-archive-cataloger", "package", "declared", "directory", "os", "rpm", "redhat"),
// language-specific package installed catalogers
dummyTask("conan-info-cataloger", "installed", "image", "language", "cpp", "conan"),
dummyTask("javascript-package-cataloger", "installed", "image", "language", "javascript", "node"),
dummyTask("php-composer-installed-cataloger", "installed", "image", "language", "php", "composer"),
dummyTask("ruby-installed-gemspec-cataloger", "installed", "image", "language", "ruby", "gem", "gemspec"),
dummyTask("rust-cargo-lock-cataloger", "installed", "image", "language", "rust", "binary"),
dummyTask("conan-info-cataloger", "package", "installed", "image", "language", "cpp", "conan"),
dummyTask("javascript-package-cataloger", "package", "installed", "image", "language", "javascript", "node"),
dummyTask("php-composer-installed-cataloger", "package", "installed", "image", "language", "php", "composer"),
dummyTask("ruby-installed-gemspec-cataloger", "package", "installed", "image", "language", "ruby", "gem", "gemspec"),
dummyTask("rust-cargo-lock-cataloger", "package", "installed", "image", "language", "rust", "binary"),
// language-specific package declared catalogers
dummyTask("conan-cataloger", "declared", "directory", "language", "cpp", "conan"),
dummyTask("dart-pubspec-lock-cataloger", "declared", "directory", "language", "dart"),
dummyTask("dotnet-deps-cataloger", "declared", "directory", "language", "dotnet", "c#"),
dummyTask("elixir-mix-lock-cataloger", "declared", "directory", "language", "elixir"),
dummyTask("erlang-rebar-lock-cataloger", "declared", "directory", "language", "erlang"),
dummyTask("javascript-lock-cataloger", "declared", "directory", "language", "javascript", "node", "npm"),
dummyTask("conan-cataloger", "package", "declared", "directory", "language", "cpp", "conan"),
dummyTask("dart-pubspec-lock-cataloger", "package", "declared", "directory", "language", "dart"),
dummyTask("dotnet-deps-cataloger", "package", "declared", "directory", "language", "dotnet", "c#"),
dummyTask("elixir-mix-lock-cataloger", "package", "declared", "directory", "language", "elixir"),
dummyTask("erlang-rebar-lock-cataloger", "package", "declared", "directory", "language", "erlang"),
dummyTask("javascript-lock-cataloger", "package", "declared", "directory", "language", "javascript", "node", "npm"),
// language-specific package for both image and directory scans (but not necessarily declared)
dummyTask("dotnet-portable-executable-cataloger", "directory", "installed", "image", "language", "dotnet", "c#"),
dummyTask("python-installed-package-cataloger", "directory", "installed", "image", "language", "python"),
dummyTask("go-module-binary-cataloger", "directory", "installed", "image", "language", "go", "golang", "gomod", "binary"),
dummyTask("java-archive-cataloger", "directory", "installed", "image", "language", "java", "maven"),
dummyTask("graalvm-native-image-cataloger", "directory", "installed", "image", "language", "java"),
dummyTask("dotnet-portable-executable-cataloger", "package", "directory", "installed", "image", "language", "dotnet", "c#"),
dummyTask("python-installed-package-cataloger", "package", "directory", "installed", "image", "language", "python"),
dummyTask("go-module-binary-cataloger", "package", "directory", "installed", "image", "language", "go", "golang", "gomod", "binary"),
dummyTask("java-archive-cataloger", "package", "directory", "installed", "image", "language", "java", "maven"),
dummyTask("graalvm-native-image-cataloger", "package", "directory", "installed", "image", "language", "java"),
// other package catalogers
dummyTask("binary-cataloger", "declared", "directory", "image", "binary"),
dummyTask("github-actions-usage-cataloger", "declared", "directory", "github", "github-actions"),
dummyTask("github-action-workflow-usage-cataloger", "declared", "directory", "github", "github-actions"),
dummyTask("sbom-cataloger", "declared", "directory", "image", "sbom"),
dummyTask("binary-cataloger", "package", "declared", "directory", "image", "binary"),
dummyTask("github-actions-usage-cataloger", "package", "declared", "directory", "github", "github-actions"),
dummyTask("github-action-workflow-usage-cataloger", "package", "declared", "directory", "github", "github-actions"),
dummyTask("sbom-cataloger", "package", "declared", "directory", "image", "sbom"),
}
}
func createDummyFileTasks() tasks {
return []Task{
dummyTask("file-content-cataloger", "file", "content"),
dummyTask("file-metadata-cataloger", "file", "metadata"),
dummyTask("file-digest-cataloger", "file", "digest"),
dummyTask("file-executable-cataloger", "file", "binary-metadata"),
}
}
@@ -72,7 +81,7 @@ func TestSelect(t *testing.T) {
expressions []string
wantNames []string
wantTokens map[string]TokenSelection
wantRequest pkgcataloging.SelectionRequest
wantRequest cataloging.SelectionRequest
wantErr assert.ErrorAssertionFunc
}{
{
@@ -82,11 +91,11 @@ func TestSelect(t *testing.T) {
expressions: []string{},
wantNames: []string{},
wantTokens: map[string]TokenSelection{},
wantRequest: pkgcataloging.SelectionRequest{},
wantRequest: cataloging.SelectionRequest{},
},
{
name: "use default tasks",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"image",
},
@@ -129,13 +138,13 @@ func TestSelect(t *testing.T) {
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
},
},
{
name: "select, add, and remove tasks",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"image",
},
@@ -175,7 +184,7 @@ func TestSelect(t *testing.T) {
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
SubSelectTags: []string{"os"},
RemoveNamesOrTags: []string{"dpkg"},
@@ -184,7 +193,7 @@ func TestSelect(t *testing.T) {
},
{
name: "allow for partial selections",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"image",
},
@@ -228,7 +237,7 @@ func TestSelect(t *testing.T) {
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
SubSelectTags: []string{"os", "rust-cargo-lock-cataloger"},
RemoveNamesOrTags: []string{"dpkg"},
@@ -238,7 +247,7 @@ func TestSelect(t *testing.T) {
},
{
name: "select all tasks",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"all",
},
@@ -299,13 +308,13 @@ func TestSelect(t *testing.T) {
"github-action-workflow-usage-cataloger": newTokenSelection([]string{"all"}, nil),
"sbom-cataloger": newTokenSelection([]string{"all"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"all"},
},
},
{
name: "set default with multiple tags",
allTasks: createDummyTasks(),
allTasks: createDummyPackageTasks(),
basis: []string{
"gemspec",
"python",
@@ -319,10 +328,31 @@ func TestSelect(t *testing.T) {
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"gemspec"}, nil),
"python-installed-package-cataloger": newTokenSelection([]string{"python"}, nil),
},
wantRequest: pkgcataloging.SelectionRequest{
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"gemspec", "python"},
},
},
{
name: "automatically add file to default tags",
allTasks: createDummyFileTasks(),
basis: []string{},
expressions: []string{},
wantNames: []string{
"file-content-cataloger",
"file-metadata-cataloger",
"file-digest-cataloger",
"file-executable-cataloger",
},
wantTokens: map[string]TokenSelection{
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"file"},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -330,7 +360,7 @@ func TestSelect(t *testing.T) {
tt.wantErr = assert.NoError
}
req := pkgcataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
req := cataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
got, gotEvidence, err := Select(tt.allTasks, req)
tt.wantErr(t, err)
@@ -367,3 +397,391 @@ func TestSelect(t *testing.T) {
})
}
}
func TestSelectInGroups(t *testing.T) {
tests := []struct {
name string
taskGroups [][]Task
selectionReq cataloging.SelectionRequest
wantGroups [][]string
wantTokens map[string]TokenSelection
wantRequest cataloging.SelectionRequest
wantErr assert.ErrorAssertionFunc
}{
{
name: "select only within the file tasks (leave package tasks alone)",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().
WithDefaults("image"). // note: file missing
WithSubSelections("content", "digest"),
wantGroups: [][]string{
{
// this is the original, untouched package task list
"alpm-db-cataloger",
"apk-db-cataloger",
"dpkg-db-cataloger",
"portage-cataloger",
"rpm-db-cataloger",
"conan-info-cataloger",
"javascript-package-cataloger",
"php-composer-installed-cataloger",
"ruby-installed-gemspec-cataloger",
"rust-cargo-lock-cataloger",
"dotnet-portable-executable-cataloger",
"python-installed-package-cataloger",
"go-module-binary-cataloger",
"java-archive-cataloger",
"graalvm-native-image-cataloger",
"binary-cataloger",
"sbom-cataloger",
},
{
// this has been filtered based on the request
"file-content-cataloger",
"file-digest-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// packages
"alpm-db-cataloger": newTokenSelection([]string{"image"}, nil),
"apk-db-cataloger": newTokenSelection([]string{"image"}, nil),
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"conan-info-cataloger": newTokenSelection([]string{"image"}, nil),
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil),
"dpkg-db-cataloger": newTokenSelection([]string{"image"}, nil),
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil),
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil),
"java-archive-cataloger": newTokenSelection([]string{"image"}, nil),
"javascript-package-cataloger": newTokenSelection([]string{"image"}, nil),
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil),
"portage-cataloger": newTokenSelection([]string{"image"}, nil),
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil),
"rpm-db-cataloger": newTokenSelection([]string{"image"}, nil),
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil),
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
// files
"file-content-cataloger": newTokenSelection([]string{"content", "file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"digest", "file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"}, // note: file automatically added
SubSelectTags: []string{"content", "digest"},
},
wantErr: assert.NoError,
},
{
name: "select package tasks (leave file tasks alone)",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().WithDefaults("image").WithSubSelections("os"),
wantGroups: [][]string{
{
// filtered based on the request
"alpm-db-cataloger",
"apk-db-cataloger",
"dpkg-db-cataloger",
"portage-cataloger",
"rpm-db-cataloger",
},
{
// this is the original, untouched file task list
"file-content-cataloger",
"file-metadata-cataloger",
"file-digest-cataloger",
"file-executable-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// packages - os
"alpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"apk-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"rpm-archive-cataloger": newTokenSelection([]string{"os"}, nil),
"rpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"portage-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"dpkg-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
// packages - remaining
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"conan-info-cataloger": newTokenSelection([]string{"image"}, nil),
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil),
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil),
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil),
"java-archive-cataloger": newTokenSelection([]string{"image"}, nil),
"javascript-package-cataloger": newTokenSelection([]string{"image"}, nil),
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil),
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil),
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil),
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
// files
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
SubSelectTags: []string{"os"},
},
wantErr: assert.NoError,
},
{
name: "select only file tasks (default)",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().WithDefaults("file"),
wantGroups: [][]string{
// filtered based on the request
nil,
{
// this is the original, untouched file task list
"file-content-cataloger",
"file-metadata-cataloger",
"file-digest-cataloger",
"file-executable-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// files
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"file"},
},
wantErr: assert.NoError,
},
{
name: "select only file tasks (via removal of package)",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().WithDefaults("file", "image").WithRemovals("package"),
wantGroups: [][]string{
// filtered based on the request
nil,
{
// this is the original, untouched file task list
"file-content-cataloger",
"file-metadata-cataloger",
"file-digest-cataloger",
"file-executable-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// packages
"alpm-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"apk-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"binary-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"conan-info-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"dpkg-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"java-archive-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"javascript-package-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"portage-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"rpm-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"sbom-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
"rpm-archive-cataloger": newTokenSelection(nil, []string{"package"}),
"conan-cataloger": newTokenSelection(nil, []string{"package"}),
"dart-pubspec-lock-cataloger": newTokenSelection(nil, []string{"package"}),
"dotnet-deps-cataloger": newTokenSelection(nil, []string{"package"}),
"elixir-mix-lock-cataloger": newTokenSelection(nil, []string{"package"}),
"erlang-rebar-lock-cataloger": newTokenSelection(nil, []string{"package"}),
"javascript-lock-cataloger": newTokenSelection(nil, []string{"package"}),
"github-actions-usage-cataloger": newTokenSelection(nil, []string{"package"}),
"github-action-workflow-usage-cataloger": newTokenSelection(nil, []string{"package"}),
// files
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"file", "image"},
RemoveNamesOrTags: []string{"package"},
},
wantErr: assert.NoError,
},
{
name: "select file and package tasks",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().
WithDefaults("image").
WithSubSelections("os", "content", "digest"),
wantGroups: [][]string{
{
// filtered based on the request
"alpm-db-cataloger",
"apk-db-cataloger",
"dpkg-db-cataloger",
"portage-cataloger",
"rpm-db-cataloger",
},
{
// filtered based on the request
"file-content-cataloger",
"file-digest-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// packages - os
"alpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"apk-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"rpm-archive-cataloger": newTokenSelection([]string{"os"}, nil),
"rpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"portage-cataloger": newTokenSelection([]string{"os", "image"}, nil),
"dpkg-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
// packages - remaining
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
"conan-info-cataloger": newTokenSelection([]string{"image"}, nil),
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil),
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil),
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil),
"java-archive-cataloger": newTokenSelection([]string{"image"}, nil),
"javascript-package-cataloger": newTokenSelection([]string{"image"}, nil),
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil),
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil),
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil),
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil),
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
// files
"file-content-cataloger": newTokenSelection([]string{"file", "content"}, nil), // note extra tags
"file-digest-cataloger": newTokenSelection([]string{"file", "digest"}, nil), // note extra tags
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
SubSelectTags: []string{"os", "content", "digest"},
},
wantErr: assert.NoError,
},
{
name: "complex selection with multiple operators across groups",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().
WithDefaults("os"). // note: no file tag present
WithExpression("+github-actions-usage-cataloger", "-dpkg", "-digest", "content", "+file-metadata-cataloger", "-declared"),
wantGroups: [][]string{
{
"alpm-db-cataloger",
"apk-db-cataloger",
"portage-cataloger",
"rpm-db-cataloger",
"github-actions-usage-cataloger",
},
{
"file-content-cataloger",
"file-metadata-cataloger",
},
},
wantTokens: map[string]TokenSelection{
// selected package tasks
"alpm-db-cataloger": newTokenSelection([]string{"os"}, nil),
"apk-db-cataloger": newTokenSelection([]string{"os"}, nil),
"dpkg-db-cataloger": newTokenSelection([]string{"os"}, []string{"dpkg"}),
"portage-cataloger": newTokenSelection([]string{"os"}, nil),
"rpm-archive-cataloger": newTokenSelection([]string{"os"}, []string{"declared"}),
"rpm-db-cataloger": newTokenSelection([]string{"os"}, nil),
"github-actions-usage-cataloger": newTokenSelection([]string{"github-actions-usage-cataloger"}, []string{"declared"}),
// selected file tasks
"file-content-cataloger": newTokenSelection([]string{"content", "file"}, nil),
"file-metadata-cataloger": newTokenSelection([]string{"file-metadata-cataloger", "file"}, nil),
// removed package tasks
"binary-cataloger": newTokenSelection(nil, []string{"declared"}),
"conan-cataloger": newTokenSelection(nil, []string{"declared"}),
"dart-pubspec-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
"dotnet-deps-cataloger": newTokenSelection(nil, []string{"declared"}),
"elixir-mix-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
"erlang-rebar-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
"github-action-workflow-usage-cataloger": newTokenSelection(nil, []string{"declared"}),
"javascript-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
"sbom-cataloger": newTokenSelection(nil, []string{"declared"}),
// removed file tasks
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
"file-digest-cataloger": newTokenSelection([]string{"file"}, []string{"digest"}),
},
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"os", "file"}, // note: file added automatically
SubSelectTags: []string{"content"},
RemoveNamesOrTags: []string{"dpkg", "digest", "declared"},
AddNames: []string{"github-actions-usage-cataloger", "file-metadata-cataloger"},
},
wantErr: assert.NoError,
},
{
name: "invalid tag",
taskGroups: [][]Task{
createDummyPackageTasks(),
createDummyFileTasks(),
},
selectionReq: cataloging.NewSelectionRequest().WithDefaults("invalid"),
wantGroups: nil,
wantTokens: nil,
wantRequest: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"invalid", "file"},
},
wantErr: assert.Error,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = assert.NoError
}
gotGroups, gotSelection, err := SelectInGroups(tt.taskGroups, tt.selectionReq)
tt.wantErr(t, err)
if err != nil {
// dev note: this is useful for debugging when needed...
//for _, e := range gotEvidence.Request.Expressions {
// t.Logf("expression (errors %q): %#v", e.Errors, e)
//}
// note: we DON'T bail early in validations... this is because we should always return the full set of
// of selected tasks and surrounding evidence.
}
var gotGroupNames [][]string
for _, group := range gotGroups {
var names []string
for _, task := range group {
names = append(names, task.Name())
}
gotGroupNames = append(gotGroupNames, names)
}
assert.Equal(t, tt.wantGroups, gotGroupNames)
assert.Equal(t, tt.wantTokens, gotSelection.TokensByTask)
assert.Equal(t, tt.wantRequest, gotSelection.Request)
})
}
}
+1 -1
View File
@@ -23,7 +23,7 @@ func ProcessPathErrors(err error) error {
}
}
unknowns, remainingErrors := ExtractCoordinateErrors(err)
log.Warn(remainingErrors)
log.Debug(remainingErrors)
var out []error
for _, u := range unknowns {
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
+74 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "anchore.io/schema/syft/json/16.0.19/document",
"$id": "anchore.io/schema/syft/json/16.0.22/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@@ -218,6 +218,44 @@
"matches"
]
},
"BitnamiSbomEntry": {
"properties": {
"name": {
"type": "string"
},
"arch": {
"type": "string"
},
"distro": {
"type": "string"
},
"revision": {
"type": "string"
},
"version": {
"type": "string"
},
"path": {
"type": "string"
},
"files": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
"required": [
"name",
"arch",
"distro",
"revision",
"version",
"path",
"files"
]
},
"CConanFileEntry": {
"properties": {
"ref": {
@@ -1288,6 +1326,9 @@
"$ref": "#/$defs/Location"
},
"type": "array"
},
"contents": {
"type": "string"
}
},
"type": "object",
@@ -1650,6 +1691,9 @@
{
"$ref": "#/$defs/BinarySignature"
},
{
"$ref": "#/$defs/BitnamiSbomEntry"
},
{
"$ref": "#/$defs/CConanFileEntry"
},
@@ -1782,6 +1826,9 @@
{
"$ref": "#/$defs/SwiplpackPackage"
},
{
"$ref": "#/$defs/TerraformLockProviderEntry"
},
{
"$ref": "#/$defs/WordpressPluginEntry"
}
@@ -2720,6 +2767,32 @@
"dependencies"
]
},
"TerraformLockProviderEntry": {
"properties": {
"url": {
"type": "string"
},
"constraints": {
"type": "string"
},
"version": {
"type": "string"
},
"hashes": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
"required": [
"url",
"constraints",
"version",
"hashes"
]
},
"WordpressPluginEntry": {
"properties": {
"pluginInstallDirectory": {
+6
View File
@@ -0,0 +1,6 @@
package filecataloging
const (
// FileTag should be used to identify catalogers that primarily discover information about files (as opposed to packages).
FileTag = "file"
)
+15
View File
@@ -0,0 +1,15 @@
package cataloging
import "github.com/anchore/syft/internal/licenses"
type LicenseConfig struct {
IncludeUnkownLicenseContent bool `json:"include-unknown-license-content" yaml:"include-unknown-license-content" mapstructure:"include-unknown-license-content"`
Coverage float64 `json:"coverage" yaml:"coverage" mapstructure:"coverage"`
}
func DefaultLicenseConfig() LicenseConfig {
return LicenseConfig{
IncludeUnkownLicenseContent: licenses.DefaultIncludeLicenseContent,
Coverage: licenses.DefaultCoverageThreshold,
}
}
+8
View File
@@ -2,6 +2,7 @@ package pkgcataloging
import (
"github.com/anchore/syft/syft/pkg/cataloger/binary"
"github.com/anchore/syft/syft/pkg/cataloger/dotnet"
"github.com/anchore/syft/syft/pkg/cataloger/golang"
"github.com/anchore/syft/syft/pkg/cataloger/java"
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
@@ -11,6 +12,7 @@ import (
type Config struct {
Binary binary.ClassifierCatalogerConfig `yaml:"binary" json:"binary" mapstructure:"binary"`
Dotnet dotnet.CatalogerConfig `yaml:"dotnet" json:"dotnet" mapstructure:"dotnet"`
Golang golang.CatalogerConfig `yaml:"golang" json:"golang" mapstructure:"golang"`
JavaArchive java.ArchiveCatalogerConfig `yaml:"java-archive" json:"java-archive" mapstructure:"java-archive"`
JavaScript javascript.CatalogerConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"`
@@ -21,6 +23,7 @@ type Config struct {
func DefaultConfig() Config {
return Config{
Binary: binary.DefaultClassifierCatalogerConfig(),
Dotnet: dotnet.DefaultCatalogerConfig(),
Golang: golang.DefaultCatalogerConfig(),
LinuxKernel: kernel.DefaultLinuxKernelCatalogerConfig(),
Python: python.DefaultCatalogerConfig(),
@@ -33,6 +36,11 @@ func (c Config) WithBinaryConfig(cfg binary.ClassifierCatalogerConfig) Config {
return c
}
func (c Config) WithDotnetConfig(cfg dotnet.CatalogerConfig) Config {
c.Dotnet = cfg
return c
}
func (c Config) WithGolangConfig(cfg golang.CatalogerConfig) Config {
c.Golang = cfg
return c
@@ -0,0 +1,11 @@
package pkgcataloging
import (
"github.com/anchore/syft/syft/cataloging"
)
// SelectionRequest is deprecated: use cataloging.SelectionRequest instead
type SelectionRequest = cataloging.SelectionRequest
// NewSelectionRequest is deprecated: use cataloging.NewSelectionRequest instead
var NewSelectionRequest = cataloging.NewSelectionRequest
@@ -1,8 +1,6 @@
package pkgcataloging
package cataloging
import (
"strings"
)
import "strings"
type SelectionRequest struct {
DefaultNamesOrTags []string `json:"default,omitempty"`
@@ -50,6 +48,10 @@ func (s SelectionRequest) WithRemovals(nameOrTags ...string) SelectionRequest {
return s
}
func (s SelectionRequest) IsEmpty() bool {
return len(s.AddNames) == 0 && len(s.RemoveNamesOrTags) == 0 && len(s.SubSelectTags) == 0 && len(s.DefaultNamesOrTags) == 0
}
func cleanSelection(tags []string) []string {
var cleaned []string
for _, tag := range tags {
+3 -2
View File
@@ -16,13 +16,14 @@ type configurationAuditTrail struct {
DataGeneration cataloging.DataGenerationConfig `json:"data-generation" yaml:"data-generation" mapstructure:"data-generation"`
Packages pkgcataloging.Config `json:"packages" yaml:"packages" mapstructure:"packages"`
Files filecataloging.Config `json:"files" yaml:"files" mapstructure:"files"`
Licenses cataloging.LicenseConfig `json:"licenses" yaml:"licenses" mapstructure:"licenses"`
Catalogers catalogerManifest `json:"catalogers" yaml:"catalogers" mapstructure:"catalogers"`
ExtraConfigs any `json:"extra,omitempty" yaml:"extra" mapstructure:"extra"`
}
type catalogerManifest struct {
Requested pkgcataloging.SelectionRequest `json:"requested" yaml:"requested" mapstructure:"requested"`
Used []string `json:"used" yaml:"used" mapstructure:"used"`
Requested cataloging.SelectionRequest `json:"requested" yaml:"requested" mapstructure:"requested"`
Used []string `json:"used" yaml:"used" mapstructure:"used"`
}
type marshalAPIConfiguration configurationAuditTrail
+13 -2
View File
@@ -52,6 +52,7 @@ func CreateSBOM(ctx context.Context, src source.Source, cfg *CreateSBOMConfig) (
DataGeneration: cfg.DataGeneration,
Packages: cfg.Packages,
Files: cfg.Files,
Licenses: cfg.Licenses,
Catalogers: *audit,
ExtraConfigs: cfg.ToolConfiguration,
},
@@ -61,8 +62,15 @@ func CreateSBOM(ctx context.Context, src source.Source, cfg *CreateSBOMConfig) (
},
}
// inject a single license scanner for all package cataloging tasks into context
ctx = licenses.SetContextLicenseScanner(ctx, licenses.NewDefaultScanner())
// 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),
)
if err != nil {
return nil, fmt.Errorf("could not build licenseScanner for cataloging: %w", err)
}
ctx = licenses.SetContextLicenseScanner(ctx, licenseScanner)
catalogingProgress := monitorCatalogingTask(src.ID(), taskGroups)
packageCatalogingProgress := monitorPackageCatalogingTask()
@@ -125,6 +133,9 @@ func monitorCatalogingTask(srcID artifact.ID, tasks [][]task.Task) *monitor.Cata
func formatTaskNames(tasks []task.Task) []string {
set := strset.New()
for _, td := range tasks {
if td == nil {
continue
}
set.Add(td.Name())
}
list := set.List()
+105 -48
View File
@@ -26,16 +26,17 @@ type CreateSBOMConfig struct {
Unknowns cataloging.UnknownsConfig
DataGeneration cataloging.DataGenerationConfig
Packages pkgcataloging.Config
Licenses cataloging.LicenseConfig
Files filecataloging.Config
Parallelism int
CatalogerSelection pkgcataloging.SelectionRequest
CatalogerSelection cataloging.SelectionRequest
// audit what tool is being used to generate the SBOM
ToolName string
ToolVersion string
ToolConfiguration interface{}
packageTaskFactories task.PackageTaskFactories
packageTaskFactories task.Factories
packageCatalogerReferences []pkgcataloging.CatalogerReference
}
@@ -46,6 +47,7 @@ func DefaultCreateSBOMConfig() *CreateSBOMConfig {
Relationships: cataloging.DefaultRelationshipsConfig(),
DataGeneration: cataloging.DefaultDataGenerationConfig(),
Packages: pkgcataloging.DefaultConfig(),
Licenses: cataloging.DefaultLicenseConfig(),
Files: filecataloging.DefaultConfig(),
Parallelism: 1,
packageTaskFactories: task.DefaultPackageTaskFactories(),
@@ -134,6 +136,12 @@ func (c *CreateSBOMConfig) WithPackagesConfig(cfg pkgcataloging.Config) *CreateS
return c
}
// WithPackagesConfig allows for defining any specific behavior for syft-implemented catalogers.
func (c *CreateSBOMConfig) WithLicenseConfig(cfg cataloging.LicenseConfig) *CreateSBOMConfig {
c.Licenses = cfg
return c
}
// WithFilesConfig allows for defining file-based cataloging parameters.
func (c *CreateSBOMConfig) WithFilesConfig(cfg filecataloging.Config) *CreateSBOMConfig {
c.Files = cfg
@@ -150,7 +158,7 @@ func (c *CreateSBOMConfig) WithoutFiles() *CreateSBOMConfig {
}
// WithCatalogerSelection allows for adding to, removing from, or sub-selecting the final set of catalogers by name or tag.
func (c *CreateSBOMConfig) WithCatalogerSelection(selection pkgcataloging.SelectionRequest) *CreateSBOMConfig {
func (c *CreateSBOMConfig) WithCatalogerSelection(selection cataloging.SelectionRequest) *CreateSBOMConfig {
c.CatalogerSelection = selection
return c
}
@@ -166,6 +174,10 @@ func (c *CreateSBOMConfig) WithoutCatalogers() *CreateSBOMConfig {
// WithCatalogers allows for adding user-provided catalogers to the final set of catalogers that will always be run
// regardless of the source type or any cataloger selections provided.
func (c *CreateSBOMConfig) WithCatalogers(catalogerRefs ...pkgcataloging.CatalogerReference) *CreateSBOMConfig {
for i := range catalogerRefs {
// ensure that all package catalogers have the package tag
catalogerRefs[i].Tags = append(catalogerRefs[i].Tags, pkgcataloging.PackageTag)
}
c.packageCatalogerReferences = append(c.packageCatalogerReferences, catalogerRefs...)
return c
@@ -182,8 +194,8 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
environmentTasks := c.environmentTasks()
relationshipsTasks := c.relationshipTasks(src)
unknownTasks := c.unknownsTasks()
fileTasks := c.fileTasks()
pkgTasks, selectionEvidence, err := c.packageTasks(src)
pkgTasks, fileTasks, selectionEvidence, err := c.selectTasks(src)
if err != nil {
return nil, nil, err
}
@@ -214,80 +226,117 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
taskGroups...,
)
var allTasks []task.Task
allTasks = append(allTasks, pkgTasks...)
allTasks = append(allTasks, fileTasks...)
return taskGroups, &catalogerManifest{
Requested: selectionEvidence.Request,
Used: formatTaskNames(pkgTasks),
Used: formatTaskNames(allTasks),
}, nil
}
// fileTasks returns the set of tasks that should be run to catalog files.
func (c *CreateSBOMConfig) fileTasks() []task.Task {
var tsks []task.Task
if t := task.NewFileDigestCatalogerTask(c.Files.Selection, c.Files.Hashers...); t != nil {
tsks = append(tsks, t)
}
if t := task.NewFileMetadataCatalogerTask(c.Files.Selection); t != nil {
tsks = append(tsks, t)
}
if t := task.NewFileContentCatalogerTask(c.Files.Content); t != nil {
tsks = append(tsks, t)
}
if t := task.NewExecutableCatalogerTask(c.Files.Selection, c.Files.Executable); t != nil {
tsks = append(tsks, t)
func (c *CreateSBOMConfig) fileTasks(cfg task.CatalogingFactoryConfig) ([]task.Task, error) {
tsks, err := task.DefaultFileTaskFactories().Tasks(cfg)
if err != nil {
return nil, fmt.Errorf("unable to create file cataloger tasks: %w", err)
}
return tsks
return tsks, nil
}
// packageTasks returns the set of tasks that should be run to catalog packages.
func (c *CreateSBOMConfig) packageTasks(src source.Description) ([]task.Task, *task.Selection, error) {
// selectTasks returns the set of tasks that should be run to catalog packages and files.
func (c *CreateSBOMConfig) selectTasks(src source.Description) ([]task.Task, []task.Task, *task.Selection, error) {
cfg := task.CatalogingFactoryConfig{
SearchConfig: c.Search,
RelationshipsConfig: c.Relationships,
DataGenerationConfig: c.DataGeneration,
PackagesConfig: c.Packages,
ComplianceConfig: c.Compliance,
FilesConfig: c.Files,
}
persistentTasks, selectableTasks, err := c.allPackageTasks(cfg)
persistentPkgTasks, selectablePkgTasks, err := c.allPackageTasks(cfg)
if err != nil {
return nil, nil, fmt.Errorf("unable to create package cataloger tasks: %w", err)
return nil, nil, nil, fmt.Errorf("unable to create package cataloger tasks: %w", err)
}
req, err := finalSelectionRequest(c.CatalogerSelection, src)
req, err := finalTaskSelectionRequest(c.CatalogerSelection, src)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
finalTasks, selection, err := task.Select(selectableTasks, *req)
selectableFileTasks, err := c.fileTasks(cfg)
if err != nil {
return nil, nil, err
return nil, nil, nil, err
}
finalTasks = append(finalTasks, persistentTasks...)
if len(finalTasks) == 0 {
log.Warn("no catalogers selected")
return finalTasks, &selection, nil
taskGroups := [][]task.Task{
selectablePkgTasks,
selectableFileTasks,
}
return finalTasks, &selection, nil
finalTaskGroups, selection, err := task.SelectInGroups(taskGroups, *req)
if err != nil {
return nil, nil, nil, err
}
finalPkgTasks := finalTaskGroups[0]
finalFileTasks := finalTaskGroups[1]
finalPkgTasks = append(finalPkgTasks, persistentPkgTasks...)
if len(finalPkgTasks) == 0 && len(finalFileTasks) == 0 {
return nil, nil, nil, fmt.Errorf("no catalogers selected")
}
logTaskNames(finalPkgTasks, "package cataloger")
logTaskNames(finalFileTasks, "file cataloger")
if len(finalPkgTasks) == 0 && len(finalFileTasks) == 0 {
return nil, nil, nil, fmt.Errorf("no catalogers selected")
}
if len(finalPkgTasks) == 0 {
log.Debug("no package catalogers selected")
}
if len(finalFileTasks) == 0 {
if c.Files.Selection != file.NoFilesSelection {
log.Warnf("no file catalogers selected but file selection is configured as %q (this may be unintentional)", c.Files.Selection)
} else {
log.Debug("no file catalogers selected")
}
}
return finalPkgTasks, finalFileTasks, &selection, nil
}
func finalSelectionRequest(req pkgcataloging.SelectionRequest, src source.Description) (*pkgcataloging.SelectionRequest, error) {
func logTaskNames(tasks []task.Task, kind string) {
// log as tree output (like tree command)
log.Debugf("selected %d %s tasks", len(tasks), kind)
names := formatTaskNames(tasks)
for idx, t := range names {
if idx == len(tasks)-1 {
log.Tracef("└── %s", t)
} else {
log.Tracef("├── %s", t)
}
}
}
func finalTaskSelectionRequest(req cataloging.SelectionRequest, src source.Description) (*cataloging.SelectionRequest, error) {
if len(req.DefaultNamesOrTags) == 0 {
defaultTag, err := findDefaultTag(src)
defaultTags, err := findDefaultTags(src)
if err != nil {
return nil, fmt.Errorf("unable to determine default cataloger tag: %w", err)
}
if defaultTag != "" {
req.DefaultNamesOrTags = append(req.DefaultNamesOrTags, defaultTag)
}
req.DefaultNamesOrTags = append(req.DefaultNamesOrTags, defaultTags...)
req.RemoveNamesOrTags = replaceDefaultTagReferences(defaultTag, req.RemoveNamesOrTags)
req.SubSelectTags = replaceDefaultTagReferences(defaultTag, req.SubSelectTags)
req.RemoveNamesOrTags = replaceDefaultTagReferences(defaultTags, req.RemoveNamesOrTags)
req.SubSelectTags = replaceDefaultTagReferences(defaultTags, req.SubSelectTags)
}
return &req, nil
@@ -379,21 +428,29 @@ func (c *CreateSBOMConfig) Create(ctx context.Context, src source.Source) (*sbom
return CreateSBOM(ctx, src, c)
}
func findDefaultTag(src source.Description) (string, error) {
func findDefaultTags(src source.Description) ([]string, error) {
switch m := src.Metadata.(type) {
case source.ImageMetadata:
return pkgcataloging.ImageTag, nil
return []string{pkgcataloging.ImageTag, filecataloging.FileTag}, nil
case source.FileMetadata, source.DirectoryMetadata:
return pkgcataloging.DirectoryTag, nil
return []string{pkgcataloging.DirectoryTag, filecataloging.FileTag}, nil
default:
return "", fmt.Errorf("unable to determine default cataloger tag for source type=%T", m)
return nil, fmt.Errorf("unable to determine default cataloger tag for source type=%T", m)
}
}
func replaceDefaultTagReferences(defaultTag string, lst []string) []string {
func replaceDefaultTagReferences(defaultTags []string, lst []string) []string {
for i, tag := range lst {
if strings.ToLower(tag) == "default" {
lst[i] = defaultTag
switch len(defaultTags) {
case 0:
lst[i] = ""
case 1:
lst[i] = defaultTags[0]
default:
// remove the default tag and add the individual tags
lst = append(lst[:i], append(defaultTags, lst[i+1:]...)...)
}
}
}
return lst
+94 -64
View File
@@ -88,15 +88,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
wantTaskNames: [][]string{
environmentCatalogerNames(),
pkgCatalogerNamesWithTagOrName(t, "image"),
fileCatalogerNames(true, true, true),
fileCatalogerNames(),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
},
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "image"), fileCatalogerNames()),
},
wantErr: require.NoError,
},
@@ -107,15 +107,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
wantTaskNames: [][]string{
environmentCatalogerNames(),
pkgCatalogerNamesWithTagOrName(t, "directory"),
fileCatalogerNames(true, true, true),
fileCatalogerNames(),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"directory"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"directory", "file"},
},
Used: pkgCatalogerNamesWithTagOrName(t, "directory"),
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "directory"), fileCatalogerNames()),
},
wantErr: require.NoError,
},
@@ -127,51 +127,53 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
wantTaskNames: [][]string{
environmentCatalogerNames(),
pkgCatalogerNamesWithTagOrName(t, "directory"),
fileCatalogerNames(true, true, true),
fileCatalogerNames(),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"directory"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"directory", "file"},
},
Used: pkgCatalogerNamesWithTagOrName(t, "directory"),
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "directory"), fileCatalogerNames()),
},
wantErr: require.NoError,
},
{
name: "no file digest cataloger",
src: imgSrc,
cfg: DefaultCreateSBOMConfig().WithFilesConfig(filecataloging.DefaultConfig().WithHashers()),
cfg: DefaultCreateSBOMConfig().WithCatalogerSelection(cataloging.NewSelectionRequest().WithRemovals("digest")),
wantTaskNames: [][]string{
environmentCatalogerNames(),
pkgCatalogerNamesWithTagOrName(t, "image"),
fileCatalogerNames(false, true, true), // note: the digest cataloger is not included
fileCatalogerNames("file-metadata", "content", "binary-metadata"),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
RemoveNamesOrTags: []string{"digest"},
},
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "image"), fileCatalogerNames("file-metadata", "content", "binary-metadata")),
},
wantErr: require.NoError,
},
{
name: "select no file catalogers",
src: imgSrc,
cfg: DefaultCreateSBOMConfig().WithFilesConfig(filecataloging.DefaultConfig().WithSelection(file.NoFilesSelection)),
cfg: DefaultCreateSBOMConfig().WithCatalogerSelection(cataloging.NewSelectionRequest().WithRemovals("file")),
wantTaskNames: [][]string{
environmentCatalogerNames(),
pkgCatalogerNamesWithTagOrName(t, "image"),
// note: there are no file catalogers in their own group
nil, // note: there is a file cataloging group, with no items in it
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
RemoveNamesOrTags: []string{"file"},
},
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
},
@@ -186,16 +188,16 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
// note: there is a single group of catalogers for pkgs and files
append(
pkgCatalogerNamesWithTagOrName(t, "image"),
fileCatalogerNames(true, true, true)...,
fileCatalogerNames()...,
),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
},
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "image"), fileCatalogerNames()),
},
wantErr: require.NoError,
},
@@ -208,15 +210,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
wantTaskNames: [][]string{
environmentCatalogerNames(),
addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"),
fileCatalogerNames(true, true, true),
fileCatalogerNames(),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
},
Used: addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"),
Used: flatten(addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"), fileCatalogerNames()),
},
wantErr: require.NoError,
},
@@ -229,15 +231,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
wantTaskNames: [][]string{
environmentCatalogerNames(),
addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"),
fileCatalogerNames(true, true, true),
fileCatalogerNames(),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"directory"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"directory", "file"},
},
Used: addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"),
Used: flatten(addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"), fileCatalogerNames()),
},
wantErr: require.NoError,
},
@@ -246,20 +248,20 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
src: imgSrc,
cfg: DefaultCreateSBOMConfig().WithCatalogers(
pkgcataloging.NewAlwaysEnabledCatalogerReference(newDummyCataloger("persistent")),
).WithCatalogerSelection(pkgcataloging.NewSelectionRequest().WithSubSelections("javascript")),
).WithCatalogerSelection(cataloging.NewSelectionRequest().WithSubSelections("javascript")),
wantTaskNames: [][]string{
environmentCatalogerNames(),
addTo(pkgIntersect("image", "javascript"), "persistent"),
fileCatalogerNames(true, true, true),
fileCatalogerNames(),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
SubSelectTags: []string{"javascript"},
},
Used: addTo(pkgIntersect("image", "javascript"), "persistent"),
Used: flatten(addTo(pkgIntersect("image", "javascript"), "persistent"), fileCatalogerNames()),
},
wantErr: require.NoError,
},
@@ -272,15 +274,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
wantTaskNames: [][]string{
environmentCatalogerNames(),
addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"),
fileCatalogerNames(true, true, true),
fileCatalogerNames(),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
},
Used: addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"),
Used: flatten(addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"), fileCatalogerNames()),
},
wantErr: require.NoError,
},
@@ -293,15 +295,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
wantTaskNames: [][]string{
environmentCatalogerNames(),
pkgCatalogerNamesWithTagOrName(t, "image"),
fileCatalogerNames(true, true, true),
fileCatalogerNames(),
relationshipCatalogerNames(),
unknownsTaskNames(),
},
wantManifest: &catalogerManifest{
Requested: pkgcataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image"},
Requested: cataloging.SelectionRequest{
DefaultNamesOrTags: []string{"image", "file"},
},
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "image"), fileCatalogerNames()),
},
wantErr: require.NoError,
},
@@ -314,9 +316,6 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
// sanity check
require.NotEmpty(t, tt.wantTaskNames)
for _, group := range tt.wantTaskNames {
require.NotEmpty(t, group)
}
// test the subject
gotTasks, gotManifest, err := tt.cfg.makeTaskGroups(tt.src)
@@ -378,20 +377,51 @@ func pkgCatalogerNamesWithTagOrName(t *testing.T, token string) []string {
return names
}
func fileCatalogerNames(digest, metadata, executable bool) []string {
func fileCatalogerNames(tokens ...string) []string {
var names []string
if digest {
names = append(names, "file-digest-cataloger")
}
if executable {
names = append(names, "file-executable-cataloger")
}
if metadata {
names = append(names, "file-metadata-cataloger")
cfg := task.DefaultCatalogingFactoryConfig()
topLoop:
for _, factory := range task.DefaultFileTaskFactories() {
cat := factory(cfg)
if cat == nil {
continue
}
name := cat.Name()
if len(tokens) == 0 {
names = append(names, name)
continue
}
for _, token := range tokens {
if selector, ok := cat.(task.Selector); ok {
if selector.HasAllSelectors(token) {
names = append(names, name)
continue topLoop
}
}
if name == token {
names = append(names, name)
}
}
}
sort.Strings(names)
return names
}
func flatten(lists ...[]string) []string {
var final []string
for _, lst := range lists {
final = append(final, lst...)
}
sort.Strings(final)
return final
}
func relationshipCatalogerNames() []string {
return []string{"relationships-cataloger"}
}
@@ -436,7 +466,7 @@ func Test_replaceDefaultTagReferences(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, replaceDefaultTagReferences("replacement", tt.lst))
assert.Equal(t, tt.want, replaceDefaultTagReferences([]string{"replacement"}, tt.lst))
})
}
}
@@ -446,7 +476,7 @@ func Test_findDefaultTag(t *testing.T) {
tests := []struct {
name string
src source.Description
want string
want []string
wantErr require.ErrorAssertionFunc
}{
{
@@ -454,21 +484,21 @@ func Test_findDefaultTag(t *testing.T) {
src: source.Description{
Metadata: source.ImageMetadata{},
},
want: pkgcataloging.ImageTag,
want: []string{pkgcataloging.ImageTag, filecataloging.FileTag},
},
{
name: "directory",
src: source.Description{
Metadata: source.DirectoryMetadata{},
},
want: pkgcataloging.DirectoryTag,
want: []string{pkgcataloging.DirectoryTag, filecataloging.FileTag},
},
{
name: "file",
src: source.Description{
Metadata: source.FileMetadata{},
},
want: pkgcataloging.DirectoryTag, // not a mistake...
want: []string{pkgcataloging.DirectoryTag, filecataloging.FileTag}, // not a mistake...
},
{
name: "unknown",
@@ -483,7 +513,7 @@ func Test_findDefaultTag(t *testing.T) {
if tt.wantErr == nil {
tt.wantErr = require.NoError
}
got, err := findDefaultTag(tt.src)
got, err := findDefaultTags(tt.src)
tt.wantErr(t, err)
if err != nil {
return
+5 -5
View File
@@ -86,14 +86,14 @@ func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.E
func processExecutableLocation(loc file.Location, resolver file.Resolver) (*file.Executable, error) {
reader, err := resolver.FileContentsByLocation(loc)
if err != nil {
log.WithFields("error", err).Warnf("unable to get file contents for %q", loc.RealPath)
log.WithFields("error", err, "path", loc.RealPath).Debug("unable to get file contents")
return nil, fmt.Errorf("unable to get file contents: %w", err)
}
defer internal.CloseAndLogError(reader, loc.RealPath)
uReader, err := unionreader.GetUnionReader(reader)
if err != nil {
log.WithFields("error", err).Warnf("unable to get union reader for %q", loc.RealPath)
log.WithFields("error", err, "path", loc.RealPath).Debug("unable to get union reader")
return nil, fmt.Errorf("unable to get union reader: %w", err)
}
@@ -168,17 +168,17 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
switch format {
case file.ELF:
if err = findELFFeatures(&data, reader); err != nil {
log.WithFields("error", err).Tracef("unable to determine ELF features for %q", loc.RealPath)
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine ELF features")
err = fmt.Errorf("unable to determine ELF features: %w", err)
}
case file.PE:
if err = findPEFeatures(&data, reader); err != nil {
log.WithFields("error", err).Tracef("unable to determine PE features for %q", loc.RealPath)
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine PE features")
err = fmt.Errorf("unable to determine PE features: %w", err)
}
case file.MachO:
if err = findMachoFeatures(&data, reader); err != nil {
log.WithFields("error", err).Tracef("unable to determine Macho features for %q", loc.RealPath)
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine Macho features")
err = fmt.Errorf("unable to determine Macho features: %w", err)
}
}
@@ -14,14 +14,14 @@ func AllRegularFiles(ctx context.Context, resolver file.Resolver) (locations []f
for location := range resolver.AllLocations(ctx) {
resolvedLocations, err := resolver.FilesByPath(location.RealPath)
if err != nil {
log.Warnf("unable to resolve %+v: %+v", location, err)
log.Debugf("unable to resolve %+v: %+v", location, err)
continue
}
for _, resolvedLocation := range resolvedLocations {
metadata, err := resolver.FileMetadataByLocation(resolvedLocation)
if err != nil {
log.Warnf("unable to get metadata for %+v: %+v", location, err)
log.Debugf("unable to get metadata for %+v: %+v", location, err)
continue
}
+1 -1
View File
@@ -24,7 +24,7 @@ func (c Coordinates) ID() artifact.ID {
f, err := artifact.IDByHash(c)
if err != nil {
// TODO: what to do in this case?
log.Warnf("unable to get fingerprint of location coordinate=%+v: %+v", c, err)
log.Debugf("unable to get fingerprint of location coordinate=%+v: %+v", c, err)
return ""
}
+1
View File
@@ -10,6 +10,7 @@ type License struct {
SPDXExpression string
Type license.Type
LicenseEvidence *LicenseEvidence // evidence from license classifier
Contents string `hash:"ignore"`
}
type LicenseEvidence struct {
@@ -6,12 +6,14 @@ import (
"strings"
"time"
"github.com/CycloneDX/cyclonedx-go"
cyclonedx "github.com/CycloneDX/cyclonedx-go"
"github.com/google/uuid"
stfile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/format/internal/cyclonedxutil/helpers"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
@@ -19,6 +21,18 @@ import (
"github.com/anchore/syft/syft/source"
)
var cycloneDXValidHash = map[string]cyclonedx.HashAlgorithm{
"sha1": cyclonedx.HashAlgoSHA1,
"md5": cyclonedx.HashAlgoMD5,
"sha256": cyclonedx.HashAlgoSHA256,
"sha384": cyclonedx.HashAlgoSHA384,
"sha512": cyclonedx.HashAlgoSHA512,
"blake2b256": cyclonedx.HashAlgoBlake2b_256,
"blake2b384": cyclonedx.HashAlgoBlake2b_384,
"blake2b512": cyclonedx.HashAlgoBlake2b_512,
"blake3": cyclonedx.HashAlgoBlake3,
}
func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
cdxBOM := cyclonedx.NewBOM()
@@ -28,12 +42,49 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
cdxBOM.SerialNumber = uuid.New().URN()
cdxBOM.Metadata = toBomDescriptor(s.Descriptor.Name, s.Descriptor.Version, s.Source)
// Packages
packages := s.Artifacts.Packages.Sorted()
components := make([]cyclonedx.Component, len(packages))
for i, p := range packages {
components[i] = helpers.EncodeComponent(p)
}
components = append(components, toOSComponent(s.Artifacts.LinuxDistribution)...)
// Files
artifacts := s.Artifacts
coordinates := s.AllCoordinates()
for _, coordinate := range coordinates {
var metadata *file.Metadata
// File Info
fileMetadata, exists := artifacts.FileMetadata[coordinate]
// no file metadata then don't include in SBOM
// the syft config allows for sometimes only capturing files owned by packages
// so there can be a map miss here where we have less metadata than all coordinates
if !exists {
continue
}
if fileMetadata.Type == stfile.TypeDirectory ||
fileMetadata.Type == stfile.TypeSocket ||
fileMetadata.Type == stfile.TypeSymLink {
// skip dir, symlinks and sockets for the final bom
continue
}
metadata = &fileMetadata
// Digests
var digests []file.Digest
if digestsForLocation, exists := artifacts.FileDigests[coordinate]; exists {
digests = digestsForLocation
}
cdxHashes := digestsToHashes(digests)
components = append(components, cyclonedx.Component{
BOMRef: string(coordinate.ID()),
Type: cyclonedx.ComponentTypeFile,
Name: metadata.Path,
Hashes: &cdxHashes,
})
}
cdxBOM.Components = &components
dependencies := toDependencies(s.Relationships)
@@ -44,6 +95,22 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
return cdxBOM
}
func digestsToHashes(digests []file.Digest) []cyclonedx.Hash {
var hashes []cyclonedx.Hash
for _, digest := range digests {
lookup := strings.ToLower(digest.Algorithm)
cdxAlgo, exists := cycloneDXValidHash[lookup]
if !exists {
continue
}
hashes = append(hashes, cyclonedx.Hash{
Algorithm: cdxAlgo,
Value: digest.Value,
})
}
return hashes
}
func toOSComponent(distro *linux.Release) []cyclonedx.Component {
if distro == nil {
return []cyclonedx.Component{}
@@ -235,7 +302,7 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
}
bomRef, err := artifact.IDByHash(metadata.ID)
if err != nil {
log.Warnf("unable to get fingerprint of source image metadata=%s: %+v", metadata.ID, err)
log.Debugf("unable to get fingerprint of source image metadata=%s: %+v", metadata.ID, err)
}
return &cyclonedx.Component{
BOMRef: string(bomRef),
@@ -249,7 +316,7 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
}
bomRef, err := artifact.IDByHash(metadata.Path)
if err != nil {
log.Warnf("unable to get fingerprint of source directory metadata path=%s: %+v", metadata.Path, err)
log.Debugf("unable to get fingerprint of source directory metadata path=%s: %+v", metadata.Path, err)
}
return &cyclonedx.Component{
BOMRef: string(bomRef),
@@ -264,7 +331,7 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
}
bomRef, err := artifact.IDByHash(metadata.Path)
if err != nil {
log.Warnf("unable to get fingerprint of source file metadata path=%s: %+v", metadata.Path, err)
log.Debugf("unable to get fingerprint of source file metadata path=%s: %+v", metadata.Path, err)
}
return &cyclonedx.Component{
BOMRef: string(bomRef),
@@ -9,7 +9,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
stfile "github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/format/internal/cyclonedxutil/helpers"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
@@ -143,6 +145,178 @@ func Test_relationships(t *testing.T) {
}
}
func Test_FileComponents(t *testing.T) {
p1 := pkg.Package{
Name: "p1",
}
tests := []struct {
name string
sbom sbom.SBOM
want []cyclonedx.Component
}{
{
name: "sbom coordinates with file metadata are serialized to cdx along with packages",
sbom: sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(p1),
FileMetadata: map[file.Coordinates]file.Metadata{
{RealPath: "/test"}: {Path: "/test", Type: stfile.TypeRegular},
},
FileDigests: map[file.Coordinates][]file.Digest{
{RealPath: "/test"}: {
{
Algorithm: "sha256",
Value: "xyz12345",
},
},
},
},
},
want: []cyclonedx.Component{
{
BOMRef: "2a1fc74ade23e357",
Type: cyclonedx.ComponentTypeLibrary,
Name: "p1",
},
{
BOMRef: "3f31cb2d98be6c1e",
Name: "/test",
Type: cyclonedx.ComponentTypeFile,
Hashes: &[]cyclonedx.Hash{
{Algorithm: "SHA-256", Value: "xyz12345"},
},
},
},
},
{
name: "sbom coordinates that don't contain metadata are not added to the final output",
sbom: sbom.SBOM{
Artifacts: sbom.Artifacts{
FileMetadata: map[file.Coordinates]file.Metadata{
{RealPath: "/test"}: {Path: "/test", Type: stfile.TypeRegular},
},
FileDigests: map[file.Coordinates][]file.Digest{
{RealPath: "/test"}: {
{
Algorithm: "sha256",
Value: "xyz12345",
},
},
{RealPath: "/test-2"}: {
{
Algorithm: "sha256",
Value: "xyz678910",
},
},
},
},
},
want: []cyclonedx.Component{
{
BOMRef: "3f31cb2d98be6c1e",
Name: "/test",
Type: cyclonedx.ComponentTypeFile,
Hashes: &[]cyclonedx.Hash{
{Algorithm: "SHA-256", Value: "xyz12345"},
},
},
},
},
{
name: "sbom coordinates that return hashes not covered by cdx only include valid digests",
sbom: sbom.SBOM{
Artifacts: sbom.Artifacts{
FileMetadata: map[file.Coordinates]file.Metadata{
{RealPath: "/test"}: {Path: "/test", Type: stfile.TypeRegular},
},
FileDigests: map[file.Coordinates][]file.Digest{
{RealPath: "/test"}: {
{
Algorithm: "xxh64",
Value: "xyz12345",
},
{
Algorithm: "sha256",
Value: "xyz678910",
},
},
},
},
},
want: []cyclonedx.Component{
{
BOMRef: "3f31cb2d98be6c1e",
Name: "/test",
Type: cyclonedx.ComponentTypeFile,
Hashes: &[]cyclonedx.Hash{
{Algorithm: "SHA-256", Value: "xyz678910"},
},
},
},
},
{
name: "sbom coordinates who's metadata is directory or symlink are skipped",
sbom: sbom.SBOM{
Artifacts: sbom.Artifacts{
FileMetadata: map[file.Coordinates]file.Metadata{
{RealPath: "/testdir"}: {
Path: "/testdir",
Type: stfile.TypeDirectory,
},
{RealPath: "/testsym"}: {
Path: "/testsym",
Type: stfile.TypeSymLink,
},
{RealPath: "/test"}: {Path: "/test", Type: stfile.TypeRegular},
},
FileDigests: map[file.Coordinates][]file.Digest{
{RealPath: "/test"}: {
{
Algorithm: "sha256",
Value: "xyz12345",
},
},
},
},
},
want: []cyclonedx.Component{
{
BOMRef: "3f31cb2d98be6c1e",
Name: "/test",
Type: cyclonedx.ComponentTypeFile,
Hashes: &[]cyclonedx.Hash{
{Algorithm: "SHA-256", Value: "xyz12345"},
},
},
},
},
{
name: "sbom with no files serialized correctly",
sbom: sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(p1),
},
},
want: []cyclonedx.Component{
{
BOMRef: "2a1fc74ade23e357",
Type: cyclonedx.ComponentTypeLibrary,
Name: "p1",
},
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cdx := ToFormatModel(test.sbom)
got := *cdx.Components
if diff := cmp.Diff(test.want, got); diff != "" {
t.Errorf("cdx file components mismatch (-want +got):\n%s", diff)
}
})
}
}
func Test_toBomDescriptor(t *testing.T) {
type args struct {
name string
@@ -15,6 +15,7 @@ import (
"github.com/spdx/tools-golang/spdx"
"github.com/anchore/packageurl-go"
internallicenses "github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/mimetype"
"github.com/anchore/syft/internal/relationship"
@@ -762,10 +763,16 @@ func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense {
if license.Value == "" {
value, _ = strings.CutPrefix(license.ID, "LicenseRef-")
}
result = append(result, &spdx.OtherLicense{
other := &spdx.OtherLicense{
LicenseIdentifier: license.ID,
ExtractedText: value,
})
}
customPrefix := spdxlicense.LicenseRefPrefix + helpers.SanitizeElementID(internallicenses.UnknownLicensePrefix)
if strings.HasPrefix(license.ID, customPrefix) {
other.LicenseName = strings.TrimPrefix(license.ID, customPrefix)
other.LicenseComment = strings.Trim(internallicenses.UnknownLicensePrefix, "-_")
}
result = append(result, other)
}
return result
}
@@ -72,7 +72,7 @@ func Test_toFormatModel(t *testing.T) {
{
Category: "PACKAGE-MANAGER",
RefType: "purl",
Locator: "pkg:oci/alpine@sha256:d34db33f?arch=&tag=latest",
Locator: "pkg:oci/alpine@sha256%3Ad34db33f?arch=&tag=latest",
},
},
PackageSupplier: &spdx.Supplier{
+1 -1
View File
@@ -164,7 +164,7 @@ func toDependencies(s *sbom.SBOM, p pkg.Package) (out []string) {
func dependencyName(p pkg.Package) string {
purl, err := packageurl.FromString(p.PURL)
if err != nil {
log.Warnf("Invalid PURL for package: '%s' PURL: '%s' (%w)", p.Name, p.PURL, err)
log.Debugf("Invalid PURL for package: '%s' PURL: '%s' (%w)", p.Name, p.PURL, err)
return ""
}
// don't use qualifiers for this
@@ -7,7 +7,7 @@
"version": "v0.42.0-bogus"
},
"metadata": {
"syft:distro": "pkg:generic/debian@1.2.3?like=like!"
"syft:distro": "pkg:generic/debian@1.2.3?like=like%21"
},
"manifests": {
"redacted/some/path/some/path/pkg1": {
@@ -7,7 +7,7 @@
"version": "v0.42.0-bogus"
},
"metadata": {
"syft:distro": "pkg:generic/debian@1.2.3?like=like!"
"syft:distro": "pkg:generic/debian@1.2.3?like=like%21"
},
"manifests": {
"user-image-input:/somefile-1.txt": {
@@ -1,10 +1,12 @@
package helpers
import (
"encoding/base64"
"strings"
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/syft/internal/licenses"
"github.com/anchore/syft/internal/spdxlicense"
"github.com/anchore/syft/syft/pkg"
)
@@ -110,7 +112,7 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression
continue
}
if l.SPDXExpression != "" {
if l.SPDXExpression != "" && !strings.HasPrefix(l.SPDXExpression, licenses.UnknownLicensePrefix) {
// COMPLEX EXPRESSION CASE
ex = append(ex, l.SPDXExpression)
continue
@@ -118,17 +120,43 @@ func separateLicenses(p pkg.Package) (spdx, other cyclonedx.Licenses, expression
// license string that are not valid spdx expressions or ids
// we only use license Name here since we cannot guarantee that the license is a valid SPDX expression
if len(l.URLs) > 0 {
if len(l.URLs) > 0 && !strings.HasPrefix(l.SPDXExpression, licenses.UnknownLicensePrefix) {
processLicenseURLs(l, "", &otherc)
continue
}
otherc = append(otherc, cyclonedx.LicenseChoice{
otherc = append(otherc, processCustomLicense(l)...)
}
return spdxc, otherc, ex
}
func processCustomLicense(l pkg.License) cyclonedx.Licenses {
result := cyclonedx.Licenses{}
if strings.HasPrefix(l.SPDXExpression, licenses.UnknownLicensePrefix) {
cyclonedxLicense := &cyclonedx.License{
Name: l.SPDXExpression,
}
if len(l.URLs) > 0 {
cyclonedxLicense.URL = l.URLs[0]
}
if len(l.Contents) > 0 {
cyclonedxLicense.Text = &cyclonedx.AttachedText{
Content: base64.StdEncoding.EncodeToString([]byte(l.Contents)),
}
cyclonedxLicense.Text.ContentType = "text/plain"
cyclonedxLicense.Text.Encoding = "base64"
}
result = append(result, cyclonedx.LicenseChoice{
License: cyclonedxLicense,
})
} else {
result = append(result, cyclonedx.LicenseChoice{
License: &cyclonedx.License{
Name: l.Value,
},
})
}
return spdxc, otherc, ex
return result
}
func processLicenseURLs(l pkg.License, spdxID string, populate *cyclonedx.Licenses) {
@@ -136,7 +136,7 @@ func encode(out map[string]string, value reflect.Value, prefix string, fn FieldN
encode(out, value.MapIndex(key), fmt.Sprintf("%s:%v", prefix, key.Interface()), fn)
}
default:
log.Warnf("skipping encoding of unsupported property: %s", prefix)
log.Debugf("skipping encoding of unsupported property: %s", prefix)
}
}
@@ -213,7 +213,7 @@ func decode(vals map[string]string, value reflect.Value, prefix string, fn Field
}
if decode(vals, v.Elem(), prefix, fn) && value.CanSet() {
o := v.Interface()
log.Infof("%v", o)
log.Tracef("%v", o)
value.Set(v)
} else {
return false
@@ -355,7 +355,7 @@ func decode(vals map[string]string, value reflect.Value, prefix string, fn Field
}
return values
default:
log.Warnf("unable to set field: %s", prefix)
log.Debugf("unable to set field: %s", prefix)
return false
}
return true
@@ -5,6 +5,7 @@ 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"
@@ -69,19 +70,26 @@ func ParseLicenses(raw []pkg.License) (concluded, declared []SPDXLicense) {
}
candidate := SPDXLicense{}
if l.SPDXExpression != "" {
if l.SPDXExpression != "" && !strings.HasPrefix(l.SPDXExpression, licenses.UnknownLicensePrefix) {
candidate.ID = l.SPDXExpression
} else {
// we did not find a valid SPDX license ID so treat as separate license
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.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)
}
}
}
switch l.Type {
@@ -43,6 +43,10 @@ func Originator(p pkg.Package) (typ string, author string) { //nolint: funlen
case pkg.ApkDBEntry:
author = metadata.Maintainer
case pkg.BitnamiSBOMEntry:
typ = orgType
author = "Bitnami"
case pkg.DotnetPortableExecutableEntry:
typ = orgType
author = metadata.CompanyName
@@ -12,6 +12,7 @@ import (
func Test_OriginatorSupplier(t *testing.T) {
completionTester := packagemetadata.NewCompletionTester(t,
pkg.BinarySignature{},
pkg.BitnamiSBOMEntry{},
pkg.CocoaPodfileLockEntry{},
pkg.ConanV1LockEntry{},
pkg.ConanV2LockEntry{}, // the field Username might be the username of either the package originator or the supplier (unclear currently)
@@ -89,6 +90,14 @@ func Test_OriginatorSupplier(t *testing.T) {
originator: "",
supplier: "Person: someone",
},
{
name: "from bitnami",
input: pkg.Package{
Metadata: pkg.BitnamiSBOMEntry{},
},
originator: "Organization: Bitnami",
supplier: "Organization: Bitnami",
},
{
name: "from dotnet -- PE binary",
input: pkg.Package{
@@ -372,6 +381,14 @@ func Test_OriginatorSupplier(t *testing.T) {
originator: "",
supplier: "",
},
{
name: "from terraform lock",
input: pkg.Package{
Metadata: pkg.TerraformLockProviderEntry{},
},
originator: "",
supplier: "",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@@ -16,6 +16,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from RPM DB"
case pkg.ApkPkg:
answer = "acquired package info from APK DB"
case pkg.BitnamiPkg:
answer = "acquired package info from a Bitnami SBOM"
case pkg.DartPubPkg:
answer = "acquired package info from pubspec manifest"
case pkg.DebPkg:
@@ -32,6 +34,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from installed gem metadata file"
case pkg.GoModulePkg:
answer = "acquired package info from go module information"
case pkg.GraalVMNativeImagePkg:
answer = "acquired package info from GraalVM native image"
case pkg.RustPkg:
answer = "acquired package info from rust cargo manifest"
case pkg.PhpComposerPkg:
@@ -70,6 +74,8 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from GitHub Actions workflow file or composite action file"
case pkg.WordpressPluginPkg:
answer = "acquired package info from found wordpress plugin PHP source files"
case pkg.TerraformPkg:
answer = "acquired package info from Terraform dependency lock file"
default:
answer = "acquired package info from the following paths"
}
@@ -111,6 +111,14 @@ func Test_SourceInfo(t *testing.T) {
"from go module information",
},
},
{
input: pkg.Package{
Type: pkg.GraalVMNativeImagePkg,
},
expected: []string{
"from GraalVM native image",
},
},
{
input: pkg.Package{
Type: pkg.RustPkg,
@@ -199,6 +207,14 @@ func Test_SourceInfo(t *testing.T) {
"acquired package info from the following paths",
},
},
{
input: pkg.Package{
Type: pkg.BitnamiPkg,
},
expected: []string{
"acquired package info from a Bitnami SBOM",
},
},
{
input: pkg.Package{
Type: pkg.HexPkg,
@@ -303,6 +319,14 @@ func Test_SourceInfo(t *testing.T) {
"acquired package info from found wordpress plugin PHP source files",
},
},
{
input: pkg.Package{
Type: pkg.TerraformPkg,
},
expected: []string{
"acquired package info from Terraform dependency lock file",
},
},
}
var pkgTypes []pkg.Type
for _, test := range tests {
@@ -81,7 +81,7 @@
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch="
"referenceLocator": "pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch="
}
],
"primaryPackagePurpose": "CONTAINER"
@@ -81,7 +81,7 @@
{
"referenceCategory": "PACKAGE-MANAGER",
"referenceType": "purl",
"referenceLocator": "pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch="
"referenceLocator": "pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch="
}
],
"primaryPackagePurpose": "CONTAINER"
@@ -71,7 +71,7 @@ PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf4
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageCopyrightText: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=
##### Package: package-2
@@ -21,7 +21,7 @@ PackageChecksum: SHA256: 2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf4
PackageLicenseConcluded: NOASSERTION
PackageLicenseDeclared: NOASSERTION
PackageCopyrightText: NOASSERTION
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256:2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=
ExternalRef: PACKAGE-MANAGER purl pkg:oci/user-image-input@sha256%3A2731251dc34951c0e50fcc643b4c5f74922dad1a5d98f302b504cf46cd5d9368?arch=
##### Package: package-2
+1
View File
@@ -51,6 +51,7 @@ type License struct {
Type license.Type `json:"type"`
URLs []string `json:"urls"`
Locations []file.Location `json:"locations"`
Contents string `json:"contents,omitempty"`
}
func newModelLicensesFromValues(licenses []string) (ml []License) {
+2 -1
View File
@@ -160,7 +160,7 @@ func toFileMetadataEntry(coordinates file.Coordinates, metadata *file.Metadata)
mode, err = strconv.Atoi(fmt.Sprintf("%o", metadata.Mode()))
if err != nil {
log.Warnf("invalid mode found in file catalog @ location=%+v mode=%q: %+v", coordinates, metadata.Mode, err)
log.Debugf("invalid mode found in file catalog @ location=%+v mode=%q: %+v", coordinates, metadata.Mode, err)
mode = 0
}
@@ -234,6 +234,7 @@ func toLicenseModel(pkgLicenses []pkg.License) (modelLicenses []model.License) {
Type: l.Type,
URLs: urls,
Locations: locations,
Contents: l.Contents,
})
}
return
+1
View File
@@ -164,6 +164,7 @@ func toSyftLicenses(m []model.License) (p []pkg.License) {
Type: l.Type,
URLs: l.URLs,
Locations: file.NewLocationSet(l.Locations...),
Contents: l.Contents,
})
}
return
+1 -1
View File
@@ -34,7 +34,7 @@ type pathSkipper struct {
func skipPathsByMountTypeAndName(root string) PathIndexVisitor {
infos, err := mountinfo.GetMounts(nil)
if err != nil {
log.WithFields("error", err).Warnf("unable to get system mounts")
log.WithFields("error", err).Debug("unable to get system mounts")
return func(_ string, _ string, _ os.FileInfo, _ error) error {
return nil
}
@@ -10,6 +10,7 @@ func AllTypes() []any {
pkg.AlpmDBEntry{},
pkg.ApkDBEntry{},
pkg.BinarySignature{},
pkg.BitnamiSBOMEntry{},
pkg.CocoaPodfileLockEntry{},
pkg.ConanV1LockEntry{},
pkg.ConanV2LockEntry{},
@@ -53,6 +54,7 @@ func AllTypes() []any {
pkg.RustCargoLockEntry{},
pkg.SwiftPackageManagerResolvedEntry{},
pkg.SwiplPackEntry{},
pkg.TerraformLockProviderEntry{},
pkg.WordpressPluginEntry{},
pkg.YarnLockEntry{},
}

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