Compare commits

...
36 Commits
Author SHA1 Message Date
b15c5dbfe2 chore(deps): update anchore dependencies (#4960)
* chore(deps): update anchore dependencies

Signed-off-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>

* update snapshots

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

---------

Signed-off-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-25 15:26:14 -04:00
Alex Goodman 35d56bfb99 Update go-make to v0.8.0 (#5010)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-25 15:01:40 -04:00
Alex Goodman abf6d78dfc fixes the wrapped taskfile-tasks (#5013)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-24 11:22:16 -04:00
Will Murphy fe42bcec38 fix(purl-backfill): respect arch qualifier (#4987)
* fix(purl-backfill): respect arch qualifier

Previously, when constructing rpm, alpm, and apk metadata struct from a
PURL, Syft would ignore the arch qualifier. Start respecting that
qualifier.

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

* chore: fix static analysis

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

* Clean up control flow in PURL backfill code

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

---------

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2026-06-23 15:23:46 -04:00
Rez MossandChristopher Phillips fea4a50124 feat: deno cataloger #4417 (#4523)
Signed-off-by: Rez Moss <hi@rezmoss.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2026-06-23 10:58:22 -04:00
dependabot[bot] 5eefd73ac7 chore(deps): bump golang.org/x/tools from 0.45.0 to 0.46.0 (#5008)
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.45.0 to 0.46.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.45.0...v0.46.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 18:50:24 +00:00
dependabot[bot] 684c7018be chore(deps): bump golang.org/x/net from 0.55.0 to 0.56.0 (#5004)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.55.0 to 0.56.0.
- [Commits](https://github.com/golang/net/compare/v0.55.0...v0.56.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 18:34:47 +00:00
dependabot[bot] f827f91ec1 chore(deps): bump golang.org/x/mod from 0.36.0 to 0.37.0 (#5007)
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.36.0 to 0.37.0.
- [Commits](https://github.com/golang/mod/compare/v0.36.0...v0.37.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 18:19:47 +00:00
dependabot[bot] e9af7d218c chore(deps): bump github.com/jedib0t/go-pretty/v6 from 6.7.10 to 6.8.1 (#5006)
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.7.10 to 6.8.1.
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.7.10...v6.8.1)

---
updated-dependencies:
- dependency-name: github.com/jedib0t/go-pretty/v6
  dependency-version: 6.8.1
  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>
2026-06-22 18:18:54 +00:00
Alex Goodman 506ad5d6a7 refactor release pipeline: TAG_TOKEN, skip-checks gate, dependabot/zizmor cleanup (#5003)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-22 14:04:48 -04:00
Rez Moss 1f4f9332c5 feat: support envoy bin classifier
Signed-off-by: Rez Moss <hi@rezmoss.com>
2026-06-22 13:16:33 -04:00
Rez Moss 52a4c3b594 feat: elastic beats bin classifier (#4969)
Signed-off-by: Rez Moss <hi@rezmoss.com>
2026-06-22 11:49:44 -04:00
Keith Zantow 9c321691d4 feat: SPDX 3 (#4269)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-06-22 10:59:34 -04:00
Alex Goodman 0e8d6deabe require tmpdir to exist for fingerprints (#5002)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-22 10:54:26 -04:00
dependabot[bot] deb2fd92ef chore(deps): bump github.com/containerd/containerd/v2 (#5001)
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.3.1 to 2.3.2.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.3.1...v2.3.2)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-version: 2.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-22 14:12:11 +00:00
Alex Goodman 80d3b62de4 bump go-make to v0.7.0 (#4999)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-22 09:47:58 -04:00
anchore-oss-update-botandanchore-oss-update-bot b71afc87fc chore(deps): update tool versions (#4994)
Signed-off-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
Co-authored-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
2026-06-19 11:51:05 +00:00
Alex Goodman efe3174b5f Preserve dependency edges when a compliance stub changes a package ID (#4993)
* fix relationship rewrites for isolated nodes

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

* cover dangling pointers

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-18 19:50:30 -04:00
Rez Moss 58e4dbbf01 feat: added bin classifier elastic-agent (#4968)
Signed-off-by: Rez Moss <hi@rezmoss.com>
2026-06-17 15:29:07 +00:00
Sebastiaan van Stijn b70fa899cb golangci-lint: enable gci formatter (#4828)
This allows linting the imports to be grouped correctly, and provides
an auto-fix (`golangci-lint run --fix`).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2026-06-17 10:34:22 -04:00
Alex Goodman 951fbd454a add purl types to cataloger info cmd (#4984)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-16 12:13:34 -04:00
Rez Moss 92ae4d44c5 fix: .net deps.json cataloger no longer shows phantom pkgs (#4971)
Signed-off-by: Rez Moss <hi@rezmoss.com>
2026-06-16 12:02:42 -04:00
Alex Goodman 8d48a8b8c2 ensure we have a snapshot build for cli tests (#4981)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-16 10:41:33 -04:00
David DashtiandAlex Goodman cff5a05681 fix(dpkg): extract License field for opkg/ipkg entries (#4963)
* fix(dpkg): extract License field for opkg/ipkg entries

opkg and ipkg use the dpkg cataloger but declare the package License
inline in the status DB (unlike Debian dpkg, where licenses live in
copyright files). The cataloger silently dropped the License field at
mapstructure decode time, so all opkg-managed packages reported empty
licenses.

This adds the field to the intermediate decode struct and the public
DpkgDBEntry, and populates licenses in newDpkgPackage using the alpine
cataloger's pattern: try license.ParseExpression first to keep valid
SPDX expressions whole, fall back to whitespace splitting for
space-separated lists.

Standard Debian dpkg status files never carry a License field per
Debian policy, so the new path is a no-op for them; the existing
copyright-file lookup in addLicenses is unaffected.

Closes #4940

Signed-off-by: David Dashti <47575784+Dashtid@users.noreply.github.com>

* remove license from dpkg metadata struct

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

* restore format snapshot files

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

* add additional tests

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

---------

Signed-off-by: David Dashti <47575784+Dashtid@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-06-15 16:15:32 -04:00
Kursat TopcuogluandKursat Topcuoglu 00ca43d24a fix: catalog uv PEP 723 script lockfiles (*.py.lock) (#4950)
Signed-off-by: Kursat Topcuoglu <7313835+ktopcuoglu@users.noreply.github.com>
Co-authored-by: Kursat Topcuoglu <7313835+ktopcuoglu@users.noreply.github.com>
2026-06-15 11:34:02 -04:00
dependabot[bot] 6a27678036 chore(deps): bump the actions-minor-patch group across 2 directories with 6 updates (#4975)
Bumps the actions-minor-patch group with 5 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [anchore/workflows/.github/workflows/codeql.yaml](https://github.com/anchore/workflows) | `0.7.0` | `0.7.2` |
| [anchore/workflows/.github/workflows/check-version-available.yaml](https://github.com/anchore/workflows) | `0.7.0` | `0.7.2` |
| [anchore/workflows/.github/workflows/check-gate.yaml](https://github.com/anchore/workflows) | `0.7.0` | `0.7.2` |
| [actions/checkout](https://github.com/actions/checkout) | `6.0.2` | `6.0.3` |
| [anchore/workflows/.github/workflows/release-install-script.yaml](https://github.com/anchore/workflows) | `0.7.0` | `0.7.2` |

Bumps the actions-minor-patch group with 1 update in the /.github/actions/bootstrap directory: [anchore/go-make](https://github.com/anchore/go-make).


Updates `anchore/workflows/.github/workflows/codeql.yaml` from 0.7.0 to 0.7.2
- [Release notes](https://github.com/anchore/workflows/releases)
- [Commits](https://github.com/anchore/workflows/compare/b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5...b0c30a80409130d329aaa356fd64a34d8c0b3375)

Updates `anchore/workflows/.github/workflows/check-version-available.yaml` from 0.7.0 to 0.7.2
- [Release notes](https://github.com/anchore/workflows/releases)
- [Commits](https://github.com/anchore/workflows/compare/b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5...b0c30a80409130d329aaa356fd64a34d8c0b3375)

Updates `anchore/workflows/.github/workflows/check-gate.yaml` from 0.7.0 to 0.7.2
- [Release notes](https://github.com/anchore/workflows/releases)
- [Commits](https://github.com/anchore/workflows/compare/b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5...b0c30a80409130d329aaa356fd64a34d8c0b3375)

Updates `actions/checkout` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/de0fac2e4500dabe0009e67214ff5f5447ce83dd...df4cb1c069e1874edd31b4311f1884172cec0e10)

Updates `anchore/workflows/.github/workflows/release-install-script.yaml` from 0.7.0 to 0.7.2
- [Release notes](https://github.com/anchore/workflows/releases)
- [Commits](https://github.com/anchore/workflows/compare/b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5...b0c30a80409130d329aaa356fd64a34d8c0b3375)

Updates `anchore/go-make` from 0.5.0 to 0.6.0
- [Release notes](https://github.com/anchore/go-make/releases)
- [Commits](https://github.com/anchore/go-make/compare/9de27be11ed73e2f9d5406a836a492b7d8aa1225...39fe5f71112d4dceb3ff0a92a40f272f067fc457)

---
updated-dependencies:
- dependency-name: anchore/workflows/.github/workflows/codeql.yaml
  dependency-version: 0.7.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: anchore/workflows/.github/workflows/check-version-available.yaml
  dependency-version: 0.7.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: anchore/workflows/.github/workflows/check-gate.yaml
  dependency-version: 0.7.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: actions/checkout
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: anchore/workflows/.github/workflows/release-install-script.yaml
  dependency-version: 0.7.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: anchore/go-make
  dependency-version: 0.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-12 13:29:23 +00:00
Keith Zantow 89773c0a12 fix: support CycloneDX 1.7 (#4967)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-06-11 09:40:42 -04:00
Yoonho HannandChristopher Phillips b08d3c2970 feat: add support for Bun lockfile (#4625)
---------
Signed-off-by: Yoonho Hann <hnnynh125@gmail.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2026-06-09 13:22:43 -04:00
Keith Zantow 63232bf725 fix: local version identifiers in python requirements parsing (#4959)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2026-06-08 11:12:47 -04:00
MarcusandChristopher Phillips 908eb57890 feat: add .bpl extension to PE cataloger (#4954)
BPL (Borland Package Library) files are standard PE/DLL format used by
Delphi and C++Builder. Adding the extension to the glob list so syft
picks them up during directory scans without users needing to rename
to .dll first.
---------
Signed-off-by: jfjrh2014 <jfjrh2014@gmail.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2026-06-08 10:07:15 -04:00
Arpit Jain c5c423ab37 fix: detect mariadb version from RHEL build path (#4952)
Signed-off-by: Arpit Jain <arpitjain099@gmail.com>
2026-06-07 13:28:18 -04:00
anchore-oss-update-botandanchore-oss-update-bot d4496b05aa chore(deps): update anchore dependencies (#4934)
Signed-off-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
Co-authored-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
2026-06-05 13:55:57 +00:00
dependabot[bot] adc55cdb3a chore(deps): bump the go-minor-patch group across 1 directory with 3 updates (#4957)
Bumps the go-minor-patch group with 3 updates in the / directory: [github.com/gkampitakis/go-snaps](https://github.com/gkampitakis/go-snaps), [github.com/gpustack/gguf-parser-go](https://github.com/gpustack/gguf-parser-go) and [modernc.org/sqlite](https://gitlab.com/cznic/sqlite).


Updates `github.com/gkampitakis/go-snaps` from 0.5.21 to 0.5.22
- [Release notes](https://github.com/gkampitakis/go-snaps/releases)
- [Commits](https://github.com/gkampitakis/go-snaps/compare/v0.5.21...v0.5.22)

Updates `github.com/gpustack/gguf-parser-go` from 0.24.0 to 0.24.1
- [Release notes](https://github.com/gpustack/gguf-parser-go/releases)
- [Commits](https://github.com/gpustack/gguf-parser-go/compare/v0.24.0...v0.24.1)

Updates `modernc.org/sqlite` from 1.50.1 to 1.51.0
- [Changelog](https://gitlab.com/cznic/sqlite/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.50.1...v1.51.0)

---
updated-dependencies:
- dependency-name: github.com/gkampitakis/go-snaps
  dependency-version: 0.5.22
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-minor-patch
- dependency-name: github.com/gpustack/gguf-parser-go
  dependency-version: 0.24.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-minor-patch
- dependency-name: modernc.org/sqlite
  dependency-version: 1.51.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-06-05 13:28:05 +00:00
anchore-oss-update-botandanchore-oss-update-bot 00d0bb59cc chore(deps): update tool versions (#4724)
Signed-off-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
Co-authored-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
2026-06-05 11:22:28 +00:00
dependabot[bot]andChristopher Phillips f474308783 chore(deps): bump the go-minor-patch group across 2 directories with 14 updates (#4947)
* chore(deps): bump the go-minor-patch group across 2 directories with 14 updates

Bumps the go-minor-patch group with 9 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [github.com/CycloneDX/cyclonedx-go](https://github.com/CycloneDX/cyclonedx-go) | `0.10.0` | `0.11.0` |
| [github.com/Masterminds/semver/v3](https://github.com/Masterminds/semver) | `3.4.0` | `3.5.0` |
| [github.com/diskfs/go-diskfs](https://github.com/diskfs/go-diskfs) | `1.7.0` | `1.9.3` |
| [github.com/github/go-spdx/v2](https://github.com/github/go-spdx) | `2.4.0` | `2.7.0` |
| [github.com/google/go-containerregistry](https://github.com/google/go-containerregistry) | `0.21.5` | `0.21.6` |
| [github.com/gookit/color](https://github.com/gookit/color) | `1.6.0` | `1.6.1` |
| [github.com/invopop/jsonschema](https://github.com/invopop/jsonschema) | `0.13.0` | `0.14.0` |
| [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) | `6.7.8` | `6.7.10` |
| [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) | `1.46.2` | `1.50.1` |

Bumps the go-minor-patch group with 1 update in the /.make directory: [github.com/anchore/go-make](https://github.com/anchore/go-make).

Updates `github.com/CycloneDX/cyclonedx-go` from 0.10.0 to 0.11.0
- [Release notes](https://github.com/CycloneDX/cyclonedx-go/releases)
- [Commits](https://github.com/CycloneDX/cyclonedx-go/compare/v0.10.0...v0.11.0)

Updates `github.com/Masterminds/semver/v3` from 3.4.0 to 3.5.0
- [Release notes](https://github.com/Masterminds/semver/releases)
- [Changelog](https://github.com/Masterminds/semver/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Masterminds/semver/compare/v3.4.0...v3.5.0)

Updates `github.com/diskfs/go-diskfs` from 1.7.0 to 1.9.3
- [Commits](https://github.com/diskfs/go-diskfs/compare/v1.7.0...v1.9.3)

Updates `github.com/github/go-spdx/v2` from 2.4.0 to 2.7.0
- [Release notes](https://github.com/github/go-spdx/releases)
- [Commits](https://github.com/github/go-spdx/compare/v2.4.0...v2.7.0)

Updates `github.com/google/go-containerregistry` from 0.21.5 to 0.21.6
- [Release notes](https://github.com/google/go-containerregistry/releases)
- [Commits](https://github.com/google/go-containerregistry/compare/v0.21.5...v0.21.6)

Updates `github.com/gookit/color` from 1.6.0 to 1.6.1
- [Release notes](https://github.com/gookit/color/releases)
- [Commits](https://github.com/gookit/color/compare/v1.6.0...v1.6.1)

Updates `github.com/invopop/jsonschema` from 0.13.0 to 0.14.0
- [Release notes](https://github.com/invopop/jsonschema/releases)
- [Commits](https://github.com/invopop/jsonschema/compare/v0.13.0...v0.14.0)

Updates `github.com/jedib0t/go-pretty/v6` from 6.7.8 to 6.7.10
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.7.8...v6.7.10)

Updates `github.com/klauspost/compress` from 1.18.5 to 1.18.6
- [Release notes](https://github.com/klauspost/compress/releases)
- [Commits](https://github.com/klauspost/compress/compare/v1.18.5...v1.18.6)

Updates `golang.org/x/mod` from 0.35.0 to 0.36.0
- [Commits](https://github.com/golang/mod/compare/v0.35.0...v0.36.0)

Updates `golang.org/x/net` from 0.53.0 to 0.54.0
- [Commits](https://github.com/golang/net/compare/v0.53.0...v0.54.0)

Updates `golang.org/x/tools` from 0.44.0 to 0.45.0
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.44.0...v0.45.0)

Updates `modernc.org/sqlite` from 1.46.2 to 1.50.1
- [Changelog](https://gitlab.com/cznic/sqlite/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.46.2...v1.50.1)

Updates `github.com/anchore/go-make` from 0.4.0 to 0.5.0
- [Release notes](https://github.com/anchore/go-make/releases)
- [Commits](https://github.com/anchore/go-make/compare/v0.4.0...v0.5.0)

---
updated-dependencies:
- dependency-name: github.com/CycloneDX/cyclonedx-go
  dependency-version: 0.11.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/Masterminds/semver/v3
  dependency-version: 3.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/diskfs/go-diskfs
  dependency-version: 1.9.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/github/go-spdx/v2
  dependency-version: 2.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/google/go-containerregistry
  dependency-version: 0.21.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-minor-patch
- dependency-name: github.com/gookit/color
  dependency-version: 1.6.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-minor-patch
- dependency-name: github.com/invopop/jsonschema
  dependency-version: 0.14.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/jedib0t/go-pretty/v6
  dependency-version: 6.7.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-minor-patch
- dependency-name: github.com/klauspost/compress
  dependency-version: 1.18.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: go-minor-patch
- dependency-name: golang.org/x/mod
  dependency-version: 0.36.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: golang.org/x/net
  dependency-version: 0.54.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: golang.org/x/tools
  dependency-version: 0.45.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: modernc.org/sqlite
  dependency-version: 1.50.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
- dependency-name: github.com/anchore/go-make
  dependency-version: 0.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: go-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>

* fix: update signatures to return fs.FileInfo after breaking changes

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

* fix: lint-fix

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2026-06-04 17:06:25 -04:00
Will Murphy bf67072246 chore: bump golang.org/x/crypto (#4955)
* chore: bump golang.org/x/crypto

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

* also bump golang.org/x/net

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

---------

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2026-06-04 16:06:08 -04:00
177 changed files with 19177 additions and 1357 deletions
+2 -3
View File
@@ -1,6 +1,5 @@
# only pull in version updates that were released more than a week ago (low-pass filter for quickly-retracted releases)
cooldown: 7d
# Most tools (binny, chronicle, cosign, golangci-lint, goreleaser, gosimports,
# bouncer, quill, syft, task, gh) are inherited from anchore/go-make's embedded
# .binny.yaml — see https://github.com/anchore/go-make. Only syft-specific tools
@@ -17,7 +16,7 @@ tools:
# used to upload test fixture cache
- name: oras
version:
want: v1.3.1
want: v1.3.2
method: github-release
with:
repo: oras-project/oras
@@ -25,7 +24,7 @@ tools:
# used to parse JSON/YAML annotations on the fixture cache image
- name: yq
version:
want: v4.52.5
want: v4.53.3
method: github-release
with:
repo: mikefarah/yq
+1 -1
View File
@@ -30,7 +30,7 @@ runs:
using: "composite"
steps:
- name: Setup go + go-make tooling
uses: anchore/go-make/.github/actions/setup@9de27be11ed73e2f9d5406a836a492b7d8aa1225 # v0.5.0
uses: anchore/go-make/.github/actions/setup@39fe5f71112d4dceb3ff0a92a40f272f067fc457 # v0.6.0
with:
go-version: ${{ inputs.go-version }}
cache-key-prefix: ${{ inputs.cache-key-prefix }}
-25
View File
@@ -1,13 +1,4 @@
# Dependabot configuration
#
# Grouping behavior (see inline comments for details):
# - Minor + patch updates: grouped into a single PR per ecosystem
# - Major version bumps: individual PR per dependency
# - Security updates: individual PR per dependency
#
# Note: "patch" refers to semver version bumps (1.2.3 -> 1.2.4), not security fixes.
# Security updates are identified separately via GitHub's Advisory Database and
# can be any version bump (patch, minor, or major) that fixes a known CVE.
version: 2
@@ -30,14 +21,6 @@ updates:
- dependency-name: "github.com/aquasecurity/go-version"
- dependency-name: "github.com/knqyf263/go-apk-version"
- dependency-name: "github.com/knqyf263/go-deb-version"
groups:
go-minor-patch:
applies-to: version-updates # security updates get individual PRs
patterns:
- "*"
update-types: # major omitted, gets individual PRs
- "minor"
- "patch"
- package-ecosystem: "github-actions"
directories:
@@ -51,11 +34,3 @@ updates:
open-pull-requests-limit: 10
labels:
- "dependencies"
groups:
actions-minor-patch:
applies-to: version-updates # security updates get individual PRs
patterns:
- "*"
update-types: # major omitted, gets individual PRs
- "minor"
- "patch"
+1 -1
View File
@@ -13,7 +13,7 @@ permissions: {}
jobs:
analyze:
name: Analyze
uses: anchore/workflows/.github/workflows/codeql.yaml@b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5 # v0.7.0
uses: anchore/workflows/.github/workflows/codeql.yaml@b0c30a80409130d329aaa356fd64a34d8c0b3375 # v0.7.2
permissions:
security-events: write
packages: read
+20 -8
View File
@@ -21,22 +21,27 @@ on:
options:
- "all"
- "install-script-only"
skip-checks:
description: skip the check-gate (release even if checks haven't passed on main)
type: boolean
default: false
required: false
jobs:
version-available:
if: ${{ github.event.inputs.phase == 'all' }}
permissions:
contents: read # required for fetching tags
uses: anchore/workflows/.github/workflows/check-version-available.yaml@b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5 # v0.7.0
uses: anchore/workflows/.github/workflows/check-version-available.yaml@b0c30a80409130d329aaa356fd64a34d8c0b3375 # v0.7.2
with:
version: ${{ github.event.inputs.version }}
check-gate:
if: ${{ github.event.inputs.phase == 'all' }}
if: ${{ github.event.inputs.phase == 'all' && !inputs.skip-checks }}
permissions:
contents: read
checks: read # required for getting the status of specific check names
uses: anchore/workflows/.github/workflows/check-gate.yaml@b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5 # v0.7.0
uses: anchore/workflows/.github/workflows/check-gate.yaml@b0c30a80409130d329aaa356fd64a34d8c0b3375 # v0.7.2
with:
# these are checks that should be run on pull-request and merges to main.
# we do NOT want to kick off a release if these have not been verified on main.
@@ -45,7 +50,14 @@ jobs:
release:
needs: [check-gate, version-available]
if: ${{ github.event.inputs.phase == 'all' }}
# run even when check-gate is skipped, but never when version-available
# failed/was skipped, nor when check-gate failed or was cancelled. note:
# always() disables the implicit success() gate on ALL needs, so the
# version-available requirement must be re-asserted explicitly here.
if: >-
${{ always()
&& needs.version-available.result == 'success'
&& !contains(fromJSON('["failure", "cancelled"]'), needs.check-gate.result) }}
environment: release
# runs-on.com: compute instances for parallel builds
# spot disabled: reliability for build workflows (used for releases too)
@@ -58,7 +70,7 @@ jobs:
packages: write # required for publishing release artifacts to GitHub packages
id-token: write # required for keyless signing (cosign/sigstore OIDC)
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
with:
fetch-depth: 0
persist-credentials: true
@@ -82,8 +94,8 @@ jobs:
- name: Build & publish release artifacts
run: make ci-release
env:
# used for pushing tags
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
# for pushing tags (does not inherit workflow permissions)
TAG_TOKEN: ${{ secrets.TAG_TOKEN }}
RELEASE_VERSION: ${{ github.event.inputs.version }}
# for mac signing and notarization...
QUILL_SIGN_P12: ${{ secrets.ANCHORE_APPLE_DEVELOPER_ID_CERT_CHAIN }}
@@ -107,7 +119,7 @@ jobs:
if: ${{ always() && (needs.release.result == 'success' || github.event.inputs.phase == 'install-script-only') }}
permissions:
contents: read # required for the reusable workflow to check out the repo and publish the install script
uses: anchore/workflows/.github/workflows/release-install-script.yaml@b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5 # v0.7.0
uses: anchore/workflows/.github/workflows/release-install-script.yaml@b0c30a80409130d329aaa356fd64a34d8c0b3375 # v0.7.2
with:
tag: ${{ github.event.inputs.version }}
secrets:
@@ -20,7 +20,7 @@ jobs:
contents: read
security-events: write # for uploading SARIF results
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
with:
persist-credentials: false
+7 -7
View File
@@ -28,7 +28,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
with:
persist-credentials: false
@@ -50,7 +50,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
with:
persist-credentials: false
@@ -72,7 +72,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
with:
persist-credentials: false
@@ -99,7 +99,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
with:
persist-credentials: false
@@ -129,7 +129,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
with:
persist-credentials: false
@@ -173,7 +173,7 @@ jobs:
- name: Install Cosign
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
with:
persist-credentials: false
@@ -207,7 +207,7 @@ jobs:
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
with:
persist-credentials: false
-5
View File
@@ -1,9 +1,4 @@
rules:
unpinned-uses:
config:
policies:
# anchore/workflows is an internal repository; using @main is acceptable
anchore/*: any
dangerous-triggers:
ignore:
# Safe use of pull_request_target - only runs trusted scripts from base repo,
+8 -2
View File
@@ -70,7 +70,6 @@ linters:
# - godot
# - godox
# - goerr113
# - goimports # we're using gosimports now instead to account for extra whitespaces (see https://github.com/golang/go/issues/20818)
# - golint # deprecated
# - gomnd # this is too aggressive
# - interfacer # this is a good idea, but is no longer supported and is prone to false positives
@@ -96,11 +95,18 @@ issues:
formatters:
enable:
- gci
- gofmt
- goimports
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$
settings:
gci:
# See https://golangci-lint.run/docs/formatters/configuration/#gci
sections:
- standard # Standard section: captures all standard packages.
- default # Default section: contains all imports that could not be matched to another section type.
- prefix(github.com/anchore)
+4 -6
View File
@@ -2,13 +2,11 @@ module github.com/anchore/syft/.make
go 1.25.8
require (
github.com/anchore/go-make v0.4.0
github.com/goccy/go-yaml v1.19.2
)
require github.com/anchore/go-make v0.8.0
require (
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/sys v0.44.0 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
golang.org/x/mod v0.37.0 // indirect
golang.org/x/sys v0.46.0 // indirect
)
+6 -6
View File
@@ -1,10 +1,10 @@
github.com/anchore/go-make v0.4.0 h1:poFE4PXcHwvix2E8AhdM7YHZ15bMhZb+W02i3+qrP8M=
github.com/anchore/go-make v0.4.0/go.mod h1:Nc/tkwQHW1d1Vi8+0rtS/vSrH6pxieaUQXLdrctn+8g=
github.com/anchore/go-make v0.8.0 h1:3XAD4WNHIbqY4kwFyCQJ7kKiYLzSzjB0UwzDVwRqlUY=
github.com/anchore/go-make v0.8.0/go.mod h1:4M6TnArb5w693VyWsgr5dCWrk2BLNu/ed4JUcsrzS34=
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+58 -115
View File
@@ -1,28 +1,19 @@
package main
import (
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/goccy/go-yaml"
. "github.com/anchore/go-make"
"github.com/anchore/go-make/file"
"github.com/anchore/go-make/git"
"github.com/anchore/go-make/lang"
"github.com/anchore/go-make/run"
"github.com/anchore/go-make/tasks/golint"
"github.com/anchore/go-make/tasks/goreleaser"
"github.com/anchore/go-make/tasks/gotask"
"github.com/anchore/go-make/tasks/gotest"
)
// taskfileDescriptions maps Taskfile.yaml task names to their `desc:` field.
// Loaded at package init so wrap() can use Taskfile.yaml as the single source
// of truth for wrapped-task descriptions.
var taskfileDescriptions = mustReadTaskfileDescriptions()
func main() {
Makefile(
// shared anchore tasks
@@ -56,11 +47,14 @@ func main() {
},
},
// cli tests: native go-make Task. Requires SYFT_BINARY_LOCATION pointing at
// an *absolute* path to the snapshot binary. Intentionally does NOT depend
// on snapshot: in CI we download a pre-built snapshot artifact and re-running
// goreleaser here would both burn ~10m and clobber the downloaded binary.
// Locally, the failure message tells you to run `make snapshot` first.
// cli tests: native go-make Task. Runs SYFT_BINARY_LOCATION at an *absolute*
// path to the snapshot binary. Builds the snapshot only when the binary is
// missing rather than depending on the snapshot task unconditionally: in
// validations.yaml CI we download a pre-built snapshot artifact, so the binary
// already exists and rebuilding would both burn ~10m and clobber it. When
// `make test` runs cold (e.g. the release pipeline) or locally with no
// snapshot, we build a single-target snapshot (current OS/arch only) since
// that's all the CLI tests need.
Task{
Name: "cli",
Description: "Run CLI tests",
@@ -68,7 +62,8 @@ func main() {
Run: func() {
bin := snapshotBinPath()
if !file.Exists(bin) {
panic(fmt.Sprintf("snapshot binary not found at %s; run `make snapshot` first", bin))
Log("snapshot binary not found at %s; building single-target snapshot", bin)
Run("make snapshot:single-target")
}
Log("testing binary: %s", bin)
Run(
@@ -85,110 +80,58 @@ func main() {
Dependencies: Deps("static-analysis", "test", "install-test"),
},
// --- everything below is implemented in Taskfile.yaml and surfaced here
// via wrap(). Descriptions come from Taskfile.yaml (single source of truth).
// --- everything else is implemented in Taskfile.yaml. gotask.Tasks()
// discovers every (non-internal) Taskfile task — including the namespaced
// `generate:cpe-index:*` tasks from the included task.d file — and surfaces
// them as first-class go-make tasks (with their canonical names and `desc:`)
// that forward to `task <name>`. Descriptions live in Taskfile.yaml as the
// single source of truth; no per-task wrapping needed here.
gotask.Tasks(),
// static analysis extras
wrap("check-json-schema-drift").RunOn("static-analysis"),
wrap("check-capability-drift"),
wrap("check-binary-fixture-size").RunOn("static-analysis"),
// gotask.Tasks() discovers canonical task names only, not Taskfile aliases,
// so re-expose `refresh-fixtures`'s `fixtures` alias for manual use.
Task{
Name: "fixtures",
Description: "Clear and fetch all test fixture cache (alias of refresh-fixtures)",
Dependencies: Deps("refresh-fixtures"),
},
// test extras
wrap("validate-cyclonedx-schema").RunOn("test"),
wrap("test-utils").RunOn("test"),
wrap("check-docker-cache").RunOn("test"),
wrap("snapshot-smoke-test"),
// update commands
wrap("update-format-golden-files"),
// fixture cache plumbing (heavy ORAS logic, lives in Taskfile).
// refresh-fixtures hooks into "unit" so `make unit` triggers the
// stale-cache detection + download just like `task unit` did on main
// (its `deps: [tmpdir, fixtures]` is what kept the fixture cache fresh).
wrap("fingerprints"),
wrap("refresh-fixtures").RunOn("unit"),
wrap("fixtures"),
wrap("build-fixtures"),
wrap("download-test-fixture-cache"),
wrap("upload-test-fixture-cache"),
wrap("show-test-image-cache"),
// install-script tests (delegates to test/install/Makefile)
wrap("install-test"),
wrap("install-test-cache-save"),
wrap("install-test-cache-load"),
wrap("install-test-ci-mac"),
// compare tests
wrap("generate-compare-file"),
wrap("compare-mac"),
wrap("compare-linux"),
wrap("compare-test-deb-package-install"),
wrap("compare-test-rpm-package-install"),
// code/data generation (umbrella + per-target; each lives in Taskfile)
wrap("generate"),
wrap("generate-json-schema"),
wrap("generate-license-list"),
wrap("generate-cpe-dictionary-index"),
wrap("generate-capabilities"),
// cleanup (each hooks into go-make's built-in `clean` label)
wrap("clean-snapshot").RunOn("clean"),
wrap("clean-docker-cache").RunOn("clean"),
wrap("clean-oras-cache").RunOn("clean"),
wrap("clean-cache").RunOn("clean"),
wrap("clean-test-observations").RunOn("clean"),
// gotask.Tasks() can't attach RunsOn labels, so wire the syft-specific
// Taskfile tasks into go-make's native phases here. These thin hooks have
// no body and no description (hidden from `make help`); they only pull the
// discovered tasks in when the labeled phase runs.
Task{
Name: "static-analysis:syft",
RunsOn: lang.List("static-analysis"),
Dependencies: Deps("check-json-schema-drift", "check-binary-fixture-size"),
},
Task{
Name: "test:syft",
RunsOn: lang.List("test"),
Dependencies: Deps("validate-cyclonedx-schema", "test-utils", "check-docker-cache"),
},
// refresh-fixtures hooks into "unit" so `make unit` triggers the stale-cache
// detection + download just like `task unit` did on main (its
// `deps: [tmpdir, fixtures]` is what kept the fixture cache fresh).
Task{
Name: "unit:syft",
RunsOn: lang.List("unit"),
Dependencies: Deps("refresh-fixtures"),
},
Task{
Name: "clean:syft",
RunsOn: lang.List("clean"),
Dependencies: Deps(
"clean-snapshot",
"clean-docker-cache",
"clean-oras-cache",
"clean-cache",
"clean-test-observations",
),
},
)
}
// wrap creates a go-make Task that delegates execution to `task <name>`. The
// task's description is pulled from Taskfile.yaml's `desc:` field — descriptions
// for wrapped tasks must always live in Taskfile.yaml, never here.
func wrap(name string) Task {
desc, ok := taskfileDescriptions[name]
if !ok || desc == "" {
// loud-fail at startup so missing descs can't sneak through review.
panic(fmt.Sprintf("Taskfile.yaml task %q is missing a `desc:` field; please add one", name))
}
return Task{
Name: name,
Description: desc,
Run: func() { Run("task " + name) },
}
}
// mustReadTaskfileDescriptions parses Taskfile.yaml at the repo root and returns
// a map of task name -> desc. Runs at package init time so wrap() can use it.
func mustReadTaskfileDescriptions() map[string]string {
root := git.Root()
if root == "" {
return nil
}
path := filepath.Join(root, "Taskfile.yaml")
data, err := os.ReadFile(path) //nolint:gosec // G304: path resolved from git.Root()
if err != nil {
return nil
}
var tf struct {
Tasks map[string]struct {
Desc string `yaml:"desc"`
Aliases []string `yaml:"aliases"`
} `yaml:"tasks"`
}
lang.Throw(yaml.Unmarshal(data, &tf))
out := make(map[string]string, len(tf.Tasks))
for name, t := range tf.Tasks {
out[name] = t.Desc
// aliases inherit the canonical task's description so wrap() can find them.
for _, alias := range t.Aliases {
out[alias] = t.Desc
}
}
return out
}
// snapshotBinPath replicates the SNAPSHOT_BIN computation from the prior Taskfile:
// <repoRoot>/snapshot/<os>-build_<os>_<arch>/syft, where arch maps amd64->amd64_v1
// and arm64->arm64_v8.0 to match goreleaser's per-target output directory naming.
+4 -3
View File
@@ -2,9 +2,9 @@ version: "3"
# NOTE: most generic tasks (static-analysis, format, lint, unit, snapshot, release,
# changelog, ci-release, etc.) are now provided natively by anchore/go-make and
# defined in .make/main.go. This file holds the syft-specific tasks that go-make
# wraps via `wrap("<name>")` calls — keep descriptions (`desc:`) populated so they
# show up in `make help`.
# defined in .make/main.go. This file holds the syft-specific tasks, which go-make
# auto-discovers via gotask.Tasks() and surfaces as `make <name>` — keep
# descriptions (`desc:`) populated so they show up in `make help`.
includes:
generate:cpe-index: ./task.d/generate/cpe-index.yaml
@@ -137,6 +137,7 @@ tasks:
fingerprints:
desc: Generate fingerprints for all non-docker test fixtures
silent: true
deps: [tmpdir]
# this will look for `testdata/Makefile` and invoke the `fingerprint` target to calculate all cache input fingerprint files
generates:
- '**/testdata/**/*.fingerprint'
@@ -1,6 +1,6 @@
[TestHandler_handleAttestationStarted/attesting_in_progress/task_line - 1]
⠋ Creating a thing running a thing
⠋ Creating a thing running a thing
---
[TestHandler_handleAttestationStarted/attesting_in_progress/log - 1]
@@ -11,7 +11,7 @@
---
[TestHandler_handleAttestationStarted/attesting_complete/task_line - 1]
✔ Created a thing running a thing
✔ Created a thing running a thing
---
[TestHandler_handleAttestationStarted/attesting_complete/log - 1]
@@ -1,8 +1,8 @@
[TestHandler_handleFetchImage/fetch_image_in_progress - 1]
⠋ Loading image ━━━━━━━━━━━━━━━━━━━━ [current] the-image
⠋ Loading image ━━━━━━━━━━━━━━━━━━━━ [current] the-image
---
[TestHandler_handleFetchImage/fetch_image_complete - 1]
✔ Loaded image the-image
✔ Loaded image the-image
---
@@ -1,8 +1,8 @@
[TestHandler_handleFileIndexingStarted/cataloging_in_progress - 1]
⠋ Indexing file system ━━━━━━━━━━━━━━━━━━━━ [current] /some/path
⠋ Indexing file system ━━━━━━━━━━━━━━━━━━━━ [current] /some/path
---
[TestHandler_handleFileIndexingStarted/cataloging_complete - 1]
✔ Indexed file system /some/path
✔ Indexed file system /some/path
---
@@ -1,8 +1,8 @@
[TestHandler_handlePullSourceStarted/snap_download_in_progress - 1]
⠋ Downloading snap file... ━━━━━━━━━━━━━━━━━━━━ example-app_1.0_amd64.snap
⠋ Downloading snap file... ━━━━━━━━━━━━━━━━━━━━ example-app_1.0_amd64.snap
---
[TestHandler_handlePullSourceStarted/snap_download_complete - 1]
✔ Snap downloaded successfully example-app_1.0_amd64.snap
✔ Snap downloaded successfully example-app_1.0_amd64.snap
---
@@ -1,8 +1,8 @@
[TestHandler_handleReadImage/read_image_in_progress - 1]
⠋ Parsing image ━━━━━━━━━━━━━━━━━━━━ id
⠋ Parsing image ━━━━━━━━━━━━━━━━━━━━ id
---
[TestHandler_handleReadImage/read_image_complete - 1]
✔ Parsed image id
✔ Parsed image id
---
+18 -6
View File
@@ -60,6 +60,7 @@ type (
Packages []detectorPackageInfo `json:"packages,omitempty"`
Comment string `json:"comment,omitempty"`
PackageTypes []string `json:"package_types,omitempty"`
PURLTypes []string `json:"purl_types,omitempty"`
JSONSchemaTypes []string `json:"json_schema_types,omitempty"`
Capabilities capabilities.CapabilitySet `json:"capabilities,omitempty"`
}
@@ -235,7 +236,7 @@ func renderCatalogerInfoJSON(doc *capabilities.Document, catalogers []capabiliti
// if no parsers, use detectors instead
if len(info.Patterns) == 0 {
info.Capabilities = cat.Capabilities
info.Patterns = convertDetectorsToPatterns(cat.Detectors, cat.PackageTypes, cat.JSONSchemaTypes)
info.Patterns = convertDetectorsToPatterns(cat.Detectors, cat.PackageTypes, cat.PURLTypes, cat.JSONSchemaTypes)
}
info.Config = getConfigInfoFromDocument(doc, cat.Config)
@@ -278,6 +279,7 @@ func convertParsersToPatterns(parsers []capabilities.Parser) []patternInfo {
Packages: convertDetectorPackages(parser.Detector.Packages),
Comment: parser.Detector.Comment,
PackageTypes: parser.PackageTypes,
PURLTypes: parser.PURLTypes,
JSONSchemaTypes: parser.JSONSchemaTypes,
Capabilities: parser.Capabilities,
})
@@ -286,7 +288,7 @@ func convertParsersToPatterns(parsers []capabilities.Parser) []patternInfo {
}
// convertDetectorsToPatterns converts detector entries to pattern info for JSON output (for non-parser catalogers)
func convertDetectorsToPatterns(detectors []capabilities.Detector, packageTypes, jsonSchemaTypes []string) []patternInfo {
func convertDetectorsToPatterns(detectors []capabilities.Detector, packageTypes, purlTypes, jsonSchemaTypes []string) []patternInfo {
var patterns []patternInfo
for _, det := range detectors {
patterns = append(patterns, patternInfo{
@@ -296,6 +298,7 @@ func convertDetectorsToPatterns(detectors []capabilities.Detector, packageTypes,
Packages: convertDetectorPackages(det.Packages),
Comment: det.Comment,
PackageTypes: packageTypes,
PURLTypes: purlTypes,
JSONSchemaTypes: jsonSchemaTypes,
})
}
@@ -353,7 +356,7 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
)
// set headers
table.Header("ECOSYSTEM", "CATALOGER", "CRITERIA", "LICENSE", "NODES", "EDGES", "KINDS", "LISTING", "DIGESTS", "HASH")
table.Header("ECOSYSTEM", "CATALOGER", "CRITERIA", "PURL", "LICENSE", "NODES", "EDGES", "KINDS", "LISTING", "DIGESTS", "HASH")
// build rows for each cataloger
var data [][]string
@@ -367,13 +370,13 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
// generic catalogers: one row per parser
for _, parser := range cat.Parsers {
criteria := formatCriteria([]capabilities.Detector{parser.Detector})
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, parser.Capabilities)
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, parser.PURLTypes, parser.Capabilities)
data = append(data, row)
}
} else {
// custom catalogers: one row with all detectors
criteria := formatCriteria(cat.Detectors)
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, cat.Capabilities)
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, cat.PURLTypes, cat.Capabilities)
data = append(data, row)
}
}
@@ -385,7 +388,7 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
}
// buildTableRowFromCapabilities builds a table row from capability values
func buildTableRowFromCapabilities(ecosystem, name, criteria string, caps capabilities.CapabilitySet) []string {
func buildTableRowFromCapabilities(ecosystem, name, criteria string, purlTypes []string, caps capabilities.CapabilitySet) []string {
// extract capability default values
license := extractBoolCapability(caps, "license")
nodes := extractNodesCapability(caps)
@@ -399,6 +402,7 @@ func buildTableRowFromCapabilities(ecosystem, name, criteria string, caps capabi
ecosystem,
name,
criteria,
formatPURLTypes(purlTypes),
license,
nodes,
edges,
@@ -409,6 +413,14 @@ func buildTableRowFromCapabilities(ecosystem, name, criteria string, caps capabi
}
}
// formatPURLTypes renders the PURL types as a comma-separated list, or a placeholder when empty
func formatPURLTypes(purlTypes []string) string {
if len(purlTypes) == 0 {
return noStyle.Render("·")
}
return strings.Join(purlTypes, ", ")
}
// extractBoolCapability extracts a boolean capability value and formats it
func extractBoolCapability(caps capabilities.CapabilitySet, name string) string {
for _, cap := range caps {
@@ -280,12 +280,15 @@ func Test_catalogerInfoReport(t *testing.T) {
Detectors: []capabilities.Detector{
{Method: capabilities.GlobDetection, Criteria: []string{"**/*.test"}},
},
PURLTypes: []string{"npm"},
},
},
assertions: func(t *testing.T, got string) {
assert.Contains(t, got, "test-cataloger")
assert.Contains(t, got, "ECOSYSTEM")
assert.Contains(t, got, "CATALOGER")
assert.Contains(t, got, "PURL")
assert.Contains(t, got, "npm")
},
},
{
@@ -298,12 +301,17 @@ func Test_catalogerInfoReport(t *testing.T) {
Ecosystem: "test",
Type: "custom",
Selectors: []string{"test", "custom"},
Detectors: []capabilities.Detector{
{Method: capabilities.GlobDetection, Criteria: []string{"**/*.test"}},
},
PURLTypes: []string{"npm"},
},
},
assertions: func(t *testing.T, got string) {
assert.Contains(t, got, `"name":"test-cataloger"`)
assert.Contains(t, got, `"ecosystem":"test"`)
assert.Contains(t, got, `"type":"custom"`)
assert.Contains(t, got, `"purl_types":["npm"]`)
},
},
{
@@ -13,12 +13,10 @@ func DefaultFormatSPDXJSON() FormatSPDXJSON {
}
func (o FormatSPDXJSON) config(v string) spdxjson.EncoderConfig {
var pretty bool
c := spdxjson.DefaultEncoderConfig()
c.Version = v
if o.Pretty != nil {
pretty = *o.Pretty
}
return spdxjson.EncoderConfig{
Version: v,
Pretty: pretty,
c.Pretty = *o.Pretty
}
return c
}
@@ -2,6 +2,8 @@ package options
import (
"testing"
"github.com/stretchr/testify/require"
)
func TestFormatSPDXJSON_buildConfig(t *testing.T) {
@@ -11,5 +13,7 @@ func TestFormatSPDXJSON_buildConfig(t *testing.T) {
ft = setAllToNonZero(t, ft).(*FormatSPDXJSON)
subject := ft.config("Version")
assertExpectedValue(t, subject)
require.Equal(t, "Version", subject.Version)
require.Equal(t, "2.3", subject.DefaultVersion)
require.True(t, subject.Pretty)
}
+57 -53
View File
@@ -4,25 +4,25 @@ go 1.26.3
require (
github.com/BurntSushi/toml v1.6.0
github.com/CycloneDX/cyclonedx-go v0.10.0
github.com/Masterminds/semver/v3 v3.4.0
github.com/CycloneDX/cyclonedx-go v0.11.0
github.com/Masterminds/semver/v3 v3.5.0
github.com/Masterminds/sprig/v3 v3.3.0
github.com/OneOfOne/xxhash v1.2.8
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/acobaugh/osrelease v0.1.0
github.com/adrg/xdg v0.5.3
github.com/anchore/bubbly v0.2.0
github.com/anchore/clio v0.1.0
github.com/anchore/fangs v0.1.0
github.com/anchore/go-collections v0.1.0
github.com/anchore/go-homedir v0.1.0
github.com/anchore/go-logger v0.1.0
github.com/anchore/go-macholibre v0.1.0
github.com/anchore/bubbly v0.2.1
github.com/anchore/clio v0.1.1
github.com/anchore/fangs v0.1.1
github.com/anchore/go-collections v0.1.1
github.com/anchore/go-homedir v0.1.1
github.com/anchore/go-logger v0.1.1
github.com/anchore/go-macholibre v0.1.1
github.com/anchore/go-rpmdb v0.1.0
github.com/anchore/go-sync v0.1.0
github.com/anchore/go-sync v0.1.1
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.2.0
github.com/anchore/stereoscope v0.2.0
github.com/anchore/stereoscope v0.2.2
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/aquasecurity/go-pep440-version v0.0.1
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
@@ -34,13 +34,13 @@ require (
github.com/charmbracelet/lipgloss v1.1.0
github.com/dave/jennifer v1.7.1
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/diskfs/go-diskfs v1.7.0
github.com/diskfs/go-diskfs v1.9.3
github.com/distribution/reference v0.6.0
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.4.0
github.com/gkampitakis/go-snaps v0.5.21
github.com/github/go-spdx/v2 v2.7.0
github.com/gkampitakis/go-snaps v0.5.22
github.com/go-git/go-billy/v5 v5.9.0
github.com/go-git/go-git/v5 v5.19.1
github.com/go-test/deep v1.1.1
@@ -48,21 +48,21 @@ require (
github.com/goccy/go-yaml v1.19.2
github.com/gohugoio/hashstructure v0.6.0
github.com/google/go-cmp v0.7.0
github.com/google/go-containerregistry v0.21.5
github.com/google/go-containerregistry v0.21.7
github.com/google/licensecheck v0.3.1
github.com/google/uuid v1.6.0
github.com/gookit/color v1.6.0
github.com/gpustack/gguf-parser-go v0.24.0
github.com/gookit/color v1.6.1
github.com/gpustack/gguf-parser-go v0.24.1
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-getter v1.8.6
github.com/hashicorp/go-multierror v1.1.1
github.com/hashicorp/hcl/v2 v2.24.0
github.com/iancoleman/strcase v0.3.0
github.com/invopop/jsonschema v0.13.0
github.com/jedib0t/go-pretty/v6 v6.7.8
github.com/invopop/jsonschema v0.14.0
github.com/jedib0t/go-pretty/v6 v6.8.1
github.com/jinzhu/copier v0.4.0
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/klauspost/compress v1.18.5
github.com/klauspost/compress v1.18.6
github.com/magiconair/properties v1.8.10
github.com/mholt/archives v0.1.5
github.com/moby/sys/mountinfo v0.7.2
@@ -79,7 +79,7 @@ require (
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
github.com/sergi/go-diff v1.4.0
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb
github.com/spdx/tools-golang v0.5.7
github.com/spdx/tools-golang v0.6.0-rc4
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
@@ -88,18 +88,22 @@ require (
github.com/vifraa/gopom v1.0.0
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0
github.com/wk8/go-ordered-map/v2 v2.1.8
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
go.uber.org/goleak v1.3.0
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
golang.org/x/mod v0.35.0
golang.org/x/net v0.53.0
golang.org/x/mod v0.37.0
golang.org/x/net v0.56.0
golang.org/x/time v0.15.0
golang.org/x/tools v0.44.0
golang.org/x/tools v0.46.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.46.2
modernc.org/sqlite v1.51.0
)
require (
github.com/pb33f/ordered-map/v2 v2.3.1
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd
)
require (
@@ -119,11 +123,11 @@ require (
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 // indirect
github.com/Microsoft/hcsshim v0.15.0-rc.1 // indirect
github.com/ProtonMail/go-crypto v1.4.0 // indirect
github.com/ProtonMail/go-crypto v1.4.1 // indirect
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
github.com/anchore/go-lzo v0.1.0 // indirect
github.com/anchore/go-struct-converter v0.1.0 // indirect
github.com/anchore/go-lzo v0.1.1 // indirect
github.com/anchore/go-struct-converter v0.2.0-rc2 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/aquasecurity/go-version v0.0.1 // indirect
@@ -155,18 +159,18 @@ require (
github.com/bodgit/sevenzip v1.6.1 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/buger/jsonparser v1.1.2 // indirect
github.com/charmbracelet/colorprofile v0.4.1 // indirect
github.com/charmbracelet/colorprofile v0.4.3 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/charmbracelet/x/ansi v0.11.6 // indirect
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
github.com/charmbracelet/x/term v0.2.2 // indirect
github.com/clipperhouse/displaywidth v0.10.0 // indirect
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
github.com/clipperhouse/displaywidth v0.11.0 // indirect
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
github.com/containerd/cgroups/v3 v3.1.3 // indirect
github.com/containerd/containerd/api v1.11.1 // indirect
github.com/containerd/containerd/v2 v2.3.1 // indirect
github.com/containerd/containerd/v2 v2.3.2 // indirect
github.com/containerd/continuity v0.5.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
@@ -174,14 +178,13 @@ require (
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v1.0.0-rc.4 // indirect
github.com/containerd/plugin v1.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
github.com/containerd/ttrpc v1.2.8 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v29.4.3+incompatible // indirect
github.com/docker/cli v29.5.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.5 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-connections v0.7.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
@@ -193,7 +196,7 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gkampitakis/ciinfo v0.3.2 // indirect
github.com/gkampitakis/ciinfo v0.3.4 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-logr/logr v1.4.3 // indirect
@@ -220,12 +223,11 @@ require (
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/maruel/natural v1.1.1 // indirect
github.com/maruel/natural v1.3.0 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
github.com/mattn/go-runewidth v0.0.19 // indirect
github.com/mattn/go-runewidth v0.0.21 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/mikelolasagasti/xz v1.0.1 // indirect
github.com/minio/minlz v1.0.1 // indirect
@@ -235,8 +237,8 @@ require (
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/locker v1.0.1 // indirect
github.com/moby/moby/api v1.54.1 // indirect
github.com/moby/moby/client v0.4.0 // indirect
github.com/moby/moby/api v1.54.2 // indirect
github.com/moby/moby/client v0.4.1 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/signal v0.7.1 // indirect
github.com/moby/sys/user v0.4.0 // indirect
@@ -255,13 +257,15 @@ require (
github.com/opencontainers/runtime-spec v1.3.0 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.3.1 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pierrec/lz4/v4 v4.1.26 // indirect
github.com/piprate/json-gold v0.7.0 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pkg/xattr v0.4.9 // indirect
github.com/pkg/xattr v0.4.12 // indirect
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
@@ -279,14 +283,13 @@ require (
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/sylabs/sif/v2 v2.24.0 // indirect
github.com/sylabs/sif/v2 v2.24.1 // indirect
github.com/sylabs/squashfs v1.0.6 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/gjson v1.19.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/vbatts/tar-split v0.12.2 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
@@ -303,13 +306,14 @@ require (
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/crypto v0.50.0 // indirect
golang.org/x/crypto v0.53.0 // indirect
golang.org/x/oauth2 v0.36.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.43.0 // indirect
golang.org/x/term v0.42.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/sync v0.21.0 // indirect
golang.org/x/sys v0.46.0 // indirect
golang.org/x/term v0.44.0 // indirect
golang.org/x/text v0.38.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.17.0 // indirect
google.golang.org/api v0.271.0 // indirect
@@ -319,7 +323,7 @@ require (
google.golang.org/grpc v1.80.0 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
modernc.org/libc v1.70.0 // indirect
modernc.org/libc v1.72.3 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)
+116 -113
View File
@@ -77,8 +77,8 @@ github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CycloneDX/cyclonedx-go v0.10.0 h1:7xyklU7YD+CUyGzSFIARG18NYLsKVn4QFg04qSsu+7Y=
github.com/CycloneDX/cyclonedx-go v0.10.0/go.mod h1:vUvbCXQsEm48OI6oOlanxstwNByXjCZ2wuleUlwGEO8=
github.com/CycloneDX/cyclonedx-go v0.11.0 h1:GokP8FiRC+foiuwWhSSLpSD5H4hSWtGnR3wo7apkBFI=
github.com/CycloneDX/cyclonedx-go v0.11.0/go.mod h1:vUvbCXQsEm48OI6oOlanxstwNByXjCZ2wuleUlwGEO8=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
@@ -92,8 +92,8 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapp
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
@@ -104,8 +104,8 @@ github.com/Microsoft/hcsshim v0.15.0-rc.1/go.mod h1:HWvvUPIy9HF6LotILj1G4VyS065r
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.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ=
github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
@@ -120,34 +120,34 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
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=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/anchore/bubbly v0.2.0 h1:VE6nLlWsYV+bTOqqCzS46NirrVmvHah3UQDtFcjzgHU=
github.com/anchore/bubbly v0.2.0/go.mod h1:zRg1mfRCCe4vq1t9Ibu24YurErmnp9LAVCbpnpsb1pQ=
github.com/anchore/clio v0.1.0 h1:lrlSgD38V0T6TLjOG5Kn3miEXCQyYr6ZlwTSnW1WIJg=
github.com/anchore/clio v0.1.0/go.mod h1:z4l6oHaU8x/yQ3BnES4Ekh4eLqFSYlj1jQpk7waPkJs=
github.com/anchore/fangs v0.1.0 h1:EidxQN2XDRWn8q2n9pCVZXrOEnvpV1CuPLiWMyWG/vE=
github.com/anchore/fangs v0.1.0/go.mod h1:5YocMj6a0MC+KiBXBP0/ocOfc54DvlsOcpkAxmPWgx0=
github.com/anchore/go-collections v0.1.0 h1:vh2LE/j6HLKC27pENkas+J+h4BK4nOte9b7pDjlhY9w=
github.com/anchore/go-collections v0.1.0/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
github.com/anchore/go-homedir v0.1.0 h1:WP+lNLOz2og8hzCkY7IccyFNInVKsrjogzV19G5G8p0=
github.com/anchore/go-homedir v0.1.0/go.mod h1:9B0DGhbmMAJVGGEbrlb+PkORM5eDFCEOtZ3xQ22qaLA=
github.com/anchore/go-logger v0.1.0 h1:3pE9Sli4SE1I/mtV+2pZXzim+aWj83BRaneVXeMgD3A=
github.com/anchore/go-logger v0.1.0/go.mod h1:0l63X5VyHqfWQwsNaTmkgXErsfohze3NAFCXFdhSjdQ=
github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs=
github.com/anchore/go-lzo v0.1.0/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk=
github.com/anchore/go-macholibre v0.1.0 h1:qHbdusBZNcZM/uuKf1Psa9xxAFSoyRTps8GW9gpJgsg=
github.com/anchore/go-macholibre v0.1.0/go.mod h1:eu0gbwaZ+ocVFJLePdmPPDKU8MboV1MKsUCr36Ckd5s=
github.com/anchore/bubbly v0.2.1 h1:b3ZNBefY8ks5DI4SQ7BXSbBIsN6eoaTD6s1su7NuCBo=
github.com/anchore/bubbly v0.2.1/go.mod h1:o6a9aH3/fCn+mb5g1T1tBbQZ4hGR0yO5/uD1d2Vtago=
github.com/anchore/clio v0.1.1 h1:9bkYnaFbpT0J3G0qSHyQqa/lpK5zV0C9Gtgh82H/jj0=
github.com/anchore/clio v0.1.1/go.mod h1:CFPIopJlakJbBpKtAL/EPpj4W9hgBO7PfFc8N2FAtlE=
github.com/anchore/fangs v0.1.1 h1:NBTHocmWM2/0gNhebpoE/wEB0Z0nxh2lESww2o/Uf5M=
github.com/anchore/fangs v0.1.1/go.mod h1:82oZuk4+RynOvAoxnXKmI1zO5xk/kbOw+5t4fAAPSsk=
github.com/anchore/go-collections v0.1.1 h1:kGlmSehs+ctTBZr+98F2+taHeolHy+XDx1Nczl8GZc8=
github.com/anchore/go-collections v0.1.1/go.mod h1:zgDbPSNDpfU47tVo7aW+2suG7+vXB1Sq1qtDhRJknQ0=
github.com/anchore/go-homedir v0.1.1 h1:KpaO5BPrVT7cVMcEZ9KfFfEi0iuo3DtagPGl0C744X0=
github.com/anchore/go-homedir v0.1.1/go.mod h1:9B0DGhbmMAJVGGEbrlb+PkORM5eDFCEOtZ3xQ22qaLA=
github.com/anchore/go-logger v0.1.1 h1:HPBNmQVBdYliwaFVUL7oJ3UvSlaoXAMj/ZagAIYaCpA=
github.com/anchore/go-logger v0.1.1/go.mod h1:ekWuh5BkZVwyXnyEd4fwL29N4pNLssgRsqh2+kFN/b8=
github.com/anchore/go-lzo v0.1.1 h1:IwL/fvkdtlIrYIXck6WxZ3nb8WjjHziYYmGxlooyOnM=
github.com/anchore/go-lzo v0.1.1/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk=
github.com/anchore/go-macholibre v0.1.1 h1:sTBHi3S1WVIOCamc6NyT8Sn9mBmD30DCTfjfVm4k6/M=
github.com/anchore/go-macholibre v0.1.1/go.mod h1:YNq2610RlvGCK0Za+Klz/OCMtUaEnwwAGA/VfUAQaro=
github.com/anchore/go-rpmdb v0.1.0 h1:Q8dc208/HYzCqhx0L1zurfm1UPil24hlo9NjkdFmLdE=
github.com/anchore/go-rpmdb v0.1.0/go.mod h1:eQVa6QFGzKy0qMcnW2pez0XBczvgwSjw9vA23qifEyU=
github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30=
github.com/anchore/go-struct-converter v0.1.0/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/anchore/go-sync v0.1.0 h1:1TEZM7jISrvtoBMOF79xP0caQKASAtgW1yKqc0EjyZg=
github.com/anchore/go-sync v0.1.0/go.mod h1:Iposeub0kHipoTei1icj4Tys0SJN+cCdxEnkS7bZUFs=
github.com/anchore/go-struct-converter v0.2.0-rc2 h1:q+859fW2/jbHJHB2etbNfRlFwYpknyvbqqk1hUdamQ4=
github.com/anchore/go-struct-converter v0.2.0-rc2/go.mod h1:cDBA5vhcR62nXWo8QH9/Kk2807o65ISaHPNPX66L+Uw=
github.com/anchore/go-sync v0.1.1 h1:91SZ+YqUIIHmf2jPAYZPuHqM/ZqnK1pVJDWtET6+AJE=
github.com/anchore/go-sync v0.1.1/go.mod h1:3haGsk2BnaoVhsfqae8Kd0FKIAILE1Hw69P4Xo5iVBw=
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.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T/EAErk=
github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE=
github.com/anchore/stereoscope v0.2.0 h1:8haOu2ugLymmxvyfrZR7OTBFiRFRBh5LlNUtRrSRoxI=
github.com/anchore/stereoscope v0.2.0/go.mod h1:PYx3fD4lvBVsYoQ/fBdauhZ5hmkRrJgw1B73svKx7/U=
github.com/anchore/stereoscope v0.2.2 h1:SGTLGoF6GHmKEn9Bb6LYpzzgz9nhvMZgN/c4fTSQjYY=
github.com/anchore/stereoscope v0.2.2/go.mod h1:ylJXJKebLctP7u9ewguB1d0zUETJxvAA7r2DiuljDTM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
@@ -248,8 +248,8 @@ github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5f
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
@@ -274,10 +274,10 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g=
github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs=
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@@ -296,8 +296,8 @@ github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6
github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
github.com/containerd/containerd/api v1.11.1 h1:h8nfoDW9+fNsC/9TwiAHj8B1GzXKtR4eFtkhi/X5RLU=
github.com/containerd/containerd/api v1.11.1/go.mod h1:CaQFRu+N1MtbgL6JDOJLUB1hCKESU1lD6MuTJhgtdlw=
github.com/containerd/containerd/v2 v2.3.1 h1:4dVXBdlvotRBlaP2TmNbY/EGc06KJrMDDUqQdxX/HOk=
github.com/containerd/containerd/v2 v2.3.1/go.mod h1:xVoxGPWZBwwph8DF2IbDhriLKdHfjdpO0b3wFP9wQ1I=
github.com/containerd/containerd/v2 v2.3.2 h1:eLven1YxRMkeiKu7IcMrPKE+gn8sGR1DqHbbshMEvWM=
github.com/containerd/containerd/v2 v2.3.2/go.mod h1:rHKGm3VW6wNrINb3x8mNT+w7qYXFVElTt/8HTuxVhD4=
github.com/containerd/continuity v0.5.0 h1:7a85HZpCSs+1Zps0Ee3DPSuAWY+0SJM1JNM51nlEVDg=
github.com/containerd/continuity v0.5.0/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
@@ -312,8 +312,6 @@ github.com/containerd/platforms v1.0.0-rc.4 h1:M42JrUT4zfZTqtkUwkr0GzmUWbfyO5VO0
github.com/containerd/platforms v1.0.0-rc.4/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A=
github.com/containerd/plugin v1.1.0 h1:O+7lczNJVMy8rz0YNx3xGB8tTf5qY4i5abF041Ew19U=
github.com/containerd/plugin v1.1.0/go.mod h1:qBTum+A8lJ6lO44A19Eo7y1OlcLj4OWFH1DA/vnHmcc=
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
github.com/containerd/ttrpc v1.2.8 h1:xbVu6D4qF2jihdh9rDVOKqUMiFBQk6YctTdo1zk087Y=
github.com/containerd/ttrpc v1.2.8/go.mod h1:wyZW2K79t4Hfcxl+GUvkZqRBzJlqFFvgEeeWXa42tyE=
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
@@ -335,18 +333,18 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8=
github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
github.com/diskfs/go-diskfs v1.9.3 h1:cLciNCeZ4QAXVxyPJDr1ZJ9N9CCG3rQlQ/z/Cs/cNDM=
github.com/diskfs/go-diskfs v1.9.3/go.mod h1:TePJORO83Adh5pb2SqsxAwaP0fofFxKLkxctiS/9OQc=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/docker/cli v29.4.3+incompatible h1:u+UliYm2J/rYrIh2FqHQg32neRG8GjbvNuwQRTzGspU=
github.com/docker/cli v29.4.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v29.5.3+incompatible h1:nbEFfz774vBwQ5KRYv7c/AghjReqnGISvrRhzjV0evs=
github.com/docker/cli v29.5.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
@@ -358,8 +356,8 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY=
github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57 h1:x5yxNrq8XffV/OoNUeFPM6hxHVi5OTspSTBxr/9pemg=
github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -408,12 +406,12 @@ github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.4.0 h1:+4IwVwJJbm3rzvrQ6P1nI9BDMcy3la4RchRy5uehV/M=
github.com/github/go-spdx/v2 v2.4.0/go.mod h1:/5rwgS0txhGtRdUZwc02bTglzg6HK3FfuEbECKlK2Sg=
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-snaps v0.5.21 h1:SvhSFeZviQXwlT+dnGyAIATVehkhqRVW6qfQZhCZH+Y=
github.com/gkampitakis/go-snaps v0.5.21/go.mod h1:gC3YqxQTPyIXvQrw/Vpt3a8VqR1MO8sVpZFWN4DGwNs=
github.com/github/go-spdx/v2 v2.7.0 h1:GzfXx4wFdlilARxmFRXW/mgUy3A4vSqZocCMFV6XFdQ=
github.com/github/go-spdx/v2 v2.7.0/go.mod h1:Ftc45YYG1WzpzwEPKRVm9Jv8vDqOrN4gWoCkK+bHer0=
github.com/gkampitakis/ciinfo v0.3.4 h1:5eBSibVuSMbb/H6Elc0IIEFbkzCJi3lm94n0+U7Z0KY=
github.com/gkampitakis/ciinfo v0.3.4/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-snaps v0.5.22 h1:xg9omphRnbDnimMCl1KqznC4krlxOGpkB0vDSfX2P7M=
github.com/gkampitakis/go-snaps v0.5.22/go.mod h1:uy3lVzCCRRsAwYqSocyw5fY8xRLCYEfqoOJNxr8HonM=
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=
@@ -511,8 +509,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/go-containerregistry v0.21.5 h1:KTJG9Pn/jC0VdZR6ctV3/jcN+q6/Iqlx0sTVz3ywZlM=
github.com/google/go-containerregistry v0.21.5/go.mod h1:ySvMuiWg+dOsRW0Hw8GYwfMwBlNRTmpYBFJPlkco5zU=
github.com/google/go-containerregistry v0.21.7 h1:/vPFuVXDjtFREsVArW+0h1CIl5urnOhzei4X2DMW9IU=
github.com/google/go-containerregistry v0.21.7/go.mod h1:kjSbt7/zMsKLWfnHrIvKvhXHUw91jbe9DNjPPJ32gXE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
@@ -558,10 +556,10 @@ github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOID
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/gpustack/gguf-parser-go v0.24.0 h1:tdJceXYp9e5RhE9RwVYIuUpir72Jz2D68NEtDXkKCKc=
github.com/gpustack/gguf-parser-go v0.24.0/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
github.com/gookit/color v1.6.1 h1:KoTnDxJPRgrL0SoX0f8rCFg2zI0t4E3GZZBMo2nN8LU=
github.com/gookit/color v1.6.1/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
github.com/gpustack/gguf-parser-go v0.24.1 h1:nTYtL8HFK6ZhB90RKBu4oX2b3ZHpJLrMmKRfL9w9Cyc=
github.com/gpustack/gguf-parser-go v0.24.1/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72 h1:vTCWu1wbdYo7PEZFem/rlr01+Un+wwVmI7wiegFdRLk=
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72/go.mod h1:Vn+BBgKQHVQYdVQ4NZDICE1Brb+JfaONyDHr3q07oQc=
@@ -622,12 +620,12 @@ github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:
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=
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8iyZjg=
github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I=
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.7.8 h1:BVYrDy5DPBA3Qn9ICT+PokP9cvCv1KaHv2i+Hc8sr5o=
github.com/jedib0t/go-pretty/v6 v6.7.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jedib0t/go-pretty/v6 v6.8.1 h1:0fkCNhjrX0zPpwkWaDYU5VMrygg41Tu197mWILIJoqQ=
github.com/jedib0t/go-pretty/v6 v6.8.1/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=
@@ -646,8 +644,8 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
@@ -673,10 +671,9 @@ 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.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/maruel/natural v1.3.0 h1:VsmCsBmEyrR46RomtgHs5hbKADGRVtliHTyCOLFBpsg=
github.com/maruel/natural v1.3.0/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
@@ -694,8 +691,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw=
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
@@ -725,10 +722,10 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4=
github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw=
github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g=
github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg=
github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY=
github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
@@ -781,6 +778,8 @@ github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
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/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY=
github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ=
github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
github.com/pborman/indent v1.2.1/go.mod h1:FitS+t35kIYtB5xWTZAPhnmrxcciEEOdbyrrpz5K6Vw=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
@@ -788,8 +787,10 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY=
github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
github.com/piprate/json-gold v0.7.0 h1:bEMirgA5y8Z2loTQfxyIFfY+EflxH1CTP6r/KIlcJNw=
github.com/piprate/json-gold v0.7.0/go.mod h1:RVhE35veDX19r5gfUAR+IYHkAUuPwJO8Ie/qVeFaIzw=
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -800,8 +801,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM=
github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -810,6 +811,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
@@ -878,8 +881,8 @@ github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cw
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb h1:bLo8hvc8XFm9J47r690TUKBzcjSWdJDxmjXJZ+/f92U=
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
github.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg=
github.com/spdx/tools-golang v0.5.7/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw=
github.com/spdx/tools-golang v0.6.0-rc4 h1:2GkvNr0DcnJHY9BDm3OYHo229jZS/h4qYDK+tHYXPOo=
github.com/spdx/tools-golang v0.6.0-rc4/go.mod h1:ruCHu3shgy7bVbZ7gtEU4Gq4fI08n2SdXtgV5PoN/OM=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
@@ -921,17 +924,19 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/sylabs/sif/v2 v2.24.0 h1:1wB5uMDUQYjk8AckTySaDcP9YnpMb1LyDRr1Jt9A10w=
github.com/sylabs/sif/v2 v2.24.0/go.mod h1:DbXWqWZ1hdLSU+K9ipdds5AmZeHWsyxCOj/oQakBa88=
github.com/sylabs/sif/v2 v2.24.1 h1:OhTOfTwBaGXfbWYWXK9C6Pojma3A1bTaNJ6CEhyuKok=
github.com/sylabs/sif/v2 v2.24.1/go.mod h1:PRq9MoP0g+p0qE5ZRGrOhyVAUdHrPsAVmXHgoiW6VU0=
github.com/sylabs/squashfs v1.0.6 h1:PvJcDzxr+vIm2kH56mEMbaOzvGu79gK7P7IX+R7BDZI=
github.com/sylabs/squashfs v1.0.6/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE=
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd h1:Rf9uhF1+VJ7ZHqxrG8pJ6YacmHvVCmByDmGbAWCc/gA=
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo=
github.com/terminalstatic/go-xsd-validate v0.1.6/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@@ -945,16 +950,12 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/vbatts/go-mtree v0.7.0 h1:ytmOc3MTRidZiBi9VBCyZ2BHe4fZS47L5v7BVXDWW4E=
github.com/vbatts/go-mtree v0.7.0/go.mod h1:EjdpFC+LZy1TXbRGNa1MKKgjQ+7ew3foMFJK8o4/TdY=
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20=
github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0 h1:EHsPe0Q0ANoLOZff1dBLAyeWLTA4sbPTpGI+2zb0FnM=
github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0/go.mod h1:g/D9uEUFp5YLyciwCpVsSOZOm56hfv4rzGJod6MlqIM=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
@@ -1022,6 +1023,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s=
go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1036,8 +1039,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.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
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=
@@ -1077,8 +1080,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.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
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=
@@ -1123,8 +1126,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
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=
@@ -1156,8 +1159,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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
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=
@@ -1232,13 +1235,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
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=
@@ -1249,8 +1252,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.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
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=
@@ -1311,8 +1314,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk=
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1506,10 +1509,10 @@ 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.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0=
modernc.org/cc/v4 v4.28.2 h1:3tQ0lf2ADtoby2EtSP+J7IE2SHwEJdP8ioR59wx7XpY=
modernc.org/cc/v4 v4.28.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
modernc.org/ccgo/v4 v4.34.0 h1:yRLPFZieg532OT4rp4JFNIVcquwalMX26G95WQDqwCQ=
modernc.org/ccgo/v4 v4.34.0/go.mod h1:AS5WYMyBakQ+fhsHhtP8mWB82KTGPkNNJDGfGQCe0/A=
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
@@ -1518,18 +1521,18 @@ modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw=
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
modernc.org/libc v1.72.3 h1:ZnDF4tXn4NBXFutMMQC4vtbTFSXhhKzR73fv0beZEAU=
modernc.org/libc v1.72.3/go.mod h1:dn0dZNnnn1clLyvRxLxYExxiKRZIRENOfqQ8XEeg4Qs=
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.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
modernc.org/opt v0.2.0/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.46.2 h1:gkXQ6R0+AjxFC/fTDaeIVLbNLNrRoOK7YYVz5BKhTcE=
modernc.org/sqlite v1.46.2/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
modernc.org/sqlite v1.51.0 h1:aH/MMSoayAIhozZ7uJbVTT9QO/VhzBf0J9tymmmuC/U=
modernc.org/sqlite v1.51.0/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM=
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=
+6 -1
View File
@@ -297,6 +297,11 @@ func addCatalogerFieldComment(keyNode, valueNode *yaml.Node, catalogerName strin
if keyNode.LineComment == "" {
keyNode.LineComment = autoGeneratedComment
}
case "purl_types":
// cataloger-level purl_types (for custom catalogers) are AUTO-GENERATED
if keyNode.LineComment == "" {
keyNode.LineComment = autoGeneratedComment
}
case "json_schema_types":
// json_schema_types are AUTO-GENERATED
if keyNode.LineComment == "" {
@@ -367,7 +372,7 @@ func addParserComments(parsersNode *yaml.Node) {
valueNode := parserNode.Content[i+1]
switch keyNode.Value {
case "parser_function", "metadata_types", "package_types", "json_schema_types":
case "parser_function", "metadata_types", "package_types", "purl_types", "json_schema_types":
// add AUTO-GENERATED comment to these fields
if keyNode.LineComment == "" {
keyNode.LineComment = autoGeneratedComment
+38
View File
@@ -3,6 +3,7 @@ package main
import (
"fmt"
"sort"
"strings"
"github.com/scylladb/go-set/strset"
@@ -10,6 +11,7 @@ import (
"github.com/anchore/syft/internal/capabilities"
"github.com/anchore/syft/internal/capabilities/internal"
"github.com/anchore/syft/internal/packagemetadata"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/binary"
)
@@ -192,6 +194,9 @@ func RegenerateCapabilities(catalogerDir string, repoRoot string) (*Statistics,
formatOrphans(orphans))
}
// 5a. Derive PURL types from the discovered package types (purely a function of pkg.Type)
derivePURLTypes(updated)
// 6. Write back to YAML files with comments, preserving existing node trees
fmt.Print(" → Writing updated capabilities files...")
if err := saveCapabilities(catalogerDir, repoRoot, updated, existingNodes); err != nil {
@@ -427,6 +432,39 @@ func (e *EnrichmentData) GetMetadataTypes(catalogerName string) ([]string, bool)
return types, ok
}
// derivePURLTypes populates the PURLTypes field on every cataloger and parser by mapping each
// observed package type to its canonical PURL type via pkg.Type.PackageURLType. this is a pure
// function of the already-discovered package_types, so it needs no additional test observations.
func derivePURLTypes(doc *capabilities.Document) {
for i := range doc.Catalogers {
cat := &doc.Catalogers[i]
cat.PURLTypes = purlTypesFromPackageTypes(cat.PackageTypes)
for j := range cat.Parsers {
cat.Parsers[j].PURLTypes = purlTypesFromPackageTypes(cat.Parsers[j].PackageTypes)
}
}
}
// purlTypesFromPackageTypes maps syft package type strings (e.g. "go-module") to their PURL types
// (e.g. "golang"), returning a deduplicated, sorted slice. package types with no PURL mapping are skipped.
func purlTypesFromPackageTypes(packageTypes []string) []string {
if len(packageTypes) == 0 {
return nil
}
set := strset.New()
for _, pt := range packageTypes {
if purlType := pkg.Type(pt).PackageURLType(); purlType != "" {
set.Add(purlType)
}
}
if set.IsEmpty() {
return nil
}
result := set.List()
sort.Strings(result)
return result
}
// GetPackageTypes returns the package types for the given cataloger name
func (e *EnrichmentData) GetPackageTypes(catalogerName string) ([]string, bool) {
types, ok := e.packageTypes[catalogerName]
@@ -552,3 +552,48 @@ func TestConvertToJSONSchemaTypesFromMetadata(t *testing.T) {
})
}
}
func TestPURLTypesFromPackageTypes(t *testing.T) {
tests := []struct {
name string
packageTypes []string
want []string
}{
{
name: "nil slice returns nil",
packageTypes: nil,
want: nil,
},
{
name: "single package type maps to purl type",
packageTypes: []string{"go-module"},
want: []string{"golang"},
},
{
name: "multiple package types are sorted",
packageTypes: []string{"npm", "go-module"},
want: []string{"golang", "npm"},
},
{
// java and jenkins-plugin both map to maven, so the result is deduplicated
name: "distinct package types collapsing to one purl type are deduplicated",
packageTypes: []string{"java-archive", "jenkins-plugin"},
want: []string{"maven"},
},
{
// unknown package types have no purl mapping and are skipped
name: "package type without a purl mapping is skipped",
packageTypes: []string{"UnknownPackage"},
want: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := purlTypesFromPackageTypes(tt.packageTypes)
if diff := cmp.Diff(tt.want, got); diff != "" {
t.Errorf("purlTypesFromPackageTypes() mismatch (-want +got):\n%s", diff)
}
})
}
}
+2
View File
@@ -82,6 +82,7 @@ type CatalogerEntry struct {
Detectors []Detector `yaml:"detectors,omitempty" json:"detectors,omitempty"` // AUTO-GENERATED - detection methods (only for type=custom)
MetadataTypes []string `yaml:"metadata_types,omitempty" json:"metadata_types,omitempty"` // AUTO-GENERATED - pkg metadata types emitted (only for type=custom)
PackageTypes []string `yaml:"package_types,omitempty" json:"package_types,omitempty"` // AUTO-GENERATED - package types emitted (only for type=custom)
PURLTypes []string `yaml:"purl_types,omitempty" json:"purl_types,omitempty"` // AUTO-GENERATED - PURL types derived from package_types (only for type=custom)
JSONSchemaTypes []string `yaml:"json_schema_types,omitempty" json:"json_schema_types,omitempty"` // AUTO-GENERATED - JSON schema type names (UpperCamelCase)
Capabilities CapabilitySet `yaml:"capabilities,omitempty" json:"capabilities,omitempty"` // MANUAL - config-driven capability definitions (only for type=custom)
}
@@ -92,6 +93,7 @@ type Parser struct {
Detector Detector `yaml:"detector" json:"detector"` // AUTO-GENERATED - how artifacts are detected
MetadataTypes []string `yaml:"metadata_types,omitempty" json:"metadata_types,omitempty"` // AUTO-GENERATED - pkg metadata types emitted by this parser
PackageTypes []string `yaml:"package_types,omitempty" json:"package_types,omitempty"` // AUTO-GENERATED - package types emitted by this parser
PURLTypes []string `yaml:"purl_types,omitempty" json:"purl_types,omitempty"` // AUTO-GENERATED - PURL types derived from package_types
JSONSchemaTypes []string `yaml:"json_schema_types,omitempty" json:"json_schema_types,omitempty"` // AUTO-GENERATED - JSON schema type names (UpperCamelCase)
Capabilities CapabilitySet `yaml:"capabilities,omitempty" json:"capabilities,omitempty"` // MANUAL - config-driven capability definitions
}
+3 -1
View File
@@ -3,12 +3,14 @@ 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.1.3"
JSONSchemaVersion = "16.1.5"
// Changelog
// 16.1.0 - reformulated the python pdm fields (added "URL" and removed the unused "path" field).
// 16.1.1 - correct elf package osCpe field according to the document of systemd (also add appCpe field)
// 16.1.2 - placeholder for 16.1.2 changelog
// 16.1.3 - add GGUFFileParts to GGUFFileHeader metadata
// 16.1.4 - add BunLockEntry metadata type for bun.lock support
// 16.1.5 - add DenoLockEntry and DenoRemoteLockEntry metadata types for deno.lock support
)
+34 -4
View File
@@ -5,6 +5,7 @@ import (
"io/fs"
"os"
"path/filepath"
"strings"
"github.com/diskfs/go-diskfs/filesystem"
)
@@ -14,8 +15,7 @@ type WalkDiskDirFunc func(fsys filesystem.FileSystem, path string, d os.FileInfo
// WalkDiskDir walks the file tree within the go-diskfs filesystem at root, calling fn for each file or directory in the tree, including root.
// This is meant to mimic the behavior of fs.WalkDir in the standard library.
func WalkDiskDir(fsys filesystem.FileSystem, root string, fn WalkDiskDirFunc) error {
infos, err := fsys.ReadDir(root)
infos, err := readDiskDir(fsys, root)
if err != nil {
return err
}
@@ -51,7 +51,7 @@ func walkDiskDir(fsys filesystem.FileSystem, name string, d os.FileInfo, walkDir
isDir := d != nil && d.IsDir()
if d == nil {
_, err := fsys.ReadDir(name)
_, err := readDiskDir(fsys, name)
if err != nil {
return nil
}
@@ -62,7 +62,7 @@ func walkDiskDir(fsys filesystem.FileSystem, name string, d os.FileInfo, walkDir
return nil
}
dirs, err := fsys.ReadDir(name)
dirs, err := readDiskDir(fsys, name)
if err != nil {
err = walkDirFn(fsys, name, d, err)
if err != nil {
@@ -87,3 +87,33 @@ func walkDiskDir(fsys filesystem.FileSystem, name string, d os.FileInfo, walkDir
}
return nil
}
// readDiskDir reads the directory entries at the given path from a go-diskfs filesystem.
// go-diskfs returns fs.DirEntry values; these are resolved to os.FileInfo so callers have
// access to the full file metadata (mode, size, modification time).
func readDiskDir(fsys filesystem.FileSystem, p string) ([]os.FileInfo, error) {
entries, err := fsys.ReadDir(ToFSPath(p))
if err != nil {
return nil, err
}
infos := make([]os.FileInfo, 0, len(entries))
for _, entry := range entries {
info, err := entry.Info()
if err != nil {
return nil, err
}
infos = append(infos, info)
}
return infos, nil
}
// ToFSPath converts an absolute ("/"-rooted) path into an io/fs-valid path as required by
// go-diskfs, where the root is "." and other paths carry no leading slash (see io/fs.ValidPath).
func ToFSPath(p string) string {
p = strings.TrimPrefix(p, "/")
if p == "" {
return "."
}
return p
}
+2 -2
View File
@@ -43,10 +43,10 @@ func createTestFS(t *testing.T) filesystem.FileSystem {
for _, tf := range testFiles {
if tf.isDir {
err := fsys.Mkdir(tf.path)
err := fsys.Mkdir(ToFSPath(tf.path))
require.NoError(t, err)
} else {
f, err := fsys.OpenFile(tf.path, os.O_CREATE|os.O_RDWR)
f, err := fsys.OpenFile(ToFSPath(tf.path), os.O_CREATE|os.O_RDWR)
require.NoError(t, err)
_, err = f.Write([]byte(tf.content))
require.NoError(t, err)
+1 -1
View File
@@ -6,9 +6,9 @@ import (
"testing"
"github.com/invopop/jsonschema"
orderedmap "github.com/pb33f/ordered-map/v2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
orderedmap "github.com/wk8/go-ordered-map/v2"
)
// TestCopyAliasFieldComments verifies that field comments from source types are correctly copied to alias types.
+3
View File
@@ -11,6 +11,7 @@ func AllTypes() []any {
pkg.ApkDBEntry{},
pkg.BinarySignature{},
pkg.BitnamiSBOMEntry{},
pkg.BunLockEntry{},
pkg.CocoaPodfileLockEntry{},
pkg.ConanV1LockEntry{},
pkg.ConanV2LockEntry{},
@@ -19,6 +20,8 @@ func AllTypes() []any {
pkg.CondaMetaPackage{},
pkg.DartPubspec{},
pkg.DartPubspecLockEntry{},
pkg.DenoLockEntry{},
pkg.DenoRemoteLockEntry{},
pkg.DotnetDepsEntry{},
pkg.DotnetPackagesLockEntry{},
pkg.DotnetPortableExecutableEntry{},
+3
View File
@@ -73,6 +73,8 @@ var jsonTypes = makeJSONTypes(
jsonNames(pkg.ConaninfoEntry{}, "c-conan-info-entry"),
jsonNames(pkg.DartPubspecLockEntry{}, "dart-pubspec-lock-entry", "DartPubMetadata"),
jsonNames(pkg.DartPubspec{}, "dart-pubspec"),
jsonNames(pkg.DenoLockEntry{}, "deno-lock-entry"),
jsonNames(pkg.DenoRemoteLockEntry{}, "deno-remote-lock-entry"),
jsonNames(pkg.DotnetDepsEntry{}, "dotnet-deps-entry", "DotnetDepsMetadata"),
jsonNames(pkg.DotnetPortableExecutableEntry{}, "dotnet-portable-executable-entry"),
jsonNames(pkg.DpkgArchiveEntry{}, "dpkg-archive-entry"),
@@ -96,6 +98,7 @@ var jsonTypes = makeJSONTypes(
jsonNames(pkg.NpmPackageLockEntry{}, "javascript-npm-package-lock-entry", "NpmPackageLockJsonMetadata"),
jsonNames(pkg.YarnLockEntry{}, "javascript-yarn-lock-entry", "YarnLockJsonMetadata"),
jsonNames(pkg.PnpmLockEntry{}, "javascript-pnpm-lock-entry"),
jsonNames(pkg.BunLockEntry{}, "javascript-bun-lock-entry"),
jsonNames(pkg.PEBinary{}, "pe-binary"),
jsonNames(pkg.PhpComposerLockEntry{}, "php-composer-lock-entry", "PhpComposerJsonMetadata"),
jsonNamesWithoutLookup(pkg.PhpComposerInstalledEntry{}, "php-composer-installed-entry", "PhpComposerJsonMetadata"), // the legacy value is split into two types, where the other is preferred
+50 -2
View File
@@ -65,6 +65,30 @@ func (i *Index) Add(relationships ...artifact.Relationship) {
}
func (i *Index) Remove(id artifact.ID) {
// scrub this node's edges from the adjacent nodes' maps before dropping the node itself. without this, the
// edges live on as dangling pointers inside the other endpoints' mappedRelationships, so query methods
// (From/To/References/Coordinates/Contains) would keep returning relationships that have been removed.
if mapped := i.fromID[id]; mapped != nil {
for _, sr := range mapped.allRelated {
if sr.to == id {
continue // self-edge: this node's own maps are deleted below
}
if other := i.toID[sr.to]; other != nil {
other.remove(id, sr)
}
}
}
if mapped := i.toID[id]; mapped != nil {
for _, sr := range mapped.allRelated {
if sr.from == id {
continue // self-edge: this node's own maps are deleted below
}
if other := i.fromID[sr.from]; other != nil {
other.remove(id, sr)
}
}
}
delete(i.fromID, id)
delete(i.toID, id)
@@ -78,6 +102,12 @@ func (i *Index) Remove(id artifact.ID) {
}
func (i *Index) Replace(ogID artifact.ID, replacement artifact.Identifiable) {
if replacement == nil || replacement.ID() == ogID {
// replacing a node with one that has the same ID would re-add the (deduped) edges and then the trailing
// Remove(ogID) would delete them, silently wiping all of the node's relationships. treat it as a no-op.
return
}
for _, mapped := range fromMappedByID(i.fromID, ogID) {
// the stale relationship(i.e. if there's an elder ID in either side) should be discarded
if len(fromMappedByID(i.toID, mapped.relationship.To.ID())) == 0 {
@@ -91,8 +121,10 @@ func (i *Index) Replace(ogID artifact.ID, replacement artifact.Identifiable) {
}
for _, mapped := range fromMappedByID(i.toID, ogID) {
// same as the above
if len(fromMappedByID(i.fromID, mapped.relationship.To.ID())) == 0 {
// same as the above, but check the surviving other endpoint (the From side, since these are edges TO ogID).
// note: this must reference From (not To, which is ogID itself) so that nodes appearing only on the To side
// of relationships (e.g. a go main module that nothing depends on) keep their edges after an ID change.
if len(fromMappedByID(i.fromID, mapped.relationship.From.ID())) == 0 {
continue
}
i.Add(artifact.Relationship{
@@ -206,6 +238,22 @@ func (m *mappedRelationships) add(id artifact.ID, newRelationship *sortableRelat
typeMap[id] = newRelationship
}
// remove deletes a single relationship (the other endpoint keyed by id) from this node's maps. it is the inverse
// of add and is used to keep adjacent nodes consistent when a node is removed from the index.
func (m *mappedRelationships) remove(id artifact.ID, target *sortableRelationship) {
filtered := m.allRelated[:0]
for _, r := range m.allRelated {
if r != target {
filtered = append(filtered, r)
}
}
m.allRelated = filtered
if typeMap := m.typeMap[target.relationship.Type]; typeMap != nil && typeMap[id] == target {
delete(typeMap, id)
}
}
type sortableRelationship struct {
from artifact.ID
to artifact.ID
+126
View File
@@ -380,6 +380,41 @@ func TestReplace(t *testing.T) {
compareRelationships(t, expectedRels, index.All())
}
func TestReplace_NodeOnlyOnToSide(t *testing.T) {
// regression: a node that only appears on the To side of relationships (nothing depends on it, e.g. a go main
// module that other modules are a dependency-of) must keep its incoming edges when its ID changes (such as when a
// compliance rule stubs a missing version and recomputes the package ID). previously the To-side remap guard
// checked ogID itself instead of the surviving From endpoint, so all such edges were dropped.
main := pkg.Package{Name: "main-module"} // empty version, like a go main module
dep1 := pkg.Package{Name: "dep-1", Version: "1.0.0"}
dep2 := pkg.Package{Name: "dep-2", Version: "2.0.0"}
for _, p := range []*pkg.Package{&main, &dep1, &dep2} {
p.SetID()
}
// both deps are a dependency-of main; main has no outgoing edges
r1 := artifact.Relationship{From: dep1, To: main, Type: artifact.DependencyOfRelationship}
r2 := artifact.Relationship{From: dep2, To: main, Type: artifact.DependencyOfRelationship}
index := NewIndex(r1, r2)
// simulate the version stub: main gets a new ID
stubbedMain := main
stubbedMain.Version = "UNKNOWN"
stubbedMain.SetID()
require.NotEqual(t, main.ID(), stubbedMain.ID())
index.Replace(main.ID(), &stubbedMain)
expectedRels := []artifact.Relationship{
{From: dep1, To: stubbedMain, Type: artifact.DependencyOfRelationship},
{From: dep2, To: stubbedMain, Type: artifact.DependencyOfRelationship},
}
compareRelationships(t, expectedRels, index.All())
}
func compareRelationships(t testing.TB, expected, actual []artifact.Relationship) {
assert.Equal(t, len(expected), len(actual), "number of relationships should match")
for _, e := range expected {
@@ -408,3 +443,94 @@ func TestReplace_NoExistingRelations(t *testing.T) {
allRels := index.All()
assert.Len(t, allRels, 0)
}
// Remove must scrub the removed node's edges from adjacent nodes too, otherwise query methods keep returning
// relationships that no longer exist. the original Remove only deleted the removed node's own maps, leaving
// dangling pointers in the other endpoints.
func TestRemove_NoStaleEdgesInAdjacentNodes(t *testing.T) {
p1 := pkg.Package{Name: "pkg-1", Version: "1"}
p2 := pkg.Package{Name: "pkg-2", Version: "2"}
p3 := pkg.Package{Name: "pkg-3", Version: "3"}
for _, p := range []*pkg.Package{&p1, &p2, &p3} {
p.SetID()
}
r12 := artifact.Relationship{From: p1, To: p2, Type: artifact.DependencyOfRelationship}
r32 := artifact.Relationship{From: p3, To: p2, Type: artifact.DependencyOfRelationship}
index := NewIndex(r12, r32)
index.Remove(p1.ID())
// p2 was on the To side of the removed edge; its incoming view must no longer include the stale p1 edge,
// but must still include the live p3 edge.
compareRelationships(t, []artifact.Relationship{r32}, index.To(p2, artifact.DependencyOfRelationship))
assert.False(t, index.Contains(r12), "Contains must not report a removed edge")
assert.Len(t, index.References(p2), 1, "p2 should only reference the surviving p3 edge")
assert.True(t, index.Contains(r32), "surviving edge should remain")
compareRelationships(t, []artifact.Relationship{r32}, index.All())
}
// Replace must leave no dangling references to the old ID in adjacent nodes' query views.
func TestReplace_NoStaleEdgesAfterReplace(t *testing.T) {
main := pkg.Package{Name: "main-module"} // empty version
dep := pkg.Package{Name: "dep", Version: "1.0.0"}
main.SetID()
dep.SetID()
oldEdge := artifact.Relationship{From: dep, To: main, Type: artifact.DependencyOfRelationship}
index := NewIndex(oldEdge)
stubbed := main
stubbed.Version = "UNKNOWN"
stubbed.SetID()
require.NotEqual(t, main.ID(), stubbed.ID())
index.Replace(main.ID(), &stubbed)
newEdge := artifact.Relationship{From: dep, To: stubbed, Type: artifact.DependencyOfRelationship}
// the dep's outgoing view must point at the stubbed node, not the old one
from := index.From(dep, artifact.DependencyOfRelationship)
require.Len(t, from, 1)
assert.Equal(t, stubbed.ID(), from[0].To.ID())
assert.True(t, index.Contains(newEdge))
assert.False(t, index.Contains(oldEdge), "stale edge to old ID must be gone")
assert.Empty(t, index.To(main), "old node should have no incoming edges")
compareRelationships(t, []artifact.Relationship{newEdge}, index.All())
}
// replacing a node with one that has the same ID must be a no-op, not silently wipe its edges.
func TestReplace_SameIDIsNoOp(t *testing.T) {
a := pkg.Package{Name: "a", Version: "1"}
b := pkg.Package{Name: "b", Version: "2"}
a.SetID()
b.SetID()
edge := artifact.Relationship{From: a, To: b, Type: artifact.DependencyOfRelationship}
index := NewIndex(edge)
// same identity object -> same ID
index.Replace(b.ID(), b)
compareRelationships(t, []artifact.Relationship{edge}, index.All())
assert.True(t, index.Contains(edge))
}
// a self-edge on the replaced node should be remapped to replacement->replacement, not dropped.
func TestReplace_SelfEdge(t *testing.T) {
n := pkg.Package{Name: "n"} // empty version
n.SetID()
index := NewIndex(artifact.Relationship{From: n, To: n, Type: artifact.DependencyOfRelationship})
sn := n
sn.Version = "UNKNOWN"
sn.SetID()
require.NotEqual(t, n.ID(), sn.ID())
index.Replace(n.ID(), &sn)
all := index.All()
require.Len(t, all, 1)
assert.Equal(t, sn.ID(), all[0].From.ID())
assert.Equal(t, sn.ID(), all[0].To.ID())
assert.Empty(t, index.From(n), "no edges should reference the old ID")
assert.Empty(t, index.To(n), "no edges should reference the old ID")
}
+2 -2
View File
@@ -105,7 +105,7 @@ func DefaultPackageTaskFactories() Factories {
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return javascript.NewLockCataloger(cfg.PackagesConfig.JavaScript)
},
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, JavaScript, Node, NPM,
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, JavaScript, Node, NPM, "deno",
),
newSimplePackageTaskFactory(php.NewComposerLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "php", "composer"),
newSimplePackageTaskFactory(php.NewPearCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, pkgcataloging.ImageTag, "php", "pear"),
@@ -161,7 +161,7 @@ func DefaultPackageTaskFactories() Factories {
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary",
),
newSimplePackageTaskFactory(binary.NewELFPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "elf-package", "elf"),
newSimplePackageTaskFactory(binary.NewPEPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "pe-package", "pe", "dll", "exe"),
newSimplePackageTaskFactory(binary.NewPEPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "pe-package", "pe", "dll", "exe", "bpl"),
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
newSimplePackageTaskFactory(java.NewJvmDistributionCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "java", "jvm", "jdk", "jre"),
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
+166 -1
View File
@@ -2,7 +2,7 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="http://cyclonedx.org/schema/spdx"
version="1.0-3.27.0">
version="1.0-3.28.0">
<xs:simpleType name="licenseId">
<xs:restriction base="xs:string">
@@ -57,6 +57,11 @@
<xs:documentation>Amazon Digital Services License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Advanced-Cryptics-Dictionary">
<xs:annotation>
<xs:documentation>Advanced Cryptics Dictionary License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="AFL-1.1">
<xs:annotation>
<xs:documentation>Academic Free License v1.1</xs:documentation>
@@ -122,6 +127,11 @@
<xs:documentation>Aladdin Free Public License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ALGLIB-Documentation">
<xs:annotation>
<xs:documentation>ALGLIB Documentation License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="AMD-newlib">
<xs:annotation>
<xs:documentation>AMD newlib License</xs:documentation>
@@ -327,6 +337,11 @@
<xs:documentation>Boehm-Demers-Weiser GC License (without fee)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BOLA-1.1">
<xs:annotation>
<xs:documentation>Buena Onda License Agreement v1.1</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Borceux">
<xs:annotation>
<xs:documentation>Borceux license</xs:documentation>
@@ -457,6 +472,11 @@
<xs:documentation>BSD 3-Clause Sun Microsystems</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-3-Clause-Tso">
<xs:annotation>
<xs:documentation>BSD 3-Clause Tso variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-4-Clause">
<xs:annotation>
<xs:documentation>BSD 4-Clause &quot;Original&quot; or &quot;Old&quot; License</xs:documentation>
@@ -497,6 +517,11 @@
<xs:documentation>BSD-Inferno-Nettverk</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-Mark-Modifications">
<xs:annotation>
<xs:documentation>BSD Mark Modifications License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-Protection">
<xs:annotation>
<xs:documentation>BSD Protection License</xs:documentation>
@@ -527,6 +552,11 @@
<xs:documentation>Boost Software License 1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Buddy">
<xs:annotation>
<xs:documentation>Buddy License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BUSL-1.1">
<xs:annotation>
<xs:documentation>Business Source License 1.1</xs:documentation>
@@ -567,6 +597,11 @@
<xs:documentation>Caldera License (without preamble)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="CAPEC-tou">
<xs:annotation>
<xs:documentation>Common Attack Pattern Enumeration and Classification License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Catharon">
<xs:annotation>
<xs:documentation>Catharon License</xs:documentation>
@@ -1212,6 +1247,21 @@
<xs:documentation>Erlang Public License v1.1</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ESA-PL-permissive-2.4">
<xs:annotation>
<xs:documentation>European Space Agency Public License – v2.4 – Permissive (Type 3)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ESA-PL-strong-copyleft-2.4">
<xs:annotation>
<xs:documentation>European Space Agency Public License (ESA-PL) - V2.4 - Strong Copyleft (Type 1)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ESA-PL-weak-copyleft-2.4">
<xs:annotation>
<xs:documentation>European Space Agency Public License – v2.4 – Weak Copyleft (Type 2)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="etalab-2.0">
<xs:annotation>
<xs:documentation>Etalab Open License 2.0</xs:documentation>
@@ -1737,6 +1787,11 @@
<xs:documentation>Historical Permission Notice and Disclaimer - sell variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-sell-variant-critical-systems">
<xs:annotation>
<xs:documentation>HPND - sell variant with safety critical systems clause</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-sell-variant-MIT-disclaimer">
<xs:annotation>
<xs:documentation>HPND sell variant with MIT disclaimer</xs:documentation>
@@ -1747,6 +1802,11 @@
<xs:documentation>HPND sell variant with MIT disclaimer - reverse</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-SMC">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - SMC variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-UC">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - University of California variant</xs:documentation>
@@ -1762,6 +1822,11 @@
<xs:documentation>HTML Tidy License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="hyphen-bulgarian">
<xs:annotation>
<xs:documentation>hyphen-bulgarian License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="IBM-pibs">
<xs:annotation>
<xs:documentation>IBM PowerPC Initialization and Boot Software</xs:documentation>
@@ -1852,6 +1917,11 @@
<xs:documentation>ISC Veillard variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ISO-permission">
<xs:annotation>
<xs:documentation>ISO permission notice</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Jam">
<xs:annotation>
<xs:documentation>Jam License</xs:documentation>
@@ -2237,6 +2307,11 @@
<xs:documentation>MIT Open Group variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="MIT-STK">
<xs:annotation>
<xs:documentation>MIT-STK License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="MIT-testregex">
<xs:annotation>
<xs:documentation>MIT testregex Variant</xs:documentation>
@@ -2257,6 +2332,11 @@
<xs:documentation>MMIXware License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="MMPL-1.0.1">
<xs:annotation>
<xs:documentation>Minecraft Mod Public License v1.0.1</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Motosoto">
<xs:annotation>
<xs:documentation>Motosoto License</xs:documentation>
@@ -2422,6 +2502,11 @@
<xs:documentation>NIST Public Domain Notice with license fallback</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="NIST-PD-TNT">
<xs:annotation>
<xs:documentation>NIST Public Domain Notice TNT variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="NIST-Software">
<xs:annotation>
<xs:documentation>NIST Software License</xs:documentation>
@@ -2687,6 +2772,11 @@
<xs:documentation>Open Market License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OpenMDW-1.0">
<xs:annotation>
<xs:documentation>OpenMDW License Agreement v1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OpenPBS-2.3">
<xs:annotation>
<xs:documentation>OpenPBS v2.3 Software License</xs:documentation>
@@ -2722,6 +2812,11 @@
<xs:documentation>Open Publication License v1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OSC-1.0">
<xs:annotation>
<xs:documentation>OSC License 1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OSET-PL-2.1">
<xs:annotation>
<xs:documentation>OSET Public License version 2.1</xs:documentation>
@@ -2752,11 +2847,21 @@
<xs:documentation>Open Software License 3.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OSSP">
<xs:annotation>
<xs:documentation>OSSP License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="PADL">
<xs:annotation>
<xs:documentation>PADL License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="ParaType-Free-Font-1.3">
<xs:annotation>
<xs:documentation>ParaType Free Font Licensing Agreement v1.3</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Parity-6.0.0">
<xs:annotation>
<xs:documentation>The Parity Public License 6.0.0</xs:documentation>
@@ -2977,6 +3082,11 @@
<xs:documentation>SGI OpenGL License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="SGMLUG-PM">
<xs:annotation>
<xs:documentation>SGMLUG Parser Materials License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="SGP4">
<xs:annotation>
<xs:documentation>SGP4 Permission Notice</xs:documentation>
@@ -3162,6 +3272,11 @@
<xs:documentation>TCP Wrappers License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="TekHVC">
<xs:annotation>
<xs:documentation>TekHVC License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="TermReadKey">
<xs:annotation>
<xs:documentation>TermReadKey License</xs:documentation>
@@ -3297,6 +3412,11 @@
<xs:documentation>Unlicense - libwhirlpool variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="UnRAR">
<xs:annotation>
<xs:documentation>UnRAR License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="UPL-1.0">
<xs:annotation>
<xs:documentation>Universal Permissive License v1.0</xs:documentation>
@@ -3312,6 +3432,11 @@
<xs:documentation>Vim License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Vixie-Cron">
<xs:annotation>
<xs:documentation>Vixie Cron License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="VOSTROM">
<xs:annotation>
<xs:documentation>VOSTROM Public License for Open Source</xs:documentation>
@@ -3352,11 +3477,21 @@
<xs:documentation>Widget Workshop License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="WordNet">
<xs:annotation>
<xs:documentation>WordNet License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Wsuipa">
<xs:annotation>
<xs:documentation>Wsuipa License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="WTFNMFPL">
<xs:annotation>
<xs:documentation>Do What The F*ck You Want To But It&apos;s Not My Fault Public License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="WTFPL">
<xs:annotation>
<xs:documentation>Do What The F*ck You Want To Public License</xs:documentation>
@@ -3382,6 +3517,11 @@
<xs:documentation>X11 License Distribution Modification Variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="X11-no-permit-persons">
<xs:annotation>
<xs:documentation>X11 no permit persons clause</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="X11-swapped">
<xs:annotation>
<xs:documentation>X11 swapped final paragraphs</xs:documentation>
@@ -3568,6 +3708,11 @@
<xs:documentation>Classpath exception 2.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Classpath-exception-2.0-short">
<xs:annotation>
<xs:documentation>Classpath exception 2.0 - short</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="CLISP-exception-2.0">
<xs:annotation>
<xs:documentation>CLISP exception 2.0</xs:documentation>
@@ -3718,6 +3863,11 @@
<xs:documentation>KiCad Libraries Exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="kvirc-openssl-exception">
<xs:annotation>
<xs:documentation>kvirc OpenSSL Exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="LGPL-3.0-linking-exception">
<xs:annotation>
<xs:documentation>LGPL-3.0 Linking Exception</xs:documentation>
@@ -3833,6 +3983,11 @@
<xs:documentation>RRDtool FLOSS exception 2.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="rsync-linking-exception">
<xs:annotation>
<xs:documentation>rsync Linking Exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="SANE-exception">
<xs:annotation>
<xs:documentation>SANE Exception</xs:documentation>
@@ -3848,6 +4003,16 @@
<xs:documentation>Solderpad Hardware License v2.1</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Simple-Library-Usage-exception">
<xs:annotation>
<xs:documentation>Simple Library Usage Exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="sqlitestudio-OpenSSL-exception">
<xs:annotation>
<xs:documentation>sqlitestudio OpenSSL exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="stunnel-exception">
<xs:annotation>
<xs:documentation>stunnel Exception</xs:documentation>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+104 -1
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "anchore.io/schema/syft/json/16.1.3/document",
"$id": "anchore.io/schema/syft/json/16.1.5/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@@ -748,6 +748,45 @@
],
"description": "DartPubspecLockEntry is a struct that represents a single entry found in the \"packages\" section in a Dart pubspec.lock file."
},
"DenoLockEntry": {
"properties": {
"integrity": {
"type": "string",
"description": "Integrity is the crpto hash of the package content for verification"
},
"dependencies": {
"items": {
"type": "string"
},
"type": "array",
"description": "Dependencies is the list of package specifiers that this package depends on"
}
},
"type": "object",
"required": [
"integrity",
"dependencies"
],
"description": "DenoLockEntry is a struct that rep a single entry found in the \"packages\" section of a Deno deno.lock file"
},
"DenoRemoteLockEntry": {
"properties": {
"url": {
"type": "string",
"description": "URL is the remote URL from which the module fetcef"
},
"integrity": {
"type": "string",
"description": "Integrity is the crpto hash of the package content for verification"
}
},
"type": "object",
"required": [
"url",
"integrity"
],
"description": "DenoRemoteLockEntry is a struct that rep a single entry found in the \"remote\" section of a Deno deno.lock file"
},
"Descriptor": {
"properties": {
"name": {
@@ -1918,6 +1957,61 @@
"type": "object",
"description": "JavaVMRelease represents JVM version and build information extracted from the release file in a Java installation."
},
"JavascriptBunLockEntry": {
"properties": {
"integrity": {
"type": "string",
"description": "Integrity is Subresource Integrity hash for verification (SRI format)"
},
"dependencies": {
"additionalProperties": {
"type": "string"
},
"type": "object",
"description": "Dependencies is a map of runtime dependencies and their version specifiers"
},
"optionalDependencies": {
"additionalProperties": {
"type": "string"
},
"type": "object",
"description": "OptionalDependencies is a map of optional dependencies and their version specifiers"
},
"peerDependencies": {
"additionalProperties": {
"type": "string"
},
"type": "object",
"description": "PeerDependencies is a map of peer dependencies and their version specifiers"
},
"bin": {
"additionalProperties": {
"type": "string"
},
"type": "object",
"description": "Bin is a map of binary names to the paths they are installed to"
},
"os": {
"type": "string",
"description": "OS is the operating system constraint for the package (e.g. \"darwin\")"
},
"cpu": {
"type": "string",
"description": "CPU is the CPU architecture constraint for the package (e.g. \"arm64\")"
}
},
"type": "object",
"required": [
"integrity",
"dependencies",
"optionalDependencies",
"peerDependencies",
"bin",
"os",
"cpu"
],
"description": "BunLockEntry represents a single entry in the \"packages\" section of a bun.lock file"
},
"JavascriptNpmPackage": {
"properties": {
"name": {
@@ -2603,6 +2697,12 @@
{
"$ref": "#/$defs/DartPubspecLockEntry"
},
{
"$ref": "#/$defs/DenoLockEntry"
},
{
"$ref": "#/$defs/DenoRemoteLockEntry"
},
{
"$ref": "#/$defs/DotnetDepsEntry"
},
@@ -2657,6 +2757,9 @@
{
"$ref": "#/$defs/JavaJvmInstallation"
},
{
"$ref": "#/$defs/JavascriptBunLockEntry"
},
{
"$ref": "#/$defs/JavascriptNpmPackage"
},
@@ -399,7 +399,7 @@ func collectDocRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out
to = toLocation
case helpers.OtherRelationship:
// Encoding uses a specifically formatted comment...
if strings.Index(r.RelationshipComment, string(artifact.EvidentByRelationship)) == 0 {
if strings.HasPrefix(r.RelationshipComment, string(artifact.EvidentByRelationship)) {
typ = artifact.EvidentByRelationship
to = toLocation
}
@@ -418,7 +418,7 @@ func collectDocRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out
to = toPackage
case helpers.OtherRelationship:
// Encoding uses a specifically formatted comment...
if strings.Index(r.RelationshipComment, string(artifact.OwnershipByFileOverlapRelationship)) == 0 {
if strings.HasPrefix(r.RelationshipComment, string(artifact.OwnershipByFileOverlapRelationship)) {
typ = artifact.OwnershipByFileOverlapRelationship
to = toPackage
}
@@ -462,10 +462,10 @@ func collectPackageFileRelationships(spdxIDMap map[string]any, doc *spdx.Documen
func toSyftCoordinates(f *spdx.File) file.Coordinates {
const layerIDPrefix = "layerID: "
var fileSystemID string
if strings.Index(f.FileComment, layerIDPrefix) == 0 {
if strings.HasPrefix(f.FileComment, layerIDPrefix) {
fileSystemID = strings.TrimPrefix(f.FileComment, layerIDPrefix)
}
if strings.Index(string(f.FileSPDXIdentifier), layerIDPrefix) == 0 {
if strings.HasPrefix(string(f.FileSPDXIdentifier), layerIDPrefix) {
fileSystemID = strings.TrimPrefix(string(f.FileSPDXIdentifier), layerIDPrefix)
}
return file.Coordinates{
@@ -481,8 +481,8 @@ func toSyftLocation(f *spdx.File) file.Location {
func requireAndTrimPrefix(val any, prefix string) string {
if v, ok := val.(string); ok {
if i := strings.Index(v, prefix); i == 0 {
return strings.Replace(v, prefix, "", 1)
if strings.HasPrefix(v, prefix) {
return strings.TrimPrefix(v, prefix)
}
}
return ""
@@ -0,0 +1,774 @@
package spdxhelpers
import (
"errors"
"fmt"
"reflect"
"regexp"
"strconv"
"strings"
spdx "github.com/spdx/tools-golang/spdx/v3/v3_0"
"github.com/anchore/packageurl-go"
"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"
"github.com/anchore/syft/syft/format/internal/spdxutil/helpers"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
func ToSyftModelV3(doc *spdx.Document) (*sbom.SBOM, error) {
if doc == nil {
return nil, errors.New("cannot convert SPDX document to Syft model because document is nil")
}
spdxMap := ptrMap[any]{}
s := &sbom.SBOM{
Source: v3extractSource(spdxMap, doc),
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(),
FileMetadata: map[file.Coordinates]file.Metadata{},
FileDigests: map[file.Coordinates][]file.Digest{},
LinuxDistribution: v3findLinuxReleaseByPURL(doc),
},
}
relationships := v3relationshipMap(doc)
v3collectSyftPackages(s, spdxMap, relationships, doc)
v3collectSyftFiles(s, spdxMap, doc)
s.Relationships = v3toSyftRelationships(spdxMap, doc)
return s, nil
}
func v3removePackage(packages spdx.ElementList, remove spdx.AnyPackage) (pkgs spdx.ElementList) {
for _, p := range packages {
if p == remove {
continue
}
pkgs = append(pkgs, p)
}
return pkgs
}
func v3removeRelationships(elements spdx.ElementList, element spdx.AnyElement) (relations spdx.ElementList) {
for _, e := range elements {
if r, ok := e.(spdx.AnyRelationship); ok {
if r != nil && r.GetFrom() == element {
continue
}
if r != nil {
var tos spdx.ElementList
for _, to := range r.GetTo() {
if to == element {
continue
}
tos = append(tos, to)
}
r.SetTo(tos)
if len(r.GetTo()) == 0 {
continue
}
}
relations = append(relations, r)
} else {
relations = append(relations, e)
}
}
return relations
}
func v3findRootPackages(doc spdx.AnyElementCollection) (out spdx.PackageList) {
for _, p := range doc.GetRootElements().Packages() {
out = append(out, p)
}
for _, s := range doc.GetRootElements().SBOMs() {
for _, p := range s.GetRootElements().Packages() {
out = append(out, p)
}
}
return
}
func v3extractSource(spdxMap ptrMap[any], doc *spdx.Document) source.Description {
namespace := doc.ID
if namespace == "" && len(doc.NamespaceMaps) > 0 {
namespace = string(doc.NamespaceMaps[0].GetNamespace())
}
src := extractSourceFromNamespace(namespace)
rootPackages := v3findRootPackages(doc)
if len(rootPackages) != 1 {
return src
}
p := rootPackages[0]
switch p.GetPrimaryPurpose() {
case spdx.SoftwarePurpose_Container:
src = v3containerSource(p)
case spdx.SoftwarePurpose_File:
src = v3fileSource(p)
default:
return src
}
spdxMap.Set(p, src)
doc.Elements = v3removePackage(doc.Elements, p)
doc.Elements = v3removeRelationships(doc.Elements, p)
return src
}
func v3containerSource(p spdx.AnyPackage) source.Description {
container := p.GetName()
v := p.GetVersion()
if v != "" {
container += ":" + v
}
digest := ""
if len(p.GetVerifiedUsing().Hashes()) > 0 {
h := p.GetVerifiedUsing().Hashes()[0]
if h != nil {
digest = fmt.Sprintf("%s:%s", v3fromChecksumAlgorithm(h.GetAlgorithm()), h.GetValue())
}
}
supplier := ""
if p.GetSuppliedBy() != nil {
supplier = v3agentString(p.GetSuppliedBy())
}
return source.Description{
ID: p.GetID(),
Name: p.GetName(),
Version: p.GetVersion(),
Supplier: supplier,
Metadata: source.ImageMetadata{
UserInput: container,
ID: p.GetID(),
Layers: nil, // TODO handle formats with nested layer packages like Tern and K8s BOM tool
ManifestDigest: digest,
},
}
}
func v3fileSource(p spdx.AnyPackage) source.Description {
typeRegex := regexp.MustCompile("DocumentRoot-([^-]+)-.*$")
typeName := typeRegex.ReplaceAllString(p.GetID(), "$1")
var version string
var metadata any
switch {
case typeName == prefixDirectory:
// is a Syft SBOM, explicitly a directory source
metadata, version = v3directorySourceMetadata(p)
case typeName == prefixFile:
// is a Syft SBOM, explicitly a file source
metadata, version = v3fileSourceMetadata(p)
case isDirectory(p.GetName()):
// is a non-Syft SBOM, which looks like a directory
metadata, version = v3directorySourceMetadata(p)
default:
// is a non-Syft SBOM, which is probably a file
metadata, version = v3fileSourceMetadata(p)
}
supplier := ""
if p.GetSuppliedBy() != nil {
supplier = p.GetSuppliedBy().GetName()
}
return source.Description{
ID: p.GetID(),
Name: p.GetName(),
Version: version,
Supplier: supplier,
Metadata: metadata,
}
}
func v3fileSourceMetadata(p spdx.AnyPackage) (any, string) {
version := p.GetVersion()
m := source.FileMetadata{
Path: p.GetName(),
}
// if this is a Syft SBOM, we might have output a digest as the version
checksum := v3toChecksum(p.GetVersion())
for _, d := range p.GetVerifiedUsing().Hashes() {
if checksum != nil && checksum.GetValue() == d.GetValue() {
version = ""
}
m.Digests = append(m.Digests, file.Digest{
Algorithm: v3fromChecksumAlgorithm(d.GetAlgorithm()),
Value: d.GetValue(),
})
}
return m, version
}
func v3directorySourceMetadata(p spdx.AnyPackage) (any, string) {
return source.DirectoryMetadata{
Path: p.GetName(),
Base: "",
}, p.GetVersion()
}
func v3findLinuxReleaseByPURL(doc *spdx.Document) *linux.Release {
for _, p := range doc.Elements.Packages() {
purlValue := v3findPURLValue(p)
if purlValue == "" {
continue
}
purl, err := packageurl.FromString(purlValue)
if err != nil {
log.Warnf("unable to parse purl: %s", purlValue)
continue
}
distro := v3findQualifierValue(purl, pkg.PURLQualifierDistro)
if distro != "" {
parts := strings.Split(distro, "-")
name := parts[0]
version := ""
if len(parts) > 1 {
version = parts[1]
}
return &linux.Release{
PrettyName: name,
Name: name,
ID: name,
IDLike: []string{name},
Version: version,
VersionID: version,
}
}
}
return nil
}
func v3collectSyftPackages(s *sbom.SBOM, spdxMap ptrMap[any], relationships ptrMap[[]spdx.AnyRelationship], doc *spdx.Document) {
skipIDs := v3packageIDsToSkip(doc)
found := ptrMap[struct{}]{}
// tools-golang collects all elements from the JSON LD @graph to the root SpdxDocument.Elements property
for _, elementList := range []spdx.ElementList{doc.Elements, doc.RootElements} {
for _, p := range elementList.Packages() {
if p == nil || skipIDs.Has(p) || found.Has(p) {
continue
}
found.Set(p, struct{}{})
syftPkg := v3toSyftPackage(relationships, p)
spdxMap.Set(p, syftPkg)
s.Artifacts.Packages.Add(syftPkg)
}
}
}
func v3collectSyftFiles(s *sbom.SBOM, spdxMap ptrMap[any], doc *spdx.Document) {
found := ptrMap[struct{}]{}
for _, elementList := range []spdx.ElementList{doc.Elements, doc.RootElements} {
for _, f := range elementList.Files() {
if found.Has(f) {
continue
}
found.Set(f, struct{}{})
l := v3toSyftLocation(f)
spdxMap.Set(f, l)
s.Artifacts.FileMetadata[l.Coordinates] = v3toFileMetadata(f)
s.Artifacts.FileDigests[l.Coordinates] = v3toFileDigests(f)
}
}
}
func v3toFileDigests(f spdx.AnyFile) (digests []file.Digest) {
for _, h := range f.GetVerifiedUsing().Hashes() {
digests = append(digests, file.Digest{
Algorithm: v3fromChecksumAlgorithm(h.GetAlgorithm()),
Value: h.GetValue(),
})
}
return digests
}
func v3fromChecksumAlgorithm(algorithm spdx.HashAlgorithm) string {
// it might be better to have a specific case statement with constants
parts := strings.Split(algorithm.GetID(), "/")
return strings.ToLower(parts[len(parts)-1])
}
func v3toFileMetadata(f spdx.AnyFile) (meta file.Metadata) {
// FIXME Syft is currently lossy due to the SPDX 2.2.1 spec not supporting arbitrary mimetypes
if f.GetContentType() != "" {
meta.MIMEType = f.GetContentType()
}
return meta
}
func v3toSyftRelationships(spdxMap ptrMap[any], doc *spdx.Document) []artifact.Relationship {
out := v3collectDocRelationships(spdxMap, doc)
return out
}
//nolint:gocognit
func v3collectDocRelationships(spdxMap ptrMap[any], doc *spdx.Document) (out []artifact.Relationship) {
for _, r := range doc.Elements.Relationships() {
from := r.GetFrom()
if from == nil || from.GetID() == "" {
log.Debugf("ignoring relationship to external document: %+v", r)
continue
}
a := spdxMap.Get(from)
for _, to := range r.GetTo() {
b := spdxMap.Get(to)
from, fromOk := a.(pkg.Package)
toPackage, toPackageOk := b.(pkg.Package)
toLocation, toLocationOk := b.(file.Location)
//nolint:staticcheck
if !fromOk || !(toPackageOk || toLocationOk) {
log.Debugf("unable to find valid relationship mapping from SPDX, ignoring: (from: %+v) (to: %+v)", a, b)
continue
}
var to artifact.Identifiable
var typ artifact.RelationshipType
if toLocationOk {
switch r.GetType() {
case spdx.RelationshipType_Contains:
typ = artifact.ContainsRelationship
to = toLocation
case spdx.RelationshipType_Other:
// Encoding uses a specifically formatted comment...
if strings.HasPrefix(r.GetComment(), string(artifact.EvidentByRelationship)) {
typ = artifact.EvidentByRelationship
to = toLocation
}
}
} else {
switch r.GetType() {
case spdx.RelationshipType_DependsOn:
typ = artifact.DependencyOfRelationship
to = from
from = toPackage
case spdx.RelationshipType_Contains:
typ = artifact.ContainsRelationship
to = toPackage
case spdx.RelationshipType_Other:
// Encoding uses a specifically formatted comment...
if strings.HasPrefix(r.GetComment(), string(artifact.OwnershipByFileOverlapRelationship)) {
typ = artifact.OwnershipByFileOverlapRelationship
to = toPackage
}
}
}
if typ != "" && to != nil {
out = append(out, artifact.Relationship{
From: from,
To: to,
Type: typ,
})
}
}
}
return out
}
func v3toSyftCoordinates(f spdx.AnyFile) file.Coordinates {
const layerIDPrefix = "layerID: "
var fileSystemID string
if strings.HasPrefix(f.GetComment(), layerIDPrefix) {
fileSystemID = strings.TrimPrefix(f.GetComment(), layerIDPrefix)
}
if strings.HasPrefix(f.GetID(), layerIDPrefix) {
fileSystemID = strings.TrimPrefix(f.GetID(), layerIDPrefix)
}
return file.Coordinates{
RealPath: f.GetName(),
FileSystemID: fileSystemID,
}
}
func v3toSyftLocation(f spdx.AnyFile) file.Location {
l := file.NewVirtualLocationFromCoordinates(v3toSyftCoordinates(f), f.GetName())
return l
}
func v3findQualifierValue(purl packageurl.PackageURL, qualifier string) string {
for _, q := range purl.Qualifiers {
if q.Key == qualifier {
return q.Value
}
}
return ""
}
func v3extractPkgInfo(p spdx.AnyPackage) pkgInfo {
pu := v3findPURLValue(p)
purl, err := packageurl.FromString(pu)
if err != nil {
return pkgInfo{}
}
return pkgInfo{
purl,
pkg.TypeByName(purl.Type),
pkg.LanguageByName(purl.Type),
}
}
func v3toSyftPackage(relationships ptrMap[[]spdx.AnyRelationship], p spdx.AnyPackage) pkg.Package {
info := v3extractPkgInfo(p)
sP := &pkg.Package{
Type: info.typ,
Name: p.GetName(),
Version: p.GetVersion(),
Licenses: pkg.NewLicenseSet(v3parseSPDXLicenses(relationships, p)...),
CPEs: v3extractCPEs(p),
PURL: v3purlValue(info.purl),
Language: info.lang,
Metadata: v3extractMetadata(p, info),
}
internal.Backfill(sP)
if p.GetID() != "" {
// always prefer the IDs from the SBOM over derived IDs
sP.OverrideID(artifact.ID(p.GetID()))
} else {
sP.SetID()
}
return *sP
}
func v3purlValue(purl packageurl.PackageURL) string {
val := purl.String()
if _, err := packageurl.FromString(val); err != nil {
return ""
}
return val
}
func v3parseSPDXLicenses(relationships ptrMap[[]spdx.AnyRelationship], p spdx.AnyPackage) []pkg.License {
licenses := make([]pkg.License, 0)
// licenses are defined with relationships in SPDX 3, see:
// https://github.com/spdx/tools-golang/blob/spdx3/spdx/v3/v3_0/convert.go#L536
rels := relationships.Get(p)
for _, r := range rels {
if r.GetType() == spdx.RelationshipType_HasConcludedLicense {
licenses = append(licenses, v3toSyftLicenses(license.Concluded, r.GetTo().LicenseInfos()...)...)
}
if r.GetType() == spdx.RelationshipType_HasDeclaredLicense {
licenses = append(licenses, v3toSyftLicenses(license.Declared, r.GetTo().LicenseInfos()...)...)
}
}
return licenses
}
func v3toSyftLicenses(licenseType license.Type, licenses ...spdx.AnyLicenseInfo) []pkg.License {
var out []pkg.License
for _, lic := range licenses {
if lic == nil {
continue
}
value := v3licenseInfoToExpression(lic)
if value == "" {
log.Debugf("skipping SPDX license during import: %#v", lic)
continue
}
l := pkg.NewLicense(value)
if l.Value != "" {
l.Type = licenseType
out = append(out, l)
}
}
return out
}
// v3licenseInfoToExpression recursively converts an SPDX 3.0 license info object to an SPDX expression string.
func v3licenseInfoToExpression(info spdx.AnyLicenseInfo) string {
switch li := info.(type) {
case spdx.AnyLicenseExpression:
return li.GetLicenseExpression()
case spdx.AnyOrLaterOperator:
subject := v3licenseInfoToExpression(li.GetSubjectLicense())
if subject != "" && !strings.HasSuffix(subject, "+") {
return subject + "+"
}
case spdx.AnyWithAdditionOperator:
subject := v3licenseInfoToExpression(li.GetSubjectExtendableLicense())
addition := li.GetSubjectAddition()
if subject != "" && addition != nil {
return subject + " WITH " + addition.GetAdditionText()
}
case spdx.AnyConjunctiveLicenseSet:
var parts []string
for _, m := range li.GetMembers() {
if e := v3licenseInfoToExpression(m); e != "" {
parts = append(parts, e)
}
}
if len(parts) > 0 {
return strings.Join(parts, " AND ")
}
case spdx.AnyDisjunctiveLicenseSet:
var parts []string
for _, m := range li.GetMembers() {
if e := v3licenseInfoToExpression(m); e != "" {
parts = append(parts, e)
}
}
if len(parts) > 0 {
return "(" + strings.Join(parts, " OR ") + ")"
}
case spdx.AnyListedLicense:
return li.GetName()
case spdx.AnyCustomLicense:
if li.GetID() != "" {
return li.GetID()
}
return li.GetName()
}
return ""
}
//nolint:funlen
func v3extractMetadata(p spdx.AnyPackage, info pkgInfo) any {
arch := info.qualifierValue(pkg.PURLQualifierArch)
upstreamValue := info.qualifierValue(pkg.PURLQualifierUpstream)
upstream := strings.SplitN(upstreamValue, "@", 2)
upstreamName := upstream[0]
upstreamVersion := ""
if len(upstream) > 1 {
upstreamVersion = upstream[1]
}
supplier := ""
if p.GetSuppliedBy() != nil {
supplier = v3agentString(p.GetSuppliedBy())
}
originator := ""
if len(p.GetOriginatedBy()) > 0 {
// FIXME there could be multiple
originator = v3agentString(p.GetOriginatedBy()[0])
}
switch info.typ {
case pkg.ApkPkg:
return pkg.ApkDBEntry{
Package: p.GetName(),
OriginPackage: upstreamName,
Maintainer: supplier,
Version: p.GetVersion(),
Architecture: arch,
URL: string(p.GetHomePage()),
Description: p.GetDescription(),
}
case pkg.RpmPkg:
converted, err := strconv.Atoi(info.qualifierValue(pkg.PURLQualifierEpoch))
var epoch *int
if err != nil {
epoch = nil
} else {
epoch = &converted
}
return pkg.RpmDBEntry{
Name: p.GetName(),
Version: p.GetVersion(),
Epoch: epoch,
Arch: arch,
SourceRpm: upstreamValue,
Vendor: originator,
}
case pkg.DebPkg:
return pkg.DpkgDBEntry{
Package: p.GetName(),
Source: upstreamName,
Version: p.GetVersion(),
SourceVersion: upstreamVersion,
Architecture: arch,
Maintainer: originator,
}
case pkg.JavaPkg:
var digests []file.Digest
for _, value := range p.GetVerifiedUsing() {
h, _ := value.(spdx.AnyHash)
if h != nil {
digests = append(digests, file.Digest{Algorithm: v3fromChecksumAlgorithm(h.GetAlgorithm()), Value: h.GetValue()})
}
}
return pkg.JavaArchive{
ArchiveDigests: digests,
}
case pkg.GoModulePkg:
var h1Digest string
for _, value := range p.GetVerifiedUsing() {
h, _ := value.(spdx.AnyHash)
if h == nil {
continue
}
digest, err := helpers.HDigestFromSHA(v3fromChecksumAlgorithm(h.GetAlgorithm()), h.GetValue())
if err != nil {
log.Debugf("invalid h1digest: %v %v", value, err)
continue
}
h1Digest = digest
break
}
return pkg.GolangBinaryBuildinfoEntry{
H1Digest: h1Digest,
}
}
return nil
}
func v3agentString(agent spdx.AnyAgent) string {
out := ""
switch o := agent.(type) {
case spdx.AnyOrganization:
out = o.GetName()
case spdx.AnyPerson:
out = o.GetName()
}
if out == helpers.NOASSERTION {
return ""
}
return out
}
func v3findPURLValue(p spdx.AnyPackage) string {
if p.GetPackageURL() != "" {
return string(p.GetPackageURL())
}
for _, r := range p.GetExternalIdentifiers() {
if r.GetType() == spdx.ExternalIdentifierType_PackageURL {
for _, l := range r.GetIdentifierLocators() {
// FIXME multiple values
return string(l)
}
}
}
return ""
}
func v3extractCPEs(p spdx.AnyPackage) (cpes []cpe.CPE) {
for _, r := range p.GetExternalIdentifiers() {
if r.GetType() == spdx.ExternalIdentifierType_Cpe23 || r.GetType() == spdx.ExternalIdentifierType_Cpe22 {
c, err := cpe.New(r.GetIdentifier(), cpe.DeclaredSource)
if err == nil {
cpes = append(cpes, c)
continue
}
log.Tracef("unable to extract SPDX IDENTIFIER CPE=%q: %+v", r.GetIdentifier(), err)
// try the locators
for _, l := range r.GetIdentifierLocators() {
c, err = cpe.New(string(l), cpe.DeclaredSource)
if err != nil {
log.Tracef("unable to extract SPDX CPE=%q: %+v", l, err)
continue
}
cpes = append(cpes, c)
}
}
}
return cpes
}
// v3packageIDsToSkip returns a set of packageIDs that should not be imported
func v3packageIDsToSkip(doc *spdx.Document) ptrMap[struct{}] {
skipIDs := ptrMap[struct{}]{}
for _, r := range doc.Elements.Relationships() {
if r != nil && r.GetFrom() != nil && r.GetType() == spdx.RelationshipType_Generates {
skipIDs.Set(r.GetFrom(), struct{}{}) // flipped from GENERATED_FROM
}
}
return skipIDs
}
// toChecksum takes a checksum in the format <algorithm>:<hash> and returns an spdx.Checksum or nil if the string is invalid
func v3toChecksum(algorithmHash string) spdx.AnyHash {
parts := strings.Split(algorithmHash, ":")
if len(parts) < 2 {
return nil
}
return &spdx.Hash{
Algorithm: v3toChecksumAlgorithm(parts[0]),
Value: parts[1],
}
}
func v3toChecksumAlgorithm(algorithm string) spdx.HashAlgorithm {
// this needs to be an uppercase version of our algorithm
switch strings.ToLower(algorithm) {
case "sha1":
return spdx.HashAlgorithm_Sha1
case "sha256":
return spdx.HashAlgorithm_Sha256
case "sha384":
return spdx.HashAlgorithm_Sha384
case "sha512":
return spdx.HashAlgorithm_Sha512
case "md5":
return spdx.HashAlgorithm_Md5
}
return spdx.HashAlgorithm{}
}
func v3relationshipMap(doc *spdx.Document) ptrMap[[]spdx.AnyRelationship] {
relationships := ptrMap[[]spdx.AnyRelationship]{}
for _, r := range doc.Elements.Relationships() {
existing := relationships.Get(r.GetFrom())
relationships.Set(r.GetFrom(), append(existing, r))
}
return relationships
}
// SPDX 3 values are stored as pointers, and there is a distinct possibility that an ID
// will be blank if it was a JSON-LD blank node in the document, as these IDs are not persisted
// nor should they be persisted from decoding
type ptrMap[T any] map[reflect.Value]T
func (s ptrMap[T]) Set(k any, v T) {
s[ptrTo(k)] = v
}
func (s ptrMap[T]) Get(k any) T {
return s[ptrTo(k)]
}
func (s ptrMap[T]) Remove(k any) {
delete(s, ptrTo(k))
}
func (s ptrMap[T]) Has(k any) bool {
_, ok := s[ptrTo(k)]
return ok
}
func ptrTo(k any) reflect.Value {
rv := reflect.ValueOf(k)
if rv.Kind() != reflect.Pointer {
// this case is a programming problem -- all objects in the SPDX 3 model are pointers and may not have IDs set
panic(fmt.Errorf("value is not a pointer; comparable SPDX 3 elements are pointers, this is probably an implementation issue: %#v", k))
}
return rv
}
@@ -0,0 +1,436 @@
package spdxhelpers
import (
"testing"
spdx "github.com/spdx/tools-golang/spdx/v3/v3_0"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/license"
"github.com/anchore/syft/syft/pkg"
)
func TestToSyftModelV3_documentConversion(t *testing.T) {
// Build SPDX 3.0 elements
pkg1 := &spdx.Package{
ID: "pkg-1-id",
Name: "pkg-1",
Version: "1.0.0",
ExternalIdentifiers: spdx.ExternalIdentifierList{
&spdx.ExternalIdentifier{
Type: spdx.ExternalIdentifierType_PackageURL,
Identifier: "pkg:npm/pkg-1@1.0.0",
IdentifierLocators: []spdx.URI{"pkg:npm/pkg-1@1.0.0"},
},
},
}
pkg2 := &spdx.Package{
ID: "pkg-2-id",
Name: "pkg-2",
Version: "2.0.0",
ExternalIdentifiers: spdx.ExternalIdentifierList{
&spdx.ExternalIdentifier{
Type: spdx.ExternalIdentifierType_PackageURL,
Identifier: "pkg:npm/pkg-2@2.0.0",
IdentifierLocators: []spdx.URI{"pkg:npm/pkg-2@2.0.0"},
},
},
}
file1 := &spdx.File{
ID: "file-1-id",
Name: "/src/main.go",
VerifiedUsing: spdx.IntegrityMethodList{
&spdx.Hash{
Algorithm: spdx.HashAlgorithm_Sha256,
Value: "abc123",
},
},
}
file2 := &spdx.File{
ID: "file-2-id",
Name: "/src/util.go",
}
// licenses
pkg1ConcludedLicense := &spdx.ListedLicense{Name: "MIT"}
pkg1DeclaredLicense := &spdx.DisjunctiveLicenseSet{
Members: spdx.LicenseInfoList{
&spdx.ListedLicense{Name: "MIT"},
&spdx.ListedLicense{Name: "Apache-2.0"},
},
}
pkg2ConcludedLicense := &spdx.ConjunctiveLicenseSet{
Members: spdx.LicenseInfoList{
&spdx.ListedLicense{Name: "BSD-3-Clause"},
&spdx.OrLaterOperator{
SubjectLicense: &spdx.ListedLicense{Name: "GPL-2.0"},
},
},
}
// relationships
pkg1DependsOnPkg2 := &spdx.Relationship{
From: pkg1,
To: spdx.ElementList{pkg2},
Type: spdx.RelationshipType_DependsOn,
}
pkg1ContainsFile1 := &spdx.Relationship{
From: pkg1,
To: spdx.ElementList{file1},
Type: spdx.RelationshipType_Contains,
}
pkg2ContainsFile2 := &spdx.Relationship{
From: pkg2,
To: spdx.ElementList{file2},
Type: spdx.RelationshipType_Contains,
}
pkg1HasConcludedLicense := &spdx.Relationship{
From: pkg1,
To: spdx.ElementList{pkg1ConcludedLicense},
Type: spdx.RelationshipType_HasConcludedLicense,
}
pkg1HasDeclaredLicense := &spdx.Relationship{
From: pkg1,
To: spdx.ElementList{pkg1DeclaredLicense},
Type: spdx.RelationshipType_HasDeclaredLicense,
}
pkg2HasConcludedLicense := &spdx.Relationship{
From: pkg2,
To: spdx.ElementList{pkg2ConcludedLicense},
Type: spdx.RelationshipType_HasConcludedLicense,
}
sbomElement := &spdx.SBOM{
RootElements: spdx.ElementList{pkg1},
Elements: spdx.ElementList{
pkg1, pkg2,
file1, file2,
pkg1DependsOnPkg2,
pkg1ContainsFile1,
pkg2ContainsFile2,
pkg1HasConcludedLicense,
pkg1HasDeclaredLicense,
pkg2HasConcludedLicense,
},
}
doc := &spdx.Document{
SpdxDocument: spdx.SpdxDocument{
ID: "https://example.org/test-doc",
Name: "test-document",
RootElements: spdx.ElementList{sbomElement},
Elements: spdx.ElementList{
sbomElement,
pkg1, pkg2,
file1, file2,
pkg1DependsOnPkg2,
pkg1ContainsFile1,
pkg2ContainsFile2,
pkg1HasConcludedLicense,
pkg1HasDeclaredLicense,
pkg2HasConcludedLicense,
},
},
}
// Convert
result, err := ToSyftModelV3(doc)
require.NoError(t, err)
require.NotNil(t, result)
// Verify packages
allPkgs := result.Artifacts.Packages.Sorted()
require.Len(t, allPkgs, 2, "expected 2 packages")
pkgsByName := map[string]pkg.Package{}
for _, p := range allPkgs {
pkgsByName[p.Name] = p
}
syftPkg1, ok := pkgsByName["pkg-1"]
require.True(t, ok, "pkg-1 not found")
assert.Equal(t, "1.0.0", syftPkg1.Version)
assert.Equal(t, "pkg:npm/pkg-1@1.0.0", syftPkg1.PURL)
syftPkg2, ok := pkgsByName["pkg-2"]
require.True(t, ok, "pkg-2 not found")
assert.Equal(t, "2.0.0", syftPkg2.Version)
assert.Equal(t, "pkg:npm/pkg-2@2.0.0", syftPkg2.PURL)
// Verify pkg-1 licenses: concluded MIT + declared (MIT OR Apache-2.0)
pkg1Licenses := syftPkg1.Licenses.ToSlice()
require.NotEmpty(t, pkg1Licenses, "pkg-1 should have licenses")
var pkg1Concluded, pkg1Declared []pkg.License
for _, l := range pkg1Licenses {
switch l.Type {
case license.Concluded:
pkg1Concluded = append(pkg1Concluded, l)
case license.Declared:
pkg1Declared = append(pkg1Declared, l)
}
}
require.Len(t, pkg1Concluded, 1)
assert.Equal(t, "MIT", pkg1Concluded[0].Value)
require.Len(t, pkg1Declared, 1)
assert.Equal(t, "(MIT OR Apache-2.0)", pkg1Declared[0].Value)
// Verify pkg-2 licenses: concluded BSD-3-Clause AND GPL-2.0-only+
pkg2Licenses := syftPkg2.Licenses.ToSlice()
require.NotEmpty(t, pkg2Licenses, "pkg-2 should have licenses")
var pkg2Concluded []pkg.License
for _, l := range pkg2Licenses {
if l.Type == license.Concluded {
pkg2Concluded = append(pkg2Concluded, l)
}
}
require.Len(t, pkg2Concluded, 1)
assert.Equal(t, "BSD-3-Clause AND GPL-2.0+", pkg2Concluded[0].Value)
// Verify files
coords1 := file.Coordinates{RealPath: "/src/main.go"}
digests, ok := result.Artifacts.FileDigests[coords1]
require.True(t, ok, "file1 digests not found")
require.Len(t, digests, 1)
assert.Equal(t, "sha256", digests[0].Algorithm)
assert.Equal(t, "abc123", digests[0].Value)
coords2 := file.Coordinates{RealPath: "/src/util.go"}
_, ok = result.Artifacts.FileMetadata[coords2]
assert.True(t, ok, "file2 metadata not found")
// Verify relationships
require.NotEmpty(t, result.Relationships)
foundDependsOn := false
foundPkg1ContainsFile := false
foundPkg2ContainsFile := false
for _, rel := range result.Relationships {
fromPkg, fromOk := rel.From.(pkg.Package)
if !fromOk {
continue
}
switch toPkg := rel.To.(type) {
case pkg.Package:
if fromPkg.Name == "pkg-2" && toPkg.Name == "pkg-1" && rel.Type == artifact.DependencyOfRelationship {
foundDependsOn = true
}
case file.Location:
if fromPkg.Name == "pkg-1" && toPkg.RealPath == "/src/main.go" && rel.Type == artifact.ContainsRelationship {
foundPkg1ContainsFile = true
}
if fromPkg.Name == "pkg-2" && toPkg.RealPath == "/src/util.go" && rel.Type == artifact.ContainsRelationship {
foundPkg2ContainsFile = true
}
}
}
assert.True(t, foundDependsOn, "expected pkg-2 DependencyOf pkg-1 relationship")
assert.True(t, foundPkg1ContainsFile, "expected pkg-1 Contains /src/main.go relationship")
assert.True(t, foundPkg2ContainsFile, "expected pkg-2 Contains /src/util.go relationship")
}
func Test_v3licenseInfoToExpression(t *testing.T) {
tests := []struct {
name string
info spdx.AnyLicenseInfo
expected string
}{
{
name: "listed license",
info: &spdx.ListedLicense{Name: "MIT"},
expected: "MIT",
},
{
name: "custom license with ID",
info: &spdx.CustomLicense{ID: "LicenseRef-Custom-1", Name: "Custom License"},
expected: "LicenseRef-Custom-1",
},
{
name: "custom license without ID falls back to name",
info: &spdx.CustomLicense{Name: "Custom License"},
expected: "Custom License",
},
{
name: "license expression",
info: &spdx.LicenseExpression{LicenseExpression: "MIT AND Apache-2.0"},
expected: "MIT AND Apache-2.0",
},
{
name: "or-later operator",
info: &spdx.OrLaterOperator{
SubjectLicense: &spdx.ListedLicense{Name: "GPL-2.0"},
},
expected: "GPL-2.0+",
},
{
name: "conjunctive license set (AND)",
info: &spdx.ConjunctiveLicenseSet{
Members: spdx.LicenseInfoList{
&spdx.ListedLicense{Name: "MIT"},
&spdx.ListedLicense{Name: "Apache-2.0"},
},
},
expected: "MIT AND Apache-2.0",
},
{
name: "disjunctive license set (OR)",
info: &spdx.DisjunctiveLicenseSet{
Members: spdx.LicenseInfoList{
&spdx.ListedLicense{Name: "MIT"},
&spdx.ListedLicense{Name: "Apache-2.0"},
},
},
expected: "(MIT OR Apache-2.0)",
},
{
name: "with-addition operator",
info: &spdx.WithAdditionOperator{
SubjectExtendableLicense: &spdx.ListedLicense{Name: "GPL-2.0-only"},
SubjectAddition: &spdx.ListedLicenseException{AdditionText: "Classpath-exception-2.0"},
},
expected: "GPL-2.0-only WITH Classpath-exception-2.0",
},
{
name: "nested: (MIT OR Apache-2.0) AND GPL-2.0",
info: &spdx.ConjunctiveLicenseSet{
Members: spdx.LicenseInfoList{
&spdx.DisjunctiveLicenseSet{
Members: spdx.LicenseInfoList{
&spdx.ListedLicense{Name: "MIT"},
&spdx.ListedLicense{Name: "Apache-2.0"},
},
},
&spdx.ListedLicense{Name: "GPL-2.0-only"},
},
},
expected: "(MIT OR Apache-2.0) AND GPL-2.0-only",
},
{
name: "nested: GPL-2.0 WITH Classpath OR MIT",
info: &spdx.DisjunctiveLicenseSet{
Members: spdx.LicenseInfoList{
&spdx.WithAdditionOperator{
SubjectExtendableLicense: &spdx.ListedLicense{Name: "GPL-2.0-only"},
SubjectAddition: &spdx.ListedLicenseException{AdditionText: "Classpath-exception-2.0"},
},
&spdx.ListedLicense{Name: "MIT"},
},
},
expected: "(GPL-2.0-only WITH Classpath-exception-2.0 OR MIT)",
},
{
name: "nil returns empty",
info: nil,
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := v3licenseInfoToExpression(tt.info)
assert.Equal(t, tt.expected, got)
})
}
}
func Test_v3toSyftLicenses(t *testing.T) {
tests := []struct {
name string
licenseType license.Type
licenses []spdx.AnyLicenseInfo
expected []string
}{
{
name: "simple listed license",
licenseType: license.Declared,
licenses: []spdx.AnyLicenseInfo{
&spdx.ListedLicense{Name: "MIT"},
},
expected: []string{"MIT"},
},
{
name: "conjunctive set produces single expression",
licenseType: license.Concluded,
licenses: []spdx.AnyLicenseInfo{
&spdx.ConjunctiveLicenseSet{
Members: spdx.LicenseInfoList{
&spdx.ListedLicense{Name: "MIT"},
&spdx.ListedLicense{Name: "Apache-2.0"},
},
},
},
expected: []string{"MIT AND Apache-2.0"},
},
{
name: "multiple license infos",
licenseType: license.Declared,
licenses: []spdx.AnyLicenseInfo{
&spdx.ListedLicense{Name: "MIT"},
&spdx.ListedLicense{Name: "GPL-2.0-only"},
},
expected: []string{"MIT", "GPL-2.0-only"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := v3toSyftLicenses(tt.licenseType, tt.licenses...)
require.Len(t, got, len(tt.expected))
for i, l := range got {
assert.Equal(t, tt.licenseType, l.Type)
assert.Equal(t, tt.expected[i], l.Value)
}
})
}
}
func Test_v3parseSPDXLicenses(t *testing.T) {
p := &spdx.Package{Name: "test-pkg"}
concludedLicense := &spdx.ListedLicense{Name: "MIT"}
declaredLicense := &spdx.ConjunctiveLicenseSet{
Members: spdx.LicenseInfoList{
&spdx.ListedLicense{Name: "Apache-2.0"},
&spdx.ListedLicense{Name: "BSD-3-Clause"},
},
}
relationships := ptrMap[[]spdx.AnyRelationship]{}
relationships.Set(p, []spdx.AnyRelationship{
&spdx.Relationship{
Type: spdx.RelationshipType_HasConcludedLicense,
From: p,
To: spdx.ElementList{concludedLicense},
},
&spdx.Relationship{
Type: spdx.RelationshipType_HasDeclaredLicense,
From: p,
To: spdx.ElementList{declaredLicense},
},
})
licenses := v3parseSPDXLicenses(relationships, p)
require.Len(t, licenses, 2)
var concluded, declared []pkg.License
for _, l := range licenses {
switch l.Type {
case license.Concluded:
concluded = append(concluded, l)
case license.Declared:
declared = append(declared, l)
}
}
require.Len(t, concluded, 1)
assert.Equal(t, "MIT", concluded[0].Value)
require.Len(t, declared, 1)
assert.Equal(t, "Apache-2.0 AND BSD-3-Clause", declared[0].Value)
}
+59
View File
@@ -0,0 +1,59 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.7",
"serialNumber": "urn:uuid:5208fea9-73dd-4624-b596-69fddccdb9e7",
"version": 1,
"metadata": {
"timestamp": "2023-09-29T12:02:02-04:00",
"tools": [
{
"vendor": "anchore",
"name": "syft",
"version": "[not provided]"
}
],
"component": {
"bom-ref": "a0ff99a6af10f11f",
"type": "file",
"name": "go.mod",
"version": "sha256:sha256:dc333f342905248a52e424d8dfd061251d01867d01a4f9d7397144a775ff9ebd"
}
},
"components": [
{
"bom-ref": "pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651?package-id=2ff71a67fb024c86",
"type": "library",
"name": "github.com/wagoodman/go-partybus",
"version": "v0.0.0-20230516145632-8ccac152c651",
"cpe": "cpe:2.3:a:wagoodman:go-partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*",
"purl": "pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651",
"properties": [
{
"name": "syft:package:foundBy",
"value": "go-module-file-cataloger"
},
{
"name": "syft:package:language",
"value": "go"
},
{
"name": "syft:package:metadataType",
"value": "GolangModMetadata"
},
{
"name": "syft:package:type",
"value": "go-module"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:wagoodman:go_partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*"
},
{
"name": "syft:location:0:path",
"value": "/go.mod"
}
]
}
]
}
@@ -1,7 +1,7 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.7",
"serialNumber": "urn:uuid:redacted",
"version": 1,
"metadata": {
@@ -1,7 +1,7 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.7",
"serialNumber": "urn:uuid:redacted",
"version": 1,
"metadata": {
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="urn:uuid:098e8516-ecd5-4130-9d5f-c32ba1ddb0dd" version="1">
<metadata>
<timestamp>2023-09-29T11:48:10-04:00</timestamp>
<tools>
<tool>
<vendor>anchore</vendor>
<name>syft</name>
<version>[not provided]</version>
</tool>
</tools>
<component bom-ref="a0ff99a6af10f11f" type="file">
<name>go.mod</name>
<version>sha256:sha256:dc333f342905248a52e424d8dfd061251d01867d01a4f9d7397144a775ff9ebd</version>
</component>
</metadata>
<components>
<component bom-ref="pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651?package-id=2ff71a67fb024c86" type="library">
<name>github.com/wagoodman/go-partybus</name>
<version>v0.0.0-20230516145632-8ccac152c651</version>
<cpe>cpe:2.3:a:wagoodman:go-partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*</cpe>
<purl>pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651</purl>
<properties>
<property name="syft:package:foundBy">go-module-file-cataloger</property>
<property name="syft:package:language">go</property>
<property name="syft:package:metadataType">GolangModMetadata</property>
<property name="syft:package:type">go-module</property>
<property name="syft:cpe23">cpe:2.3:a:wagoodman:go_partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*</property>
<property name="syft:location:0:path">/go.mod</property>
</properties>
</component>
</components>
</bom>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="redacted" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="redacted" version="1">
<metadata>
<timestamp>redacted</timestamp>
<tools>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="redacted" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="redacted" version="1">
<metadata>
<timestamp>redacted</timestamp>
<tools>
+28 -5
View File
@@ -14,12 +14,22 @@ import (
)
type EncoderCollection struct {
encoders []sbom.FormatEncoder
defaultVersions map[sbom.FormatID]string
encoders []sbom.FormatEncoder
}
func NewEncoderCollection(encoders ...sbom.FormatEncoder) *EncoderCollection {
defaultVersions := map[sbom.FormatID]string{}
for _, encoder := range encoders {
if defaultVersion, ok := encoder.(defaultVersionIndicator); ok {
if defaultVersion.DefaultVersion() {
defaultVersions[encoder.ID()] = encoder.Version()
}
}
}
return &EncoderCollection{
encoders: encoders,
defaultVersions: defaultVersions,
encoders: encoders,
}
}
@@ -77,13 +87,20 @@ func (e EncoderCollection) Get(name string, version string) sbom.FormatEncoder {
var mostRecentFormat sbom.FormatEncoder
for _, f := range e.encoders {
defaultVersion := e.defaultVersions[f.ID()]
log.WithFields("name", f.ID(), "version", f.Version(), "aliases", f.Aliases()).Trace("considering format")
names := []string{string(f.ID())}
names = append(names, f.Aliases()...)
for _, n := range names {
if cleanFormatName(n) == name && versionMatches(f.Version(), version) {
if mostRecentFormat == nil || f.Version() > mostRecentFormat.Version() {
mostRecentFormat = f
if cleanFormatName(n) == name {
// if the name or alias matches, and the requested version is any-version, use the default
if version == sbom.AnyVersion && defaultVersion != "" {
version = defaultVersion
}
if versionMatches(f.Version(), version) {
if mostRecentFormat == nil || f.Version() > mostRecentFormat.Version() {
mostRecentFormat = f
}
}
}
}
@@ -141,3 +158,9 @@ func Encode(s sbom.SBOM, f sbom.FormatEncoder) ([]byte, error) {
return buff.Bytes(), nil
}
type defaultVersionIndicator interface {
// DefaultVersion a sbom.FormatEncoder may indicate it is the default version to be used
// when no version is specified for the type
DefaultVersion() bool
}
+75
View File
@@ -4,7 +4,9 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/format/internal/spdxutil"
"github.com/anchore/syft/syft/sbom"
)
@@ -109,3 +111,76 @@ func Test_versionMatches(t *testing.T) {
})
}
}
func Test_EncoderCollection_Get(t *testing.T) {
tests := []struct {
name string
searchName string
searchVersion string
expectedID sbom.FormatID
expectedVersion string
}{
{
name: "explicit name and version",
searchName: "spdx-json",
searchVersion: "2.3",
expectedID: spdxutil.JSONFormatID,
expectedVersion: "2.3",
},
{
name: "explicit name without version gets default",
searchName: "spdx-json",
searchVersion: "",
expectedID: spdxutil.JSONFormatID,
expectedVersion: "2.3",
},
{
name: "alias name with version",
searchName: "spdx",
searchVersion: "2.2",
expectedID: spdxutil.TagValueFormatID,
expectedVersion: "2.2",
},
{
name: "alias name without version gets default",
searchName: "spdx",
searchVersion: "",
expectedID: spdxutil.TagValueFormatID,
expectedVersion: "2.3",
},
{
name: "invalid name gets nothing",
searchName: "json-spdx",
searchVersion: "2.3",
expectedID: "",
},
{
name: "invalid version gets nothing",
searchName: "spdx-json",
searchVersion: "2.0",
expectedID: "",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
config := DefaultEncodersConfig()
// ensure SPDX default is 2.3 for test
config.SPDXJSON.DefaultVersion = "2.3"
encoders, err := config.Encoders()
require.NoError(t, err)
collection := NewEncoderCollection(encoders...)
result := collection.Get(test.searchName, test.searchVersion)
if test.expectedID != "" {
require.NotNil(t, result, "expected to find encoder but got nil")
if result != nil {
require.Equal(t, test.expectedID, result.ID())
require.Equal(t, test.expectedVersion, result.Version())
}
} else {
require.Nil(t, result, "expected nil but found encoder")
}
})
}
}
+98 -13
View File
@@ -48,6 +48,7 @@ func backfillFromPurl(p *pkg.Package) {
var cpes []cpe.CPE
epoch := ""
rpmmod := ""
arch := ""
for _, qualifier := range purl.Qualifiers {
switch qualifier.Key {
@@ -65,6 +66,8 @@ func backfillFromPurl(p *pkg.Package) {
epoch = qualifier.Value
case pkg.PURLQualifierRpmModularity:
rpmmod = qualifier.Value
case pkg.PURLQualifierArch:
arch = qualifier.Value
}
}
@@ -84,9 +87,7 @@ func backfillFromPurl(p *pkg.Package) {
setJavaMetadataFromPurl(p, purl)
}
if p.Type == pkg.RpmPkg {
setRpmMetadataFromPurl(p, rpmmod)
}
setDistroMetadata(p, rpmmod, arch)
for _, c := range cpes {
if slices.Contains(p.CPEs, c) {
@@ -96,6 +97,19 @@ func backfillFromPurl(p *pkg.Package) {
}
}
func setDistroMetadata(p *pkg.Package, rpmmod, arch string) {
switch p.Type {
case pkg.RpmPkg:
setRpmMetadataFromPurl(p, rpmmod, arch)
case pkg.DebPkg:
setDpkgMetadataFromPurl(p, arch)
case pkg.AlpmPkg:
setAlpmMetadataFromPurl(p, arch)
case pkg.ApkPkg:
setApkMetadataFromPurl(p, arch)
}
}
func setJavaMetadataFromPurl(p *pkg.Package, _ packageurl.PackageURL) {
if p.Type != pkg.JavaPkg {
return
@@ -107,32 +121,103 @@ func setJavaMetadataFromPurl(p *pkg.Package, _ packageurl.PackageURL) {
}
}
func setRpmMetadataFromPurl(p *pkg.Package, rpmmod string) {
func setRpmMetadataFromPurl(p *pkg.Package, rpmmod, arch string) {
if p.Type != pkg.RpmPkg {
return
}
if rpmmod == "" {
if rpmmod == "" && arch == "" {
return
}
if p.Metadata == nil {
p.Metadata = pkg.RpmDBEntry{
ModularityLabel: &rpmmod,
}
return
p.Metadata = pkg.RpmDBEntry{}
}
switch m := p.Metadata.(type) {
case pkg.RpmDBEntry:
if m.ModularityLabel == nil {
if m.ModularityLabel == nil && rpmmod != "" {
m.ModularityLabel = &rpmmod
p.Metadata = m
}
if m.Arch == "" {
m.Arch = arch
}
p.Metadata = m
case pkg.RpmArchive:
if m.ModularityLabel == nil {
if m.ModularityLabel == nil && rpmmod != "" {
m.ModularityLabel = &rpmmod
p.Metadata = m
}
if m.Arch == "" {
m.Arch = arch
}
p.Metadata = m
}
}
func setDpkgMetadataFromPurl(p *pkg.Package, arch string) {
if p.Type != pkg.DebPkg {
return
}
if arch == "" {
return
}
if p.Metadata == nil {
p.Metadata = pkg.DpkgDBEntry{}
}
switch m := p.Metadata.(type) {
case pkg.DpkgDBEntry:
if m.Architecture == "" {
m.Architecture = arch
}
p.Metadata = m
case pkg.DpkgArchiveEntry:
if m.Architecture == "" {
m.Architecture = arch
}
p.Metadata = m
}
}
func setAlpmMetadataFromPurl(p *pkg.Package, arch string) {
if p.Type != pkg.AlpmPkg {
return
}
if arch == "" {
return
}
if p.Metadata == nil {
p.Metadata = pkg.AlpmDBEntry{Architecture: arch}
return
}
if m, ok := p.Metadata.(pkg.AlpmDBEntry); ok {
if m.Architecture == "" {
m.Architecture = arch
}
p.Metadata = m
}
}
func setApkMetadataFromPurl(p *pkg.Package, arch string) {
if p.Type != pkg.ApkPkg {
return
}
if arch == "" {
return
}
if p.Metadata == nil {
p.Metadata = pkg.ApkDBEntry{Architecture: arch}
return
}
if m, ok := p.Metadata.(pkg.ApkDBEntry); ok {
if m.Architecture == "" {
m.Architecture = arch
}
p.Metadata = m
}
}
+241
View File
@@ -39,6 +39,9 @@ func Test_Backfill(t *testing.T) {
Type: pkg.RpmPkg,
Name: "dbus-common",
Version: "1.12.8-26.el8",
Metadata: pkg.RpmDBEntry{
Arch: "noarch",
},
},
},
{
@@ -51,6 +54,9 @@ func Test_Backfill(t *testing.T) {
Type: pkg.RpmPkg,
Name: "dbus-common",
Version: "1:1.12.8-26.el8",
Metadata: pkg.RpmDBEntry{
Arch: "noarch",
},
},
},
{
@@ -65,6 +71,22 @@ func Test_Backfill(t *testing.T) {
Version: "2.4.37-51",
Metadata: pkg.RpmDBEntry{
ModularityLabel: strRef("httpd:2.4"),
Arch: "x86_64",
},
},
},
{
name: "rpm with arch and no rpmmod",
in: pkg.Package{
PURL: "pkg:rpm/redhat/httpd@2.4.37-51?arch=x86_64&distro=rhel-8.7",
},
expected: pkg.Package{
PURL: "pkg:rpm/redhat/httpd@2.4.37-51?arch=x86_64&distro=rhel-8.7",
Type: pkg.RpmPkg,
Name: "httpd",
Version: "2.4.37-51",
Metadata: pkg.RpmDBEntry{
Arch: "x86_64",
},
},
},
@@ -121,6 +143,225 @@ func Test_Backfill(t *testing.T) {
Metadata: pkg.JavaArchive{},
},
},
// deb cases
{
name: "deb nil metadata gets arch set",
in: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
},
expected: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
Type: pkg.DebPkg,
Name: "curl",
Version: "7.74.0-1.3+deb11u7",
Metadata: pkg.DpkgDBEntry{
Architecture: "amd64",
},
},
},
{
name: "deb arch:all preserved (nil metadata)",
in: pkg.Package{
PURL: "pkg:deb/debian/tzdata@2021a-1+deb11u10?arch=all&distro=debian-11",
},
expected: pkg.Package{
PURL: "pkg:deb/debian/tzdata@2021a-1+deb11u10?arch=all&distro=debian-11",
Type: pkg.DebPkg,
Name: "tzdata",
Version: "2021a-1+deb11u10",
Metadata: pkg.DpkgDBEntry{
Architecture: "all",
},
},
},
{
name: "deb populated metadata empty arch gets filled",
in: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
Metadata: pkg.DpkgDBEntry{
Package: "curl",
},
},
expected: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
Type: pkg.DebPkg,
Name: "curl",
Version: "7.74.0-1.3+deb11u7",
Metadata: pkg.DpkgDBEntry{
Package: "curl",
Architecture: "amd64",
},
},
},
{
name: "deb populated metadata with arch preserved",
in: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
Metadata: pkg.DpkgDBEntry{
Package: "curl",
Architecture: "arm64",
},
},
expected: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
Type: pkg.DebPkg,
Name: "curl",
Version: "7.74.0-1.3+deb11u7",
Metadata: pkg.DpkgDBEntry{
Package: "curl",
Architecture: "arm64",
},
},
},
{
name: "deb archive entry empty arch gets filled",
in: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
Metadata: pkg.DpkgArchiveEntry{
Package: "curl",
},
},
expected: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
Type: pkg.DebPkg,
Name: "curl",
Version: "7.74.0-1.3+deb11u7",
Metadata: pkg.DpkgArchiveEntry{
Package: "curl",
Architecture: "amd64",
},
},
},
{
name: "deb archive entry with arch preserved",
in: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
Metadata: pkg.DpkgArchiveEntry{
Package: "curl",
Architecture: "arm64",
},
},
expected: pkg.Package{
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
Type: pkg.DebPkg,
Name: "curl",
Version: "7.74.0-1.3+deb11u7",
Metadata: pkg.DpkgArchiveEntry{
Package: "curl",
Architecture: "arm64",
},
},
},
// alpm cases
{
name: "alpm nil metadata gets arch set",
in: pkg.Package{
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
},
expected: pkg.Package{
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
Type: pkg.AlpmPkg,
Name: "curl",
Version: "7.88.1-1",
Metadata: pkg.AlpmDBEntry{
Architecture: "x86_64",
},
},
},
{
name: "alpm populated metadata empty arch gets filled",
in: pkg.Package{
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
Metadata: pkg.AlpmDBEntry{
Package: "curl",
},
},
expected: pkg.Package{
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
Type: pkg.AlpmPkg,
Name: "curl",
Version: "7.88.1-1",
Metadata: pkg.AlpmDBEntry{
Package: "curl",
Architecture: "x86_64",
},
},
},
{
name: "alpm populated metadata with arch preserved",
in: pkg.Package{
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
Metadata: pkg.AlpmDBEntry{
Package: "curl",
Architecture: "aarch64",
},
},
expected: pkg.Package{
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
Type: pkg.AlpmPkg,
Name: "curl",
Version: "7.88.1-1",
Metadata: pkg.AlpmDBEntry{
Package: "curl",
Architecture: "aarch64",
},
},
},
// apk cases
{
name: "apk nil metadata gets arch set",
in: pkg.Package{
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
},
expected: pkg.Package{
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
Type: pkg.ApkPkg,
Name: "curl",
Version: "7.83.1-r2",
Metadata: pkg.ApkDBEntry{
Architecture: "aarch64",
},
},
},
{
name: "apk populated metadata empty arch gets filled",
in: pkg.Package{
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
Metadata: pkg.ApkDBEntry{
Package: "curl",
},
},
expected: pkg.Package{
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
Type: pkg.ApkPkg,
Name: "curl",
Version: "7.83.1-r2",
Metadata: pkg.ApkDBEntry{
Package: "curl",
Architecture: "aarch64",
},
},
},
{
name: "apk populated metadata with arch preserved",
in: pkg.Package{
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
Metadata: pkg.ApkDBEntry{
Package: "curl",
Architecture: "x86_64",
},
},
expected: pkg.Package{
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
Type: pkg.ApkPkg,
Name: "curl",
Version: "7.83.1-r2",
Metadata: pkg.ApkDBEntry{
Package: "curl",
Architecture: "x86_64",
},
},
},
{
name: "target-sw from CPE",
in: pkg.Package{
@@ -9,8 +9,6 @@ import (
"github.com/anchore/syft/syft/sbom"
)
const DefaultVersion = "1.6"
type Encoder struct {
version cyclonedx.SpecVersion
format cyclonedx.BOMFileFormat
@@ -317,7 +317,7 @@ func Test_decodeComponent(t *testing.T) {
},
},
},
wantMetadata: pkg.RpmDBEntry{},
wantMetadata: pkg.RpmDBEntry{Arch: "x86_64"},
wantPURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8",
},
{
@@ -341,6 +341,7 @@ func Test_decodeComponent(t *testing.T) {
},
wantMetadata: pkg.RpmDBEntry{
Release: "some-release",
Arch: "x86_64",
},
wantPURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8",
},
+67 -41
View File
@@ -2,6 +2,10 @@ package cyclonedxutil
import (
"fmt"
"maps"
"slices"
"strconv"
"strings"
"github.com/CycloneDX/cyclonedx-go"
@@ -13,59 +17,81 @@ const (
JSONFormatID sbom.FormatID = "cyclonedx-json"
)
const DefaultVersion = "1.7"
var commonVersions = map[string]cyclonedx.SpecVersion{
"1.2": cyclonedx.SpecVersion1_2,
"1.3": cyclonedx.SpecVersion1_3,
"1.4": cyclonedx.SpecVersion1_4,
"1.5": cyclonedx.SpecVersion1_5,
"1.6": cyclonedx.SpecVersion1_6,
DefaultVersion: cyclonedx.SpecVersion1_7,
}
var allVersions = func() map[string]cyclonedx.SpecVersion {
out := map[string]cyclonedx.SpecVersion{
"1.0": cyclonedx.SpecVersion1_0,
"1.1": cyclonedx.SpecVersion1_1,
}
maps.Copy(out, commonVersions)
return out
}()
func SupportedVersions(id sbom.FormatID) []string {
versions := []string{
"1.2",
"1.3",
"1.4",
"1.5",
"1.6",
versionSet := commonVersions
if id == XMLFormatID {
versionSet = allVersions
}
if id != JSONFormatID {
// JSON format not supported for version < 1.2
versions = append([]string{"1.0", "1.1"}, versions...)
versions := make([]string, 0, len(versionSet))
for _, v := range versionSet {
versions = append(versions, v.String())
}
slices.SortFunc(versions, versionSort)
return versions
}
func SpecVersionFromString(v string) (cyclonedx.SpecVersion, error) {
switch v {
case "1.0":
return cyclonedx.SpecVersion1_0, nil
case "1.1":
return cyclonedx.SpecVersion1_1, nil
case "1.2":
return cyclonedx.SpecVersion1_2, nil
case "1.3":
return cyclonedx.SpecVersion1_3, nil
case "1.4":
return cyclonedx.SpecVersion1_4, nil
case "1.5":
return cyclonedx.SpecVersion1_5, nil
case "1.6":
return cyclonedx.SpecVersion1_6, nil
if specVersion, ok := allVersions[v]; ok {
return specVersion, nil
}
return -1, fmt.Errorf("unsupported CycloneDX version %q", v)
}
func VersionFromSpecVersion(spec cyclonedx.SpecVersion) string {
switch spec {
case cyclonedx.SpecVersion1_0:
return "1.0"
case cyclonedx.SpecVersion1_1:
return "1.1"
case cyclonedx.SpecVersion1_2:
return "1.2"
case cyclonedx.SpecVersion1_3:
return "1.3"
case cyclonedx.SpecVersion1_4:
return "1.4"
case cyclonedx.SpecVersion1_5:
return "1.5"
case cyclonedx.SpecVersion1_6:
return "1.6"
for version, specVersion := range allVersions {
if specVersion == spec {
return version
}
}
return ""
}
func versionSort(a string, b string) int {
partsA := strings.Split(a, ".")
partsB := strings.Split(b, ".")
lenA := len(partsA)
lenB := len(partsB)
for i := range max(lenA, lenB) {
if i >= lenA {
return -1 // 1 < 1.x
}
if i >= lenB {
return 1 // 1.x > 1
}
partA, errA := strconv.ParseInt(partsA[i], 10, 64)
partB, errB := strconv.ParseInt(partsB[i], 10, 64)
if errA != nil || errB != nil {
// string compare if we can't parse one of the sides
strcmp := strings.Compare(partsA[i], partsB[i])
if strcmp == 0 {
continue
}
return strcmp // not equal
}
if partA == partB {
continue
}
return int(partA - partB)
}
return 0
}
@@ -0,0 +1,107 @@
package cyclonedxutil
import (
"go/constant"
"go/types"
"regexp"
"slices"
"testing"
"github.com/CycloneDX/cyclonedx-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/tools/go/packages"
)
// Test_allSpecVersionsMapped loads the cyclonedx-go package's type information and enumerates every
// SpecVersion* constant it declares, asserting each one is present in our version maps. Upgrading
// cyclonedx-go to a release that adds a new spec version will fail this test until versions.go is updated.
func Test_allSpecVersionsMapped(t *testing.T) {
const pkgPath = "github.com/CycloneDX/cyclonedx-go"
pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName | packages.NeedTypes}, pkgPath)
require.NoError(t, err)
require.Len(t, pkgs, 1)
pkg := pkgs[0]
require.Empty(t, pkg.Errors, "errors loading %s", pkgPath)
require.NotNil(t, pkg.Types, "no type info loaded for %s", pkgPath)
// allVersions is the superset (json/common versions merged in), so it covers every spec version we map.
mapped := make(map[cyclonedx.SpecVersion]struct{})
for syftVersion, sv := range allVersions {
// make sure the key matches the version it's mapped to
require.Contains(t, sv.String(), syftVersion, "version %q is not correctly mapped to cyclonedx SpecVersion %q", syftVersion, sv.String())
mapped[sv] = struct{}{}
}
specVersionConst := regexp.MustCompile(`^SpecVersion\d`)
scope := pkg.Types.Scope()
found := 0
for _, name := range scope.Names() {
if !specVersionConst.MatchString(name) {
continue
}
c, ok := scope.Lookup(name).(*types.Const)
if !ok {
continue
}
found++
val, ok := constant.Int64Val(c.Val())
require.Truef(t, ok, "could not read int value of cyclonedx.%s", name)
_, ok = mapped[cyclonedx.SpecVersion(val)]
assert.Truef(t, ok, "cyclonedx.%s is not mapped in versions.go; add it when upgrading cyclonedx-go", name)
}
require.NotZero(t, found, "no SpecVersion* constants found in %s", pkgPath)
}
func Test_versionSort(t *testing.T) {
// versionSort returns <0 when a<b, 0 when a==b, >0 when a>b; assert on sign rather than exact value.
sign := func(n int) int {
switch {
case n < 0:
return -1
case n > 0:
return 1
default:
return 0
}
}
tests := []struct {
name string
a string
b string
want int
}{
{name: "equal single part", a: "1", b: "1", want: 0},
{name: "equal two parts", a: "1.4", b: "1.4", want: 0},
{name: "minor less than", a: "1.3", b: "1.4", want: -1},
{name: "minor greater than", a: "1.6", b: "1.5", want: 1},
{name: "major less than", a: "1.7", b: "2.0", want: -1},
{name: "major greater than", a: "2.0", b: "1.7", want: 1},
{name: "fewer parts sorts first", a: "1", b: "1.0", want: -1},
{name: "more parts sorts last", a: "1.0", b: "1", want: 1},
{name: "numeric not lexical (10 > 9)", a: "1.10", b: "1.9", want: 1},
{name: "multi-digit major", a: "10.0", b: "9.0", want: 1},
{name: "three parts equal", a: "1.4.0", b: "1.4.0", want: 0},
{name: "three parts patch differs", a: "1.4.1", b: "1.4.0", want: 1},
{name: "non-numeric equal falls through", a: "1.x", b: "1.x", want: 0},
{name: "non-numeric string compare", a: "1.a", b: "1.b", want: -1},
{name: "non-numeric side string compare", a: "1.2", b: "1.x", want: -1},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, sign(versionSort(tt.a, tt.b)))
// comparator must be antisymmetric: swapping operands negates the sign.
assert.Equal(t, -tt.want, sign(versionSort(tt.b, tt.a)))
})
}
}
func Test_versionSort_sortsSlice(t *testing.T) {
versions := []string{"1.10", "1.2", "1", "2.0", "1.9", "1.0"}
slices.SortFunc(versions, versionSort)
assert.Equal(t, []string{"1", "1.0", "1.2", "1.9", "1.10", "2.0"}, versions)
}
@@ -13,6 +13,7 @@ func Test_OriginatorSupplier(t *testing.T) {
completionTester := packagemetadata.NewCompletionTester(t,
pkg.BinarySignature{},
pkg.BitnamiSBOMEntry{},
pkg.BunLockEntry{},
pkg.CocoaPodfileLockEntry{},
pkg.ConanV1LockEntry{},
pkg.ConanV2LockEntry{}, // the field Username might be the username of either the package originator or the supplier (unclear currently)
@@ -56,6 +57,8 @@ func Test_OriginatorSupplier(t *testing.T) {
pkg.YarnLockEntry{},
pkg.TerraformLockProviderEntry{},
pkg.GGUFFileHeader{},
pkg.DenoLockEntry{},
pkg.DenoRemoteLockEntry{},
)
tests := []struct {
name string
+7 -3
View File
@@ -4,9 +4,10 @@ import (
"github.com/anchore/syft/syft/sbom"
)
const DefaultVersion = "2.3"
const (
DefaultVersion = "2.3"
V3_0 = "3.0"
JSONFormatID sbom.FormatID = "spdx-json"
TagValueFormatID sbom.FormatID = "spdx-tag-value"
)
@@ -19,7 +20,10 @@ func SupportedVersions(id sbom.FormatID) []string {
if id != JSONFormatID {
// JSON format is not supported in v2.1
return append([]string{"2.1"}, versions...)
versions = append([]string{"2.1"}, versions...)
} else {
// is JSON, v3 only supported in JSON format:
versions = append(versions, V3_0)
}
return versions
+20 -16
View File
@@ -58,10 +58,11 @@ func TestDecoder_Decode(t *testing.T) {
purl: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit",
expected: []pkg.Package{
{
Name: "sysv-rc",
Version: "2.88dsf-59",
Type: pkg.DebPkg,
PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit",
Name: "sysv-rc",
Version: "2.88dsf-59",
Type: pkg.DebPkg,
PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit",
Metadata: pkg.DpkgDBEntry{Architecture: "all"},
},
},
},
@@ -91,10 +92,11 @@ func TestDecoder_Decode(t *testing.T) {
purl: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm",
expected: []pkg.Package{
{
Name: "systemd-x",
Version: "239-82.el8_10.2",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm",
Name: "systemd-x",
Version: "239-82.el8_10.2",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm",
Metadata: pkg.RpmDBEntry{Arch: "aarch64"},
},
},
},
@@ -102,10 +104,11 @@ func TestDecoder_Decode(t *testing.T) {
purl: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm",
expected: []pkg.Package{
{
Name: "dbus-common",
Version: "1:1.12.8-26.el8",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm",
Name: "dbus-common",
Version: "1:1.12.8-26.el8",
Type: pkg.RpmPkg,
PURL: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm",
Metadata: pkg.RpmDBEntry{Arch: "noarch"},
},
},
},
@@ -113,10 +116,11 @@ func TestDecoder_Decode(t *testing.T) {
purl: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3",
expected: []pkg.Package{
{
Name: "curl",
Version: "7.61.1",
Type: pkg.ApkPkg,
PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3",
Name: "curl",
Version: "7.61.1",
Type: pkg.ApkPkg,
PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3",
Metadata: pkg.ApkDBEntry{Architecture: "aarch64"},
},
},
},
+16 -6
View File
@@ -44,6 +44,10 @@ func (d decoder) Decode(r io.Reader) (*sbom.SBOM, sbom.FormatID, string, error)
return nil, "", "", fmt.Errorf("unable to seek to start of SPDX JSON SBOM: %+v", err)
}
if strings.HasPrefix(version, "3") {
return decodeSpdx3(version, reader)
}
doc, err := spdxJson.Read(reader)
if err != nil {
return nil, id, version, fmt.Errorf("unable to decode spdx json: %w", err)
@@ -62,11 +66,6 @@ func (d decoder) Identify(r io.Reader) (sbom.FormatID, string) {
return "", ""
}
if _, err := reader.Seek(0, io.SeekStart); err != nil {
log.Debugf("unable to seek to start of SPDX JSON SBOM: %+v", err)
return "", ""
}
// Example JSON document
// {
// "spdxVersion": "SPDX-2.3",
@@ -85,7 +84,18 @@ func (d decoder) Identify(r io.Reader) (sbom.FormatID, string) {
id, version := getFormatInfo(doc.SPDXVersion)
if version == "" || id != ID {
// not a spdx json document that we support
// not a spdx 2 json document that we support, check for v3
if _, err = reader.Seek(0, io.SeekStart); err != nil {
log.Debugf("unable to seek to start of SPDX JSON SBOM: %+v", err)
return "", ""
}
id3, version3 := identifySpdx3(reader)
if id3 != "" && version3 != "" {
return id3, version3
}
return "", ""
}
+17 -4
View File
@@ -9,6 +9,7 @@ import (
"github.com/spdx/tools-golang/spdx/v2/v2_1"
"github.com/spdx/tools-golang/spdx/v2/v2_2"
"github.com/spdx/tools-golang/spdx/v2/v2_3"
"github.com/spdx/tools-golang/spdx/v3/v3_0"
"github.com/anchore/syft/syft/format/common/spdxhelpers"
"github.com/anchore/syft/syft/format/internal/spdxutil"
@@ -22,8 +23,9 @@ func SupportedVersions() []string {
}
type EncoderConfig struct {
Version string
Pretty bool // don't include spaces and newlines; same as jq -c
Version string
Pretty bool // don't include spaces and newlines; same as jq -c
DefaultVersion string
}
type encoder struct {
@@ -38,8 +40,9 @@ func NewFormatEncoderWithConfig(cfg EncoderConfig) (sbom.FormatEncoder, error) {
func DefaultEncoderConfig() EncoderConfig {
return EncoderConfig{
Version: spdxutil.DefaultVersion,
Pretty: false,
DefaultVersion: spdxutil.DefaultVersion,
Version: spdxutil.DefaultVersion,
Pretty: false,
}
}
@@ -68,6 +71,7 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
doc := v2_1.Document{}
err = convert.Document(latestDoc, &doc)
encodeDoc = doc
case "2.2":
doc := v2_2.Document{}
err = convert.Document(latestDoc, &doc)
@@ -77,6 +81,11 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
doc := v2_3.Document{}
err = convert.Document(latestDoc, &doc)
encodeDoc = doc
case spdxutil.V3_0:
doc := &v3_0.Document{}
err = convert.Document(latestDoc, doc)
encodeDoc = doc
default:
return fmt.Errorf("unsupported SPDX version %q", e.cfg.Version)
}
@@ -95,3 +104,7 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
return enc.Encode(encodeDoc)
}
func (e encoder) DefaultVersion() bool {
return e.cfg.DefaultVersion == e.cfg.Version
}
+6
View File
@@ -216,6 +216,10 @@ func redactor(values ...string) testutil.Redactor {
// each SBOM reports a unique documentNamespace when generated, this is not useful for snapshot testing
`"documentNamespace":\s+"[^"]*"`: `"documentNamespace":"redacted"`,
// spdx3 IDs are URI with the documentnamespace spdxId containing a UID; namespace is the equivalent documentNamespace with the same value
`"spdxId":\s+"[^"]*"`: `"spdxId":"https://redacted"`,
`"namespace":\s+"[^"]*"`: `"namespace":"https://redacted/"`,
// the license list will be updated periodically, the value here should not be directly tested in snapshot tests
`"licenseListVersion":\s+"[^"]*"`: `"licenseListVersion":"redacted"`,
},
@@ -239,12 +243,14 @@ func TestSupportedVersions(t *testing.T) {
"2.2": 2,
// the source-to-package relationships can be removed since the primaryPackagePurpose info is available in 2.3
"2.3": 0,
"3.0": 0,
}
pkgCountOffsetPerVersion := map[string]int{
"2.1": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
"2.2": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
"2.3": 0, // the source package can be removed since the primaryPackagePurpose info is available
"3.0": 0,
}
for _, enc := range encs {
+63
View File
@@ -0,0 +1,63 @@
package spdxjson
import (
"encoding/json"
"fmt"
"io"
"regexp"
"github.com/spdx/tools-golang/spdx/v3/v3_0"
"github.com/anchore/syft/syft/format/common/spdxhelpers"
"github.com/anchore/syft/syft/format/internal/spdxutil"
"github.com/anchore/syft/syft/sbom"
)
var spdx3_0contextRegex = spdxContextRegex(spdxutil.V3_0)
func decodeSpdx3(version string, reader io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) {
switch version {
case spdxutil.V3_0:
doc := v3_0.NewDocument(v3_0.ProfileIdentifierType_Software, "", nil, nil)
err := doc.FromJSON(reader)
if err != nil {
return nil, "", "", err
}
sb, err := spdxhelpers.ToSyftModelV3(doc)
return sb, spdxutil.JSONFormatID, spdxutil.V3_0, err
default:
return nil, "", "", fmt.Errorf("unsupported version: %v", version)
}
}
func identifySpdx3(reader io.Reader) (sbom.FormatID, string) {
type Document struct {
Context string `json:"@context"`
}
dec := json.NewDecoder(reader)
var doc Document
if err := dec.Decode(&doc); err != nil {
// maybe not json? maybe not valid? doesn't matter, we won't process it.
return "", ""
}
formatID := sbom.FormatID("")
spdxVersion := ""
switch {
case doc.Context == "":
case spdx3_0contextRegex.MatchString(doc.Context):
formatID = spdxutil.JSONFormatID
spdxVersion = spdxutil.V3_0
default:
}
return formatID, spdxVersion
}
func spdxContextRegex(minorVersion string) *regexp.Regexp {
// today this is "3.0.1", but is likely to be changed to only include the minor version "3.0"
return regexp.MustCompile(regexp.QuoteMeta("https://spdx.org/rdf/") + minorVersion + `(\.\d+)?` + regexp.QuoteMeta("/spdx-context.jsonld"))
}
+426
View File
@@ -0,0 +1,426 @@
package spdxjson
import (
"bytes"
"context"
"sort"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/require"
"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/spdxutil"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
// TestSPDX3JSONRoundTrip_AllPackageTypes encodes a syft SBOM as SPDX 3.0 JSON and decodes it back, asserting
// (via a full struct diff) that every package field representable in SPDX 3.0 survives the trip.
//
// Each case provides the package to encode (input) and the package expected after decoding
// (want). They differ because some information is not representable in the SPDX 3.0 model:
// - Locations / FoundBy are not encoded per-package (ignored in the diff below).
// - The package ID is derived from the SPDX element ID rather than the original syft ID
// (the unexported id field is ignored in the diff below).
// - Type and Language are reconstructed from the package URL type.
// - Metadata is only reconstructed for the package types with dedicated handling in the
// SPDX encoder/decoder (apk, deb, rpm, java-archive, go-module); for all other types the
// decoded package carries no metadata.
func TestSPDX3JSONRoundTrip_AllPackageTypes(t *testing.T) {
ctx := context.Background()
license := func(value string) pkg.LicenseSet {
return pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, value))
}
cpes := func(value string) []cpe.CPE {
return []cpe.CPE{cpe.Must(value, cpe.DeclaredSource)}
}
cases := []struct {
name string
input pkg.Package
want pkg.Package
}{
{
name: "python package (no reconstructed metadata)",
input: pkg.Package{
Name: "package-python",
Version: "1.0.1",
Type: pkg.PythonPkg,
Language: pkg.Python,
Licenses: license("MIT"),
CPEs: cpes("cpe:2.3:a:python:package-python:1.0.1:*:*:*:*:*:*:*"),
Locations: file.NewLocationSet(file.NewLocation("/python")),
PURL: "pkg:pypi/package-python@1.0.1",
Metadata: pkg.PythonPackage{Name: "package-python", Version: "1.0.1"},
},
want: pkg.Package{
Name: "package-python",
Version: "1.0.1",
Type: pkg.PythonPkg,
Language: pkg.Python,
Licenses: license("MIT"),
CPEs: cpes("cpe:2.3:a:python:package-python:1.0.1:*:*:*:*:*:*:*"),
PURL: "pkg:pypi/package-python@1.0.1",
},
},
{
name: "npm package (no reconstructed metadata)",
input: pkg.Package{
Name: "package-npm",
Version: "2.0.1",
Type: pkg.NpmPkg,
Language: pkg.JavaScript,
Licenses: license("Apache-2.0"),
CPEs: cpes("cpe:2.3:a:npm:package-npm:2.0.1:*:*:*:*:*:*:*"),
Locations: file.NewLocationSet(file.NewLocation("/npm")),
PURL: "pkg:npm/package-npm@2.0.1",
Metadata: pkg.NpmPackage{Name: "package-npm", Version: "2.0.1"},
},
want: pkg.Package{
Name: "package-npm",
Version: "2.0.1",
Type: pkg.NpmPkg,
Language: pkg.JavaScript,
Licenses: license("Apache-2.0"),
CPEs: cpes("cpe:2.3:a:npm:package-npm:2.0.1:*:*:*:*:*:*:*"),
PURL: "pkg:npm/package-npm@2.0.1",
},
},
{
name: "apk package",
input: pkg.Package{
Name: "package-apk",
Version: "3.0",
Type: pkg.ApkPkg,
Licenses: license("GPL-2.0-only"),
CPEs: cpes("cpe:2.3:a:alpine:package-apk:3.0:*:*:*:*:*:*:*"),
Locations: file.NewLocationSet(file.NewLocation("/apk")),
PURL: "pkg:apk/alpine/package-apk@3.0?arch=x86_64&upstream=apk-origin",
Metadata: pkg.ApkDBEntry{
Package: "package-apk",
OriginPackage: "apk-origin",
Maintainer: "Alpine Maintainer",
Version: "3.0",
Architecture: "x86_64",
Description: "the apk package",
},
},
want: pkg.Package{
Name: "package-apk",
Version: "3.0",
Type: pkg.ApkPkg,
Licenses: license("GPL-2.0-only"),
CPEs: cpes("cpe:2.3:a:alpine:package-apk:3.0:*:*:*:*:*:*:*"),
PURL: "pkg:apk/alpine/package-apk@3.0?arch=x86_64&upstream=apk-origin",
Metadata: pkg.ApkDBEntry{
Package: "package-apk",
OriginPackage: "apk-origin",
Maintainer: "Alpine Maintainer",
Version: "3.0",
Architecture: "x86_64",
Description: "the apk package",
},
},
},
{
name: "deb package",
input: pkg.Package{
Name: "package-deb",
Version: "4.0",
Type: pkg.DebPkg,
Licenses: license("LGPL-2.1-only"),
CPEs: cpes("cpe:2.3:a:debian:package-deb:4.0:*:*:*:*:*:*:*"),
Locations: file.NewLocationSet(file.NewLocation("/deb")),
PURL: "pkg:deb/debian/package-deb@4.0?arch=amd64&upstream=deb-src%404.1",
Metadata: pkg.DpkgDBEntry{
Package: "package-deb",
Source: "deb-src",
Version: "4.0",
SourceVersion: "4.1",
Architecture: "amd64",
Maintainer: "Debian Maintainer",
},
},
want: pkg.Package{
Name: "package-deb",
Version: "4.0",
Type: pkg.DebPkg,
Licenses: license("LGPL-2.1-only"),
CPEs: cpes("cpe:2.3:a:debian:package-deb:4.0:*:*:*:*:*:*:*"),
PURL: "pkg:deb/debian/package-deb@4.0?arch=amd64&upstream=deb-src%404.1",
Metadata: pkg.DpkgDBEntry{
Package: "package-deb",
Source: "deb-src",
Version: "4.0",
SourceVersion: "4.1",
Architecture: "amd64",
Maintainer: "Debian Maintainer",
},
},
},
{
name: "rpm package",
input: pkg.Package{
Name: "package-rpm",
Version: "5.0",
Type: pkg.RpmPkg,
Licenses: license("BSD-3-Clause"),
CPEs: cpes("cpe:2.3:a:redhat:package-rpm:5.0:*:*:*:*:*:*:*"),
Locations: file.NewLocationSet(file.NewLocation("/rpm")),
PURL: "pkg:rpm/redhat/package-rpm@5.0?arch=x86_64&upstream=rpm-src-5.0",
Metadata: pkg.RpmDBEntry{
Name: "package-rpm",
Version: "5.0",
Arch: "x86_64",
SourceRpm: "rpm-src-5.0",
Vendor: "RedHat",
},
},
want: pkg.Package{
Name: "package-rpm",
Version: "5.0",
Type: pkg.RpmPkg,
Licenses: license("BSD-3-Clause"),
CPEs: cpes("cpe:2.3:a:redhat:package-rpm:5.0:*:*:*:*:*:*:*"),
PURL: "pkg:rpm/redhat/package-rpm@5.0?arch=x86_64&upstream=rpm-src-5.0",
Metadata: pkg.RpmDBEntry{
Name: "package-rpm",
Version: "5.0",
Arch: "x86_64",
SourceRpm: "rpm-src-5.0",
Vendor: "RedHat",
},
},
},
{
name: "java package",
input: pkg.Package{
Name: "package-java",
Version: "6.0",
Type: pkg.JavaPkg,
Language: pkg.Java,
Licenses: license("EPL-2.0"),
CPEs: cpes("cpe:2.3:a:example:package-java:6.0:*:*:*:*:*:*:*"),
Locations: file.NewLocationSet(file.NewLocation("/java")),
PURL: "pkg:maven/com.example/package-java@6.0",
Metadata: pkg.JavaArchive{
ArchiveDigests: []file.Digest{
{Algorithm: "sha1", Value: "3b4ab96c371d913e2a88c269844b6c5fb5cbe761"},
},
},
},
want: pkg.Package{
Name: "package-java",
Version: "6.0",
Type: pkg.JavaPkg,
Language: pkg.Java,
Licenses: license("EPL-2.0"),
CPEs: cpes("cpe:2.3:a:example:package-java:6.0:*:*:*:*:*:*:*"),
PURL: "pkg:maven/com.example/package-java@6.0",
Metadata: pkg.JavaArchive{
ArchiveDigests: []file.Digest{
{Algorithm: "sha1", Value: "3b4ab96c371d913e2a88c269844b6c5fb5cbe761"},
},
},
},
},
{
name: "go module package",
input: pkg.Package{
Name: "package-go",
Version: "7.0",
Type: pkg.GoModulePkg,
Language: pkg.Go,
Licenses: license("MPL-2.0"),
CPEs: cpes("cpe:2.3:a:example:package-go:7.0:*:*:*:*:*:*:*"),
Locations: file.NewLocationSet(file.NewLocation("/go")),
PURL: "pkg:golang/example.com/package-go@7.0",
Metadata: pkg.GolangBinaryBuildinfoEntry{
H1Digest: "h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=",
},
},
want: pkg.Package{
Name: "package-go",
Version: "7.0",
Type: pkg.GoModulePkg,
Language: pkg.Go,
Licenses: license("MPL-2.0"),
CPEs: cpes("cpe:2.3:a:example:package-go:7.0:*:*:*:*:*:*:*"),
PURL: "pkg:golang/example.com/package-go@7.0",
Metadata: pkg.GolangBinaryBuildinfoEntry{
H1Digest: "h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=",
},
},
},
}
var inputs []pkg.Package
var want []pkg.Package
for _, c := range cases {
in := c.input
in.SetID()
inputs = append(inputs, in)
want = append(want, c.want)
}
subject := sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(inputs...),
},
Descriptor: sbom.Descriptor{
Name: "syft",
Version: "v0.42.0-bogus",
},
Source: source.Description{
Metadata: source.DirectoryMetadata{Path: "/home/app", Base: "/home/app"},
},
}
cfg := DefaultEncoderConfig()
cfg.Pretty = true
cfg.Version = spdxutil.V3_0
enc, err := NewFormatEncoderWithConfig(cfg)
require.NoError(t, err)
var buf bytes.Buffer
require.NoError(t, enc.Encode(&buf, subject))
dec := NewFormatDecoder()
id, version := dec.Identify(bytes.NewReader(buf.Bytes()))
require.Equal(t, ID, id)
require.Equal(t, spdxutil.V3_0, version)
s, decodeID, decodeVersion, err := dec.Decode(bytes.NewReader(buf.Bytes()))
require.NoError(t, err)
require.NotNil(t, s)
require.Equal(t, ID, decodeID)
require.Equal(t, spdxutil.V3_0, decodeVersion)
// the directory source should be recognized as the document root and not surface as a package
require.Equal(t, len(want), s.Artifacts.Packages.PackageCount())
got := s.Artifacts.Packages.Sorted()
sortByName(want)
sortByName(got)
diff := cmp.Diff(want, got,
cmpopts.IgnoreUnexported(pkg.Package{}, pkg.LicenseSet{}, file.LocationSet{}),
cmpopts.IgnoreFields(pkg.Package{}, "Locations", "FoundBy"),
)
require.Empty(t, diff, "decoded packages differ from expected (-want +got):\n%s", diff)
}
func sortByName(pkgs []pkg.Package) {
sort.Slice(pkgs, func(i, j int) bool {
return pkgs[i].Name < pkgs[j].Name
})
}
// TestSPDX3JSONRoundTrip_FullSBOM encodes a more complete SBOM (a container image source, several
// packages, and relationships between them) as SPDX 3.0 JSON and decodes it back, verifying that
// the source description and the package relationships survive the round trip.
func TestSPDX3JSONRoundTrip_FullSBOM(t *testing.T) {
newPkg := func(name string) pkg.Package {
p := pkg.Package{
Name: name,
Version: "1.0",
Type: pkg.NpmPkg,
Language: pkg.JavaScript,
PURL: "pkg:npm/" + name + "@1.0",
Locations: file.NewLocationSet(
file.NewLocation("/" + name),
),
}
p.SetID()
return p
}
app := newPkg("app")
lib := newPkg("lib")
dep := newPkg("dep")
subject := sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(app, lib, dep),
},
Relationships: []artifact.Relationship{
// app contains lib
{From: app, To: lib, Type: artifact.ContainsRelationship},
// dep is a dependency of lib
{From: dep, To: lib, Type: artifact.DependencyOfRelationship},
},
Descriptor: sbom.Descriptor{
Name: "syft",
Version: "v0.42.0-bogus",
},
Source: source.Description{
Name: "some-image",
Version: "some-tag",
Supplier: "some-supplier",
Metadata: source.ImageMetadata{
UserInput: "some-image:some-tag",
ManifestDigest: "sha256:abcdef0123456789",
Architecture: "amd64",
},
},
}
cfg := DefaultEncoderConfig()
cfg.Pretty = true
cfg.Version = spdxutil.V3_0
enc, err := NewFormatEncoderWithConfig(cfg)
require.NoError(t, err)
var buf bytes.Buffer
require.NoError(t, enc.Encode(&buf, subject))
dec := NewFormatDecoder()
s, decodeID, decodeVersion, err := dec.Decode(bytes.NewReader(buf.Bytes()))
require.NoError(t, err)
require.NotNil(t, s)
require.Equal(t, ID, decodeID)
require.Equal(t, spdxutil.V3_0, decodeVersion)
// the container image source is decoded as the document root rather than a package
require.Equal(t, 3, s.Artifacts.Packages.PackageCount())
// source information survives, and the source is not surfaced as a package
// (the ID is re-derived from the SPDX element ID and is therefore not compared)
require.Equal(t, "some-image", s.Source.Name)
require.Equal(t, "some-tag", s.Source.Version)
require.Equal(t, "some-supplier", s.Source.Supplier)
require.IsType(t, source.ImageMetadata{}, s.Source.Metadata)
imageMetadata := s.Source.Metadata.(source.ImageMetadata)
// the user input is reconstructed from the image name and tag (version)
require.Equal(t, "some-image:some-tag", imageMetadata.UserInput)
require.Equal(t, "sha256:abcdef0123456789", imageMetadata.ManifestDigest)
// relationships between packages survive, including direction
type rel struct {
from string
to string
typ artifact.RelationshipType
}
var got []rel
for _, r := range s.Relationships {
from, fromOk := r.From.(pkg.Package)
to, toOk := r.To.(pkg.Package)
require.True(t, fromOk, "relationship from should be a package: %+v", r.From)
require.True(t, toOk, "relationship to should be a package: %+v", r.To)
got = append(got, rel{from: from.Name, to: to.Name, typ: r.Type})
}
require.ElementsMatch(t, []rel{
{from: "app", to: "lib", typ: artifact.ContainsRelationship},
{from: "dep", to: "lib", typ: artifact.DependencyOfRelationship},
}, got)
}
+141
View File
@@ -0,0 +1,141 @@
{
"@context" : "https://spdx.org/rdf/3.0.1/spdx-context.jsonld",
"@graph" : [ {
"@id" : "_:creationInfo_0",
"type" : "CreationInfo",
"specVersion" : "3.0.1",
"createdBy" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd0" ],
"createdUsing" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/additionalToolSPDXRef-gnrtd2", "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/additionalToolSPDXRef-gnrtd1" ],
"created" : "2021-08-26T01:46:00Z"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd23",
"type" : "Relationship",
"relationshipType" : "hasDeclaredLicense",
"to" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd8" ],
"from" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd4",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/document0",
"type" : "SpdxDocument",
"dataLicense" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd24",
"rootElement" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd4" ],
"name" : "hello",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/additionalToolSPDXRef-gnrtd1",
"type" : "Tool",
"name" : "github.com/spdx/tools-golang/builder",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/additionalToolSPDXRef-gnrtd2",
"type" : "Tool",
"name" : "github.com/spdx/tools-golang/idsearcher",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd8",
"type" : "simplelicensing_LicenseExpression",
"simplelicensing_licenseExpression" : "GPL-3.0-or-later",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd20",
"type" : "simplelicensing_LicenseExpression",
"simplelicensing_licenseExpression" : "NOASSERTION",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd24",
"type" : "simplelicensing_LicenseExpression",
"simplelicensing_licenseExpression" : "CC0-1.0",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd14",
"type" : "LifecycleScopedRelationship",
"relationshipType" : "usesTool",
"scope" : "build",
"to" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd13" ],
"completeness" : "noAssertion",
"from" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd4",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd0",
"type" : "Person",
"externalIdentifier" : [ {
"type" : "ExternalIdentifier",
"identifier" : "steve@swinslow.net",
"externalIdentifierType" : "email"
} ],
"name" : "Steve Winslow",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd6",
"type" : "software_File",
"software_copyrightText" : "Copyright Contributors to the spdx-examples project.",
"verifiedUsing" : [ {
"type" : "Hash",
"algorithm" : "md5",
"hashValue" : "935054fe899ca782e11003bbae5e166c"
}, {
"type" : "Hash",
"algorithm" : "sha1",
"hashValue" : "20862a6d08391d07d09344029533ec644fac6b21"
}, {
"type" : "Hash",
"algorithm" : "sha256",
"hashValue" : "b4e5ca56d1f9110ca94ed0bf4e6d9ac11c2186eb7cd95159c6fdb50e8db5a823"
} ],
"name" : "./src/hello.c",
"software_primaryPurpose" : "source",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd11",
"type" : "software_File",
"software_copyrightText" : "NOASSERTION",
"verifiedUsing" : [ {
"type" : "Hash",
"algorithm" : "sha1",
"hashValue" : "20291a81ef065ff891b537b64d4fdccaf6f5ac02"
}, {
"type" : "Hash",
"algorithm" : "sha256",
"hashValue" : "83a33ff09648bb5fc5272baca88cf2b59fd81ac4cc6817b86998136af368708e"
}, {
"type" : "Hash",
"algorithm" : "md5",
"hashValue" : "08a12c966d776864cc1eb41fd03c3c3d"
} ],
"name" : "./build/hello",
"contentType" : "application/octet-stream",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd13",
"type" : "software_File",
"software_copyrightText" : "NOASSERTION",
"verifiedUsing" : [ {
"type" : "Hash",
"algorithm" : "sha1",
"hashValue" : "69a2e85696fff1865c3f0686d6c3824b59915c80"
}, {
"type" : "Hash",
"algorithm" : "sha256",
"hashValue" : "5da19033ba058e322e21c90e6d6d859c90b1b544e7840859c12cae5da005e79c"
}, {
"type" : "Hash",
"algorithm" : "md5",
"hashValue" : "559424589a4f3f75fd542810473d8bc1"
} ],
"name" : "./src/Makefile",
"software_primaryPurpose" : "source",
"creationInfo" : "_:creationInfo_0"
}, {
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd4",
"type" : "software_Package",
"software_copyrightText" : "NOASSERTION",
"software_downloadLocation" : "git+https://github.com/swinslow/spdx-examples.git#example1/content",
"verifiedUsing" : [ {
"type" : "PackageVerificationCode",
"algorithm" : "sha1",
"hashValue" : "9d20237bb72087e87069f96afb41c6ca2fa2a342"
} ],
"name" : "hello",
"creationInfo" : "_:creationInfo_0"
} ]
}
@@ -26,6 +26,8 @@ catalogers:
- pkg.ApkDBEntry
package_types: # AUTO-GENERATED
- apk
purl_types: # AUTO-GENERATED
- apk
json_schema_types: # AUTO-GENERATED
- ApkDbEntry
capabilities: # MANUAL - preserved across regeneration
@@ -27,6 +27,8 @@ catalogers:
- pkg.AlpmDBEntry
package_types: # AUTO-GENERATED
- alpm
purl_types: # AUTO-GENERATED
- alpm
json_schema_types: # AUTO-GENERATED
- AlpmDbEntry
capabilities: # MANUAL - preserved across regeneration
@@ -786,6 +786,66 @@ catalogers:
cpes:
- cpe:2.3:a:kubernetes:ingress-nginx:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/filebeat'
packages:
- class: filebeat-binary
name: filebeat
purl: pkg:generic/filebeat
cpes:
- cpe:2.3:a:elastic:filebeat:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/metricbeat'
packages:
- class: metricbeat-binary
name: metricbeat
purl: pkg:generic/metricbeat
cpes:
- cpe:2.3:a:elastic:metricbeat:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/heartbeat'
packages:
- class: heartbeat-binary
name: heartbeat
purl: pkg:generic/heartbeat
cpes:
- cpe:2.3:a:elastic:heartbeat:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/packetbeat'
packages:
- class: packetbeat-binary
name: packetbeat
purl: pkg:generic/packetbeat
cpes:
- cpe:2.3:a:elastic:packetbeat:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/auditbeat'
packages:
- class: auditbeat-binary
name: auditbeat
purl: pkg:generic/auditbeat
cpes:
- cpe:2.3:a:elastic:auditbeat:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/elastic-agent'
packages:
- class: elastic-agent-binary
name: elastic-agent
purl: pkg:generic/elastic-agent
cpes:
- cpe:2.3:a:elastic:elastic_agent:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/java'
@@ -932,6 +992,9 @@ catalogers:
- apk
- binary
- rpm
purl_types: # AUTO-GENERATED
- apk
- rpm
json_schema_types: # AUTO-GENERATED
- ElfBinaryPackageNoteJsonPayload
capabilities: # MANUAL - edit capabilities here
@@ -957,6 +1020,7 @@ catalogers:
function: NewPEPackageCataloger
selectors: # AUTO-GENERATED
- binary
- bpl
- declared
- directory
- dll
@@ -973,6 +1037,7 @@ catalogers:
criteria: # AUTO-GENERATED
- '**/*.dll'
- '**/*.exe'
- '**/*.bpl'
metadata_types: # AUTO-GENERATED
- pkg.PEBinary
package_types: # AUTO-GENERATED
@@ -207,6 +207,20 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("mariadb-binary"),
},
},
{
// RHEL / MariaDB.org tarball builds do not embed the "-MariaDB" marker; the version is only
// present in the build path (e.g. mariadb-11.8.5-2-redhat-x86_64). The release suffix ("-2")
// must not leak into the version. Regression for anchore/grype#3452.
logicalFixture: "mariadb/11.8.5/linux-amd64",
expected: pkg.Package{
Name: "mariadb",
Version: "11.8.5",
Type: "binary",
PURL: "pkg:generic/mariadb@11.8.5",
Locations: locations("mariadb"),
Metadata: metadata("mariadb-binary"),
},
},
{
logicalFixture: "mysqld/9.7.0/linux-amd64",
expected: pkg.Package{
@@ -2543,6 +2557,127 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "filebeat/9.4.2/linux-amd64",
expected: pkg.Package{
Name: "filebeat",
Version: "9.4.2",
Type: "binary",
PURL: "pkg:generic/filebeat@9.4.2",
Locations: locations("filebeat"),
Metadata: metadata("filebeat-binary"),
},
},
{
logicalFixture: "filebeat/8.18.4/linux-amd64",
expected: pkg.Package{
Name: "filebeat",
Version: "8.18.4",
Type: "binary",
PURL: "pkg:generic/filebeat@8.18.4",
Locations: locations("filebeat"),
Metadata: metadata("filebeat-binary"),
},
},
{
logicalFixture: "filebeat/8.11.2/linux-amd64",
expected: pkg.Package{
Name: "filebeat",
Version: "8.11.2",
Type: "binary",
PURL: "pkg:generic/filebeat@8.11.2",
Locations: locations("filebeat"),
Metadata: metadata("filebeat-binary"),
},
},
{
logicalFixture: "metricbeat/9.4.2/linux-amd64",
expected: pkg.Package{
Name: "metricbeat",
Version: "9.4.2",
Type: "binary",
PURL: "pkg:generic/metricbeat@9.4.2",
Locations: locations("metricbeat"),
Metadata: metadata("metricbeat-binary"),
},
},
{
logicalFixture: "heartbeat/9.4.2/linux-amd64",
expected: pkg.Package{
Name: "heartbeat",
Version: "9.4.2",
Type: "binary",
PURL: "pkg:generic/heartbeat@9.4.2",
Locations: locations("heartbeat"),
Metadata: metadata("heartbeat-binary"),
},
},
{
logicalFixture: "packetbeat/9.4.2/linux-amd64",
expected: pkg.Package{
Name: "packetbeat",
Version: "9.4.2",
Type: "binary",
PURL: "pkg:generic/packetbeat@9.4.2",
Locations: locations("packetbeat"),
Metadata: metadata("packetbeat-binary"),
},
},
{
logicalFixture: "auditbeat/9.4.2/linux-amd64",
expected: pkg.Package{
Name: "auditbeat",
Version: "9.4.2",
Type: "binary",
PURL: "pkg:generic/auditbeat@9.4.2",
Locations: locations("auditbeat"),
Metadata: metadata("auditbeat-binary"),
},
},
{
logicalFixture: "elastic-agent/9.4.2/linux-amd64",
expected: pkg.Package{
Name: "elastic-agent",
Version: "9.4.2",
Type: "binary",
PURL: "pkg:generic/elastic-agent@9.4.2",
Locations: locations("elastic-agent"),
Metadata: metadata("elastic-agent-binary"),
},
},
{
logicalFixture: "elastic-agent/9.0.0/linux-amd64",
expected: pkg.Package{
Name: "elastic-agent",
Version: "9.0.0",
Type: "binary",
PURL: "pkg:generic/elastic-agent@9.0.0",
Locations: locations("elastic-agent"),
Metadata: metadata("elastic-agent-binary"),
},
},
{
logicalFixture: "elastic-agent/8.19.4/linux-amd64",
expected: pkg.Package{
Name: "elastic-agent",
Version: "8.19.4",
Type: "binary",
PURL: "pkg:generic/elastic-agent@8.19.4",
Locations: locations("elastic-agent"),
Metadata: metadata("elastic-agent-binary"),
},
},
{
logicalFixture: "elastic-agent/8.11.2/linux-amd64",
expected: pkg.Package{
Name: "elastic-agent",
Version: "8.11.2",
Type: "binary",
PURL: "pkg:generic/elastic-agent@8.11.2",
Locations: locations("elastic-agent"),
Metadata: metadata("elastic-agent-binary"),
},
},
{
logicalFixture: "julia/1.13.0-alpha2/linux-amd64",
expected: pkg.Package{
@@ -2643,6 +2778,127 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
},
},
},
{
logicalFixture: "envoy/1.22.10/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.22.10",
Type: "binary",
PURL: "pkg:generic/envoy@1.22.10",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.24.5/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.24.5",
Type: "binary",
PURL: "pkg:generic/envoy@1.24.5",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.30.2/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.30.2",
Type: "binary",
PURL: "pkg:generic/envoy@1.30.2",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.31.2/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.31.2",
Type: "binary",
PURL: "pkg:generic/envoy@1.31.2",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.34.12/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.34.12",
Type: "binary",
PURL: "pkg:generic/envoy@1.34.12",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.35.4/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.35.4",
Type: "binary",
PURL: "pkg:generic/envoy@1.35.4",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.23.7/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.23.7",
Type: "binary",
PURL: "pkg:generic/envoy@1.23.7",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.24.9/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.24.9",
Type: "binary",
PURL: "pkg:generic/envoy@1.24.9",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.30.1/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.30.1",
Type: "binary",
PURL: "pkg:generic/envoy@1.30.1",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.30.6/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.30.6",
Type: "binary",
PURL: "pkg:generic/envoy@1.30.6",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "envoy/1.36.1/linux-amd64",
expected: pkg.Package{
Name: "envoy",
Version: "1.36.1",
Type: "binary",
PURL: "pkg:generic/envoy@1.36.1",
Locations: locations("envoy"),
Metadata: metadata("envoy-binary"),
},
},
}
for _, test := range tests {
+91 -2
View File
@@ -28,6 +28,18 @@ func DefaultClassifiers() []binutils.Classifier {
// ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [x86_64-linux]
`(?m)ruby (?P<version>[0-9]+\.[0-9]+\.[0-9]+((p|preview|rc|dev)[0-9]*)?) `)
// all Elastic Beats (filebeat, metricbeat, heartbeat, packetbeat, auditbeat)
var elasticBeatsMatcher = binutils.MatchAny(
// 9.x: forcestdinsetupTest 9.4.2%s %w (filebeat/metricbeat/auditbeat)
// forcestdinsetupTest 9.4.2input (heartbeat/packetbeat)
m.FileContentsVersionMatcher(`Test (?P<version>[0-9]+\.[0-9]+\.[0-9]+)[a-z%]`),
// 9.x: exportconfigcreateplugin9.4.2-globalclient
// 8.18.x: exportconfigcreateplugin8.18.4globalclient
m.FileContentsVersionMatcher(`plugin(?:output)?(?P<version>[0-9]+\.[0-9]+\.[0-9]+)[-a-z]`),
// 8.11.x: 5m.rate8.11.2-9765625
m.FileContentsVersionMatcher(`5m\.rate(?P<version>[0-9]+\.[0-9]+\.[0-9]+)-`),
)
classifiers := []binutils.Classifier{
{
Class: "python-binary",
@@ -440,9 +452,17 @@ func DefaultClassifiers() []binutils.Classifier {
{
Class: "mariadb-binary",
FileGlob: "**/{mariadb,mysql}",
EvidenceMatcher: m.FileContentsVersionMatcher(
EvidenceMatcher: binutils.MatchAny(
// 10.6.15-MariaDB
`(?m)(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-MariaDB`),
m.FileContentsVersionMatcher(`(?m)(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-MariaDB`),
// MariaDB.org / RHEL tarball builds embed the release directory name, which does not contain the
// "-MariaDB" marker. The version is in the build path instead, e.g.:
// mariadb-11.8.5-2-redhat-x86_64/rhel-8/bin/mariadb
// mariadb-11.8.5-linux-systemd-x86_64
// Without this the older matcher misses the version and a later release suffix (e.g. "2") can be
// picked up instead, producing false-positive matches against ancient CVEs (see anchore/grype#3452).
m.FileContentsVersionMatcher(`(?m)(?:^|/)mariadb-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-`),
),
Package: "mariadb",
PURL: mustPURL("pkg:generic/mariadb@version"),
CPEs: singleCPE("cpe:2.3:a:mariadb:mariadb:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
@@ -1056,6 +1076,20 @@ func DefaultClassifiers() []binutils.Classifier {
m.FileContentsVersionMatcher(`(?s)\[source/.{0,200}\x00(?P<version>1\.1[0-9]\.[0-9]+(-dev)?)\x00`),
// 1.x [NUL]1.6.0[NUL]RELEASE
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.[0-9]\.[0-9]+(-dev)?)\x00.{0,20}RELEASE`),
// contrib 1.3x [NUL]1.34.12[NUL]envoy_quiche... or [NUL]1.30.2[NUL]envoy/extensions... or [NUL]1.30.8[NUL]envoy://
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.3[0-9]\.[0-9]+(-dev)?)\x00envoy[/._:]`),
// contrib 1.3x [NUL]1.30.7[NUL]...envoy.service or [NUL]1.30.6[NUL]...envoy.data
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.3[0-9]\.[0-9]+(-dev)?)\x00.{0,200}envoy[./]`),
// contrib1.3x [NUL]1.31.2[NUL]...envoy_quic or [NUL]1.36.x[NUL]...envoy_quic
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.3[0-9]\.[0-9]+(-dev)?)\x00.{0,1000}envoy_quic`),
// contrib 1.3x [NUL]1.30.1[NUL]TLS client
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.3[0-9]\.[0-9]+(-dev)?)\x00TLS `),
// contrib 1.2x [NUL]1.24.9[NUL]envoy/extensions...
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.2[0-9]\.[0-9]+(-dev)?)\x00envoy[/._]`),
// contrib 1.2x [NUL]1.22.10[NUL]...envoy. or [NUL]1.24.5[NUL]...envoy.
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.2[0-9]\.[0-9]+(-dev)?)\x00.{0,500}envoy\.`),
// contrib 1.2x [NUL]1.23.7[NUL]...envoy_internal
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.2[0-9]\.[0-9]+(-dev)?)\x00.{0,300}envoy_`),
),
Package: "envoy",
PURL: mustPURL("pkg:generic/envoy@version"),
@@ -1102,6 +1136,61 @@ func DefaultClassifiers() []binutils.Classifier {
PURL: mustPURL("pkg:generic/nginx-ingress-controller@version"),
CPEs: singleCPE("cpe:2.3:a:kubernetes:ingress-nginx:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "filebeat-binary",
FileGlob: "**/filebeat",
EvidenceMatcher: elasticBeatsMatcher,
Package: "filebeat",
PURL: mustPURL("pkg:generic/filebeat@version"),
CPEs: singleCPE("cpe:2.3:a:elastic:filebeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "metricbeat-binary",
FileGlob: "**/metricbeat",
EvidenceMatcher: elasticBeatsMatcher,
Package: "metricbeat",
PURL: mustPURL("pkg:generic/metricbeat@version"),
CPEs: singleCPE("cpe:2.3:a:elastic:metricbeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "heartbeat-binary",
FileGlob: "**/heartbeat",
EvidenceMatcher: elasticBeatsMatcher,
Package: "heartbeat",
PURL: mustPURL("pkg:generic/heartbeat@version"),
CPEs: singleCPE("cpe:2.3:a:elastic:heartbeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "packetbeat-binary",
FileGlob: "**/packetbeat",
EvidenceMatcher: elasticBeatsMatcher,
Package: "packetbeat",
PURL: mustPURL("pkg:generic/packetbeat@version"),
CPEs: singleCPE("cpe:2.3:a:elastic:packetbeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "auditbeat-binary",
FileGlob: "**/auditbeat",
EvidenceMatcher: elasticBeatsMatcher,
Package: "auditbeat",
PURL: mustPURL("pkg:generic/auditbeat@version"),
CPEs: singleCPE("cpe:2.3:a:elastic:auditbeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "elastic-agent-binary",
FileGlob: "**/elastic-agent",
EvidenceMatcher: binutils.MatchAny(
// 9.4.x: config/statsenroll: true9.4.2-headeruint16secret
// 9.0.x: configenroll9.0.0-headeruint16secret
// 8.19.x: config/statsenroll8.19.4headeruint16secret
m.FileContentsVersionMatcher(`enroll(?:: true)?(?P<version>[0-9]+\.[0-9]+\.[0-9]+)-?header`),
// 8.11.x: 3:04PM8.11.2:https
m.FileContentsVersionMatcher(`PM(?P<version>[0-9]+\.[0-9]+\.[0-9]+):https`),
),
Package: "elastic-agent",
PURL: mustPURL("pkg:generic/elastic-agent@version"),
CPEs: singleCPE("cpe:2.3:a:elastic:elastic_agent:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
}
return append(classifiers, defaultJavaClassifiers()...)
@@ -12,10 +12,11 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/internal/pe"
)
// NewPEPackageCataloger returns a cataloger that interprets packages from DLL and EXE files.
// NewPEPackageCataloger returns a cataloger that interprets packages from DLL, EXE, and BPL files.
// BPL (Borland Package Library) files are PE-format binaries used by Delphi and C++Builder.
func NewPEPackageCataloger() pkg.Cataloger {
return generic.NewCataloger("pe-binary-package-cataloger").
WithParserByGlobs(parsePE, "**/*.dll", "**/*.exe")
WithParserByGlobs(parsePE, "**/*.dll", "**/*.exe", "**/*.bpl")
}
func parsePE(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
@@ -62,3 +62,30 @@ func Test_PEPackageCataloger(t *testing.T) {
}
}
func Test_PEPackageCataloger_Globs(t *testing.T) {
tests := []struct {
name string
fixture string
expected []string
}{
{
name: "obtain PE binary files (dll, exe, bpl)",
fixture: "testdata/glob-paths",
expected: []string{
"src/library.dll",
"src/program.exe",
"src/archive.bpl",
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
pkgtest.NewCatalogTester().
FromDirectory(t, test.fixture).
ExpectsResolverContentQueries(test.expected).
TestCataloger(t, NewPEPackageCataloger())
})
}
}
@@ -8,4 +8,5 @@ classifiers/bin
# allow for go-hint files and binaries
!VERSION*
!classifiers/snippets/**/bin/
!*.exe
!*.exe
!*.dll
@@ -0,0 +1,8 @@
name: auditbeat
offset: 106492476
length: 100
snippetSha256: 7cfca991c6d073690c48c9489a6929009b866e78d266ecb6f6f2b6f04d9f592f
fileSha256: 81ac1f3ab45bb7b3211cf26f94660ae73fdd257bb4890bdf8e71ecddb5afce10
### byte snippet to follow ###
rulesforcestdinsetupTest 9.4.2%s %windexeventint16int32int64uint8arraysliceInts:Ptrs:sse41sse42ssse3
@@ -0,0 +1,9 @@
name: elastic-agent
offset: 29510673
length: 100
snippetSha256: abd2e0c395a14431231a89bc9fb4d8cbab5843e3b92b44b4b9c2c942bfedb386
fileSha256: ff6758786d60089bda6113e302257ae227ab3f15924a3b3856a6a40a7ac5ca88
### byte snippet to follow ###
15.01.58.33906253:04PM8.11.2:https<-chan</a>.
<node>AElig;ALLOCSAacuteAcceptAcirc;AgraveAlpha;Amacr;
@@ -0,0 +1,8 @@
name: elastic-agent
offset: 345016607
length: 100
snippetSha256: f66476e828071ae2211f9b279d18a4b23c247bd52cb2bc86cfbe611ec48c88e4
fileSha256: c7d7bcf1e43ede7b8c5e55537b382ec02b4d8d1493eedb072bfd5f180442ee43
### byte snippet to follow ###
atspliceconfig/statsenroll8.19.4headeruint16secret%s: %sinputseventsfollownumbersourcereexecstatusou
@@ -0,0 +1,8 @@
name: elastic-agent
offset: 248594563
length: 100
snippetSha256: f1a35077f386583fec63cfbffe42c8eb1a1b59056e5e3a1b15c50afcce0e4d38
fileSha256: e87819df6fc5a387b42436bd304f4c7d778c1d66c8dbbf4f0aa801e8aa89b596
### byte snippet to follow ###
idopenatspliceconfigenroll9.0.0-headeruint16secret%s: %sinputseventsfollownumbersourcereexecstatusou
@@ -0,0 +1,8 @@
name: elastic-agent
offset: 66470886
length: 100
snippetSha256: 7cbc4bee88dbcb215e1335dacc614041aa63bca2c5cc4ce05c795f421ce9b143
fileSha256: 71b8b1552629c1c845516580054eb4d8a1eb1afc1f6fefbcee1922c932fac6a6
### byte snippet to follow ###
idspliceconfig/statsenroll: true9.4.2-headeruint16secret%s: %seventsfollownumberreexecstatusoutputso
@@ -0,0 +1,8 @@
name: filebeat
offset: 99362887
length: 100
snippetSha256: e81df9ab08f801188a6912a2c3651233049a50474063deba3181be22d50e4c14
fileSha256: e29c151f3eae6d2aa6c57ce6810a1ca657fd29cd335d85c83d3b76fddaf0d7ed
### byte snippet to follow ###
.5.4.82.5.4.92006-015m.rate8.11.2-9765625: type ::ffff::method:scheme:status<<nil>><UNSET><array><cl
@@ -0,0 +1,8 @@
name: filebeat
offset: 140231767
length: 100
snippetSha256: a35df402d58dd12b8b993d52f991fabbe8f842d5f1ed22706ead99b2682d0bbc
fileSha256: 4d9fbee3eb1c1610f3692e2e5519e6e84e09a5606a49e169897b849061106a28
### byte snippet to follow ###
leexportconfigcreateplugin8.18.4globalclient%v{%v}aws-s3rdtscppopcntuint16uint32uint64structcmd/gone
@@ -0,0 +1,8 @@
name: filebeat
offset: 156731953
length: 100
snippetSha256: e41d8a6af2f8614feab3bc1875622c4e1a817198d2db64230af14c82f5655550
fileSha256: 52919ee04ec37e4ee86dbe5f6bd216f0d95e9dab037c230a582a921c4a677dcc
### byte snippet to follow ###
*.ymlforcestdinsetupTest 9.4.2%s %w8.0.0sse41sse42ssse3int16int32int64uint8arraysliceInts:Ptrs:value
@@ -0,0 +1,8 @@
name: heartbeat
offset: 97948236
length: 100
snippetSha256: 88c46b68ddc495a24baee84a04b70ad23285725a5d214a216d28904db7bb24ec
fileSha256: ecf50b047a5461a736164566a3d19b6fbe9f1f9f4cbfb086f0c168c93e94b108
### byte snippet to follow ###
8.0.0forcestdinsetupTest 9.4.2inputerroraddedblockmutexdebugFetchFOUNDINPUTERRORDEBUGTRACEStoreindex
@@ -0,0 +1,8 @@
name: metricbeat
offset: 174021883
length: 100
snippetSha256: 44802bbc7e534b43aeed3c4f166744836a11560bee4b9bd0282de1827d7c191e
fileSha256: 1761a0ec65aa7324bd3fdd69904e943ea4861d20b2f1e3c09f7389f9ba5ed859
### byte snippet to follow ###
COBRAforcestdinsetupTest 9.4.2%s %w*.ymlsse41sse42ssse3int16int32int64uint8arraysliceInts:Ptrs:order

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