Compare commits

...

105 Commits

Author SHA1 Message Date
anchore-actions-token-generator[bot]
365325376a
chore(deps): update tools to latest versions (#4370)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-11-15 06:47:23 -05:00
Alex Goodman
153f2321ce
Fix test-fixture publish (#4369)
* pin python dependencies

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

* pin rust dependencies

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

* pin php deps

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

* update and pin http and curl fixtures

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-11-14 15:41:23 -05:00
Alex Goodman
7bf7bcc461
Support extras statements in Python PDM cataloger (#4352)
* fix pdm

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

* update json schema

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

* fix tests

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

* add test for metadata construction

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

* add missing test fixture

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

* conserve markers

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

* update json schema

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: Alex Goodman <wagoodman@users.noreply.github.com>
2025-11-14 15:13:10 -05:00
anchore-actions-token-generator[bot]
6a21b5e5e2
chore(deps): update tools to latest versions (#4365)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-11-14 09:25:27 -05:00
dependabot[bot]
6480c8a425
chore(deps): bump github/codeql-action from 4.31.2 to 4.31.3 (#4366)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.2 to 4.31.3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](0499de31b9...014f16e7ab)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-14 09:25:08 -05:00
Kudryavcev Nikolay
89842bd2f6
chore: migrate syft to use mholt/archives instead of anchore fork (#4029)
---------
Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
Signed-off-by: Christopher Phillips <spiffcs@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-11-13 23:04:43 +00:00
Christopher Angelo Phillips
4a60c41f38
feat: 4184 gguf parser (ai artifact cataloger) part 1 (#4279)
---------
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-11-13 17:43:48 -05:00
anchore-actions-token-generator[bot]
2e100f33f3
chore(deps): update tools to latest versions (#4358)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-11-12 13:27:47 -05:00
dependabot[bot]
b444f0c2ed
chore(deps): bump golang.org/x/mod from 0.29.0 to 0.30.0 (#4359)
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.29.0 to 0.30.0.
- [Commits](https://github.com/golang/mod/compare/v0.29.0...v0.30.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-12 13:27:33 -05:00
Adam Chovanec
102d362daf
feat: CPEs format decoder (#4207)
Signed-off-by: Adam Chovanec <git@adamchovanec.cz>
2025-11-12 10:45:09 -05:00
Alex Goodman
66c78d44af
Document additional json schema fields (#4356)
* add documentation to key fields

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

* regenerate json schema

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-11-10 16:29:06 -05:00
dependabot[bot]
78a4ab8ced
chore(deps): bump github.com/olekukonko/tablewriter from 1.0.9 to 1.1.1 (#4354)
Bumps [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) from 1.0.9 to 1.1.1.
- [Commits](https://github.com/olekukonko/tablewriter/compare/v1.0.9...v1.1.1)

---
updated-dependencies:
- dependency-name: github.com/olekukonko/tablewriter
  dependency-version: 1.1.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>
2025-11-10 13:31:15 -05:00
dependabot[bot]
25ca33d20e
chore(deps): bump github.com/jedib0t/go-pretty/v6 from 6.7.0 to 6.7.1 (#4355)
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.7.0 to 6.7.1.
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.7.0...v6.7.1)

---
updated-dependencies:
- dependency-name: github.com/jedib0t/go-pretty/v6
  dependency-version: 6.7.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-10 13:30:56 -05:00
anchore-actions-token-generator[bot]
60ca241593
chore(deps): update tools to latest versions (#4347)
* chore: new tool checks
---------
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-11-07 20:56:44 +00:00
dependabot[bot]
0f475c8bcd
chore(deps): bump github.com/opencontainers/selinux (#4349)
Bumps [github.com/opencontainers/selinux](https://github.com/opencontainers/selinux) from 1.11.0 to 1.13.0.
- [Release notes](https://github.com/opencontainers/selinux/releases)
- [Commits](https://github.com/opencontainers/selinux/compare/v1.11.0...v1.13.0)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/selinux
  dependency-version: 1.13.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-07 15:21:35 -05:00
Alex Goodman
199394934d
preserve --from order (#4350)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-11-07 10:17:10 -05:00
dependabot[bot]
8a22d394ed
chore(deps): bump golang.org/x/time from 0.12.0 to 0.14.0 (#4348)
Bumps [golang.org/x/time](https://github.com/golang/time) from 0.12.0 to 0.14.0.
- [Commits](https://github.com/golang/time/compare/v0.12.0...v0.14.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-07 08:48:20 -05:00
Tim Olshansky
bbef262b8f
feat: Add license enrichment from pypi to python packages (#4295)
* feat: Add license enrichment from pypi to python packages
* Implement license caching and improve test coverage
---------
Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>
2025-11-06 16:05:08 -05:00
Tim Olshansky
4e06a7ab32
feat(javascript): Add dependency parsing (#4304)
* feat: Add dependency parsing to javascript package locks

Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>

* Bump schema version

Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>

* Add support for yarn and pnpm, excl. yarn v1

Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>

* Add support for dependencies for v1 yarn lock files

Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>

* Ensure schema is correctly generated

Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>

* Fix tests

Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>

* PR feedback

Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>

---------

Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>
2025-11-06 16:03:43 -05:00
Alex Goodman
e5711e9b42
Update CPE processing to use NVD API (#4332)
* update NVD CPE dictionary processor to use API

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

* pass linting with exceptions

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-11-06 16:02:26 -05:00
Rez Moss
f69b1db099
feat: detect elixir bin (#4334)
* Elixir detection, fixed #4333
---------
Signed-off-by: Rez Moss <hi@rezmoss.com>
2025-11-06 16:02:02 -05:00
dependabot[bot]
fe1ea443c2
chore(deps): bump github.com/jedib0t/go-pretty/v6 from 6.6.9 to 6.7.0 (#4337)
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.6.9 to 6.7.0.
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.6.9...v6.7.0)

---
updated-dependencies:
- dependency-name: github.com/jedib0t/go-pretty/v6
  dependency-version: 6.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-06 15:47:49 -05:00
dependabot[bot]
bfcbf266df
chore(deps): bump github.com/containerd/containerd from 1.7.28 to 1.7.29 (#4340)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.28 to 1.7.29.
- [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/v1.7.28...v1.7.29)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-06 15:46:32 -05:00
Keith Zantow
a400c675fc
feat: license file search (#4327)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2025-11-03 14:16:05 -05:00
Alex Goodman
7c154e7c37
use official action for token generation (#4331)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-11-03 13:08:42 -05:00
anchore-actions-token-generator[bot]
4c93394bc2
chore(deps): update anchore dependencies (#4330)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-11-03 12:44:07 -05:00
kdt523
3e4e82f03e
Canonicalize Ghostscript CPE/PURL for ghostscript packages from PE Binaries (#4308)
* binary(pe): canonicalize Ghostscript CPE to artifex:ghostscript and add generic purl for PE (#4275)\n\n- Detect Ghostscript via PE version resources and set purl pkg:generic/ghostscript@<version>\n- Add PE-specific CPE candidates: vendor 'artifex', product 'ghostscript'\n- Add focused unit tests for purl and CPE generation

Signed-off-by: kdt523 <krushna.datir231@vit.edu>

* fix: gofmt formatting for static analysis pass (pe-ghostscript-cpe-purl-4275)

Signed-off-by: kdt523 <krushna.datir231@vit.edu>

---------

Signed-off-by: kdt523 <krushna.datir231@vit.edu>
2025-11-03 14:54:48 +00:00
dependabot[bot]
793b0a346f
chore(deps): bump github/codeql-action from 4.31.1 to 4.31.2 (#4325)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.1 to 4.31.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](5fe9434cd2...0499de31b9)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-03 09:11:20 -05:00
dependabot[bot]
a0dac519db
chore(deps): bump github.com/hashicorp/go-getter from 1.8.2 to 1.8.3 (#4326)
Bumps [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) from 1.8.2 to 1.8.3.
- [Release notes](https://github.com/hashicorp/go-getter/releases)
- [Changelog](https://github.com/hashicorp/go-getter/blob/main/.goreleaser.yml)
- [Commits](https://github.com/hashicorp/go-getter/compare/v1.8.2...v1.8.3)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-getter
  dependency-version: 1.8.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-03 09:11:12 -05:00
dependabot[bot]
34f5e521c1
chore(deps): bump modernc.org/sqlite from 1.39.1 to 1.40.0 (#4329)
Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.39.1 to 1.40.0.
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.39.1...v1.40.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-03 09:11:05 -05:00
dependabot[bot]
774b1e97b9
chore(deps): bump github/codeql-action from 4.31.0 to 4.31.1 (#4321)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.31.0 to 4.31.1.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](4e94bd11f7...5fe9434cd2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-30 13:19:57 -04:00
Alex Goodman
538430d65d
describe cataloger capabilities via test observations (#4318)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-10-30 13:19:42 -04:00
Alex Goodman
5db3a9bf55
add workflow to create PR for spdx license list updates (#4319)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-10-30 12:14:13 -04:00
Stepan
efc2f0012c
fix: go binary replace handling in path (#4156)
* Fix issue with relative paths on go binary

Signed-off-by: Stepan <stepworm@yandex.ru>

* Linting

Signed-off-by: Stepan <stepworm@yandex.ru>

---------

Signed-off-by: Stepan <stepworm@yandex.ru>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-10-29 15:59:47 +00:00
kyounghoonJang
c5c1454848
feat(java): Add support for .far (Feature Archive) files (#4193)
* feat(java): add support for .far archivesEnables the Java cataloger to recognize and catalog dependencies within .far files, which are used in Apache Sling applications.

Signed-off-by: Kyounghoon Jang <matkimchi_@naver.com>

* feat(java): Add tests for .far (Feature Archive) file support

Signed-off-by: Kyounghoon Jang <matkimchi_@naver.com>

---------

Signed-off-by: Kyounghoon Jang <matkimchi_@naver.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-10-29 15:41:27 +00:00
Kudryavcev Nikolay
f5c765192c
Refactor fileresolver to not require base path (#4298)
* ref: close source in test and examples

Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>

* ref: pretty file/directory source resolver (make them more similar)

Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>

* ref: move absoluteSymlinkFreePathToParent to file resolver

Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>

* revert breaking change

Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>

---------

Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
2025-10-29 10:41:18 -04:00
Will Murphy
728feea620
ci: use apple creds before pushing tags (#4313)
We have had a few releases fail because the Apple credentials needed
some sort of fix. These release were operationally more interesting
because they failed after pushing a git tag (which effectively releases
the golagn package). Therefore, try to use these creds early, before
there's a tag pushed.

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2025-10-29 10:07:47 -04:00
dependabot[bot]
45fb52dca1
chore(deps): bump github.com/jedib0t/go-pretty/v6 from 6.6.8 to 6.6.9 (#4315)
Bumps [github.com/jedib0t/go-pretty/v6](https://github.com/jedib0t/go-pretty) from 6.6.8 to 6.6.9.
- [Release notes](https://github.com/jedib0t/go-pretty/releases)
- [Commits](https://github.com/jedib0t/go-pretty/compare/v6.6.8...v6.6.9)

---
updated-dependencies:
- dependency-name: github.com/jedib0t/go-pretty/v6
  dependency-version: 6.6.9
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-29 10:06:37 -04:00
Rez Moss
45bf8b14ab
fix: omit records with empty PURL in GitHub format (#4312)
Signed-off-by: Rez Moss <hi@rezmoss.com>
2025-10-28 18:34:10 -04:00
Brian Muenzenmeyer
9478cd974b
docs: update template link in README.md (#4306)
Signed-off-by: Brian Muenzenmeyer <brian.muenzenmeyer@gmail.com>
2025-10-28 11:29:07 -04:00
Will Murphy
0d9ea69a66
Respect "rpmmod" PURL qualifier (#4314)
Red Hat purls the RPM modularity info in a query param in the PURLs in
their vulnerability data. It would be nice if Syft respected this
qualifier so that Grype can use it when a Red Hat purl is passed.

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2025-10-28 09:35:11 -04:00
dependabot[bot]
bee78c0b16
chore(deps): bump github/codeql-action from 4.30.9 to 4.31.0 (#4310)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.30.9 to 4.31.0.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](16140ae1a1...4e94bd11f7)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-27 10:43:04 -04:00
dependabot[bot]
88bbcbe9c6
chore(deps): bump anchore/sbom-action from 0.20.8 to 0.20.9 (#4305) 2025-10-27 02:03:09 -04:00
anchore-actions-token-generator[bot]
e0680eb704
chore(deps): update tools to latest versions (#4307) 2025-10-27 02:02:47 -04:00
Marc
16f851c5d9
feat: include .rar files as Java archives for Java resource adapters (#4137)
Signed-off-by: Marc Thomas <marc.thomas@t-systems.com>
2025-10-24 11:55:02 -04:00
Ross Kirk
d5ca1ad543
fix: ignore dpkg entries with "deinstall" status (#4231)
Signed-off-by: Ross Kirk <ross.kirk@upwind.io>
2025-10-23 16:23:58 -04:00
anchore-actions-token-generator[bot]
8be463911c
chore(deps): update tools to latest versions (#4302)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-10-22 09:38:18 -04:00
dependabot[bot]
44b7b0947c
chore(deps): bump github.com/github/go-spdx/v2 from 2.3.3 to 2.3.4 (#4301) 2025-10-21 09:34:26 -04:00
dependabot[bot]
675075e882
chore(deps): bump github/codeql-action from 4.30.8 to 4.30.9 (#4299)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 4.30.8 to 4.30.9.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](f443b600d9...16140ae1a1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-20 10:08:39 -04:00
JoeyShapiro
31b2c4c090
support universal (fat) mach-o binary files (#4278)
Signed-off-by: Joseph Shapiro <joeyashapiro@gmail.com>
2025-10-17 13:41:59 -04:00
dependabot[bot]
07029ead8a
chore(deps): bump sigstore/cosign-installer from 3.10.0 to 4.0.0 (#4296)
Bumps [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer) from 3.10.0 to 4.0.0.
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](d7543c93d8...faadad0cce)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-17 10:22:20 -04:00
dependabot[bot]
f4de1e863c
chore(deps): bump anchore/sbom-action from 0.20.7 to 0.20.8 (#4297)
Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.20.7 to 0.20.8.
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](d8a2c01300...aa0e114b2e)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-17 10:22:10 -04:00
JoeyShapiro
538b4a2194
convert posix path back to windows (#4285)
Signed-off-by: Joseph Shapiro <joeyashapiro@gmail.com>
2025-10-17 09:29:06 -04:00
Kudryavcev Nikolay
fc74b07369
Remove duplicate image source providers (#4289)
Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
2025-10-16 16:19:11 -04:00
dependabot[bot]
6627c5214c
chore(deps): bump anchore/sbom-action from 0.20.6 to 0.20.7 (#4293)
Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.20.6 to 0.20.7.
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](f8bdd1d8ac...d8a2c01300)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-16 13:57:17 -04:00
Tim Olshansky
c0f32e1dba
feat: add option to fetch remote licenses for pnpm-lock.yaml files (#4286)
Signed-off-by: Tim Olshansky <456103+timols@users.noreply.github.com>
2025-10-16 12:23:06 -04:00
Pavel Buchart
e923db2a94
Add PDM parser (#4234)
Signed-off-by: Pavel Buchart <pavel@buchart.cz>
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Co-authored-by: Keith Zantow <kzantow@gmail.com>
2025-10-16 08:50:44 -04:00
anchore-actions-token-generator[bot]
0c98a364d5
chore(deps): update tools to latest versions (#4291)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-10-16 07:02:32 -04:00
Keith Zantow
4343d04652
fix: panic during java archive maven resolution (#4290)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2025-10-16 07:00:31 -04:00
Kudryavcev Nikolay
065ac13ab7
Extract zip archive with multiple entries (#4283)
* extract zip archive with multiple entries

Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>

* set OverwriteExisting by type assertion switch case

Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>

---------

Signed-off-by: Kudryavcev Nikolay <kydry.nikolau@gmail.com>
2025-10-15 12:05:05 -04:00
Christopher Angelo Phillips
e9a8bc5ab9
chore: update to use old configuration on new cosign (#4287)
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-10-15 15:12:20 +00:00
anchore-actions-token-generator[bot]
6d790ec6ec
chore(deps): update anchore dependencies (#4282)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-10-14 22:09:17 +00:00
dependabot[bot]
1d5bcc553a
chore(deps): bump github.com/mholt/archives from 0.1.3 to 0.1.5 (#4280)
* chore(deps): bump github.com/mholt/archives from 0.1.3 to 0.1.5

Bumps [github.com/mholt/archives](https://github.com/mholt/archives) from 0.1.3 to 0.1.5.
- [Release notes](https://github.com/mholt/archives/releases)
- [Commits](https://github.com/mholt/archives/compare/v0.1.3...v0.1.5)

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

Signed-off-by: dependabot[bot] <support@github.com>

* chore: allow lzip-go in bouncer yaml

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2025-10-14 14:22:00 -04:00
Alex Goodman
d22914baf5
add docs to configs (#4281)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-10-14 13:58:31 -04:00
Doug Clarke
760bd9a50a
feat: Pom xml only archive parser (#4272)
fix: identifying jar files with a single pom.xml and no pom.properties file
fix: test works with pom.xml being found, used and reported in metadata
Signed-off-by: Doug Clarke <douglas.clarke@oracle.com>

test: check for current project path and use
Signed-off-by: Christopher Phillips <spiffcs@users.noreply.github.com>
---------
Signed-off-by: Doug Clarke <douglas.clarke@oracle.com>
Signed-off-by: Christopher Phillips <spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <spiffcs@users.noreply.github.com>
2025-10-13 15:59:08 -04:00
Hala Ali
2d1ada1d00
fix: enhance setup.py parser to handle unquoted dependencies (#4255)
* fix: add support for unquoted Python dependencies in setup.py

- Add regex pattern to match unquoted package==version format
- Handles common .split() pattern used in projects like mayan-edms
- Maintains backward compatibility with quoted dependencies
- Prevents duplicate package detection
Signed-off-by: Hala Ali alih16@vcu.edu

Signed-off-by: HalaAli198 <alih16@vcu.edu>

* fix: apply gofmt formatting

Signed-off-by: HalaAli198 <alih16@vcu.edu>

* lint: incorporate new changes and refactor complexity

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

---------

Signed-off-by: HalaAli198 <alih16@vcu.edu>
Signed-off-by: Christopher Phillips <spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <spiffcs@users.noreply.github.com>
2025-10-13 15:10:42 -04:00
dependabot[bot]
8ffe15c710
chore(deps): bump golang.org/x/tools from 0.37.0 to 0.38.0 (#4265)
Bumps [golang.org/x/tools](https://github.com/golang/tools) from 0.37.0 to 0.38.0.
- [Release notes](https://github.com/golang/tools/releases)
- [Commits](https://github.com/golang/tools/compare/v0.37.0...v0.38.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-13 11:50:59 -04:00
dependabot[bot]
89948dfa51
chore(deps): bump golang.org/x/mod from 0.28.0 to 0.29.0 (#4266)
Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.28.0 to 0.29.0.
- [Commits](https://github.com/golang/mod/compare/v0.28.0...v0.29.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-13 11:50:49 -04:00
anchore-actions-token-generator[bot]
1a58f27f87
chore(deps): update tools to latest versions (#4274)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-10-13 11:50:41 -04:00
dependabot[bot]
450cd72da5
chore(deps): bump modernc.org/sqlite from 1.39.0 to 1.39.1 (#4276)
Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.39.0 to 1.39.1.
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.39.0...v1.39.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-13 11:50:25 -04:00
dependabot[bot]
5056c7f861
chore(deps): bump github/codeql-action from 4.30.7 to 4.30.8 (#4277) 2025-10-13 10:47:50 -04:00
Alex Goodman
4ae8f73583
migrate json schema generation (#4270)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-10-10 14:16:28 +00:00
dependabot[bot]
18e789c4fd
chore(deps): bump github.com/gohugoio/hashstructure from 0.5.0 to 0.6.0 (#4267)
Bumps [github.com/gohugoio/hashstructure](https://github.com/gohugoio/hashstructure) from 0.5.0 to 0.6.0.
- [Release notes](https://github.com/gohugoio/hashstructure/releases)
- [Commits](https://github.com/gohugoio/hashstructure/compare/v0.5.0...v0.6.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-09 15:10:47 -04:00
dependabot[bot]
7d4680bc08
chore(deps): bump golang.org/x/net from 0.45.0 to 0.46.0 (#4268)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.45.0 to 0.46.0.
- [Commits](https://github.com/golang/net/compare/v0.45.0...v0.46.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  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>
2025-10-09 15:10:36 -04:00
Bernardo de Araujo
231f04ae0e
feat: Parse pnpm v9 lockfiles (#4256)
Signed-off-by: bernardoamc <bernardo.amc@gmail.com>
2025-10-09 15:07:59 -04:00
dependabot[bot]
3b82a3724a
chore(deps): bump github/codeql-action from 3.30.6 to 4.30.7 (#4262)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.6 to 4.30.7.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](64d10c1313...e296a93559)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-08 16:44:21 -04:00
dependabot[bot]
337a2754e5
chore(deps): bump golang.org/x/net from 0.44.0 to 0.45.0 (#4263)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.44.0 to 0.45.0.
- [Commits](https://github.com/golang/net/compare/v0.44.0...v0.45.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-08 16:44:13 -04:00
anchore-actions-token-generator[bot]
190f3068d8
chore(deps): update tools to latest versions (#4261)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-10-08 16:44:05 -04:00
Sebastien Dionne
bd013fe99a
docs: Fix typos and linguistic errors in documentation (#4257)
Signed-off-by: Sebastien Dionne <survivant00@gmail.com>
2025-10-06 14:22:22 +00:00
Parthib Mukherjee
c732052cf1
feat(cpegenerate): add support for binary package digit-suffix variations in CPE generation (#4093)
* feat(cpegenerate): add support for binary package digit-suffix variations in CPE generation

Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump github.com/gkampitakis/go-snaps from 0.5.13 to 0.5.14 (#4089)

Bumps [github.com/gkampitakis/go-snaps](https://github.com/gkampitakis/go-snaps) from 0.5.13 to 0.5.14.
- [Release notes](https://github.com/gkampitakis/go-snaps/releases)
- [Commits](https://github.com/gkampitakis/go-snaps/compare/v0.5.13...v0.5.14)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump modernc.org/sqlite from 1.38.1 to 1.38.2 (#4088)

Bumps [modernc.org/sqlite](https://gitlab.com/cznic/sqlite) from 1.38.1 to 1.38.2.
- [Commits](https://gitlab.com/cznic/sqlite/compare/v1.38.1...v1.38.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump github.com/docker/docker (#4092)

Bumps [github.com/docker/docker](https://github.com/docker/docker) from 28.2.2+incompatible to 28.3.3+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v28.2.2...v28.3.3)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-version: 28.3.3+incompatible
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump github.com/anchore/stereoscope (#4091)

Bumps [github.com/anchore/stereoscope](https://github.com/anchore/stereoscope) from 0.1.7-0.20250716200927-94c6f92877d4 to 0.1.7.
- [Release notes](https://github.com/anchore/stereoscope/releases)
- [Changelog](https://github.com/anchore/stereoscope/blob/main/RELEASE.md)
- [Commits](https://github.com/anchore/stereoscope/commits/v0.1.7)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* migrate to get.anchore.io (#4095)

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

* chore(deps): update anchore dependencies (#4098)

* chore(deps): update anchore dependencies

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

* address reader close operations

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

---------

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): update anchore dependencies (#4104)

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump github/codeql-action from 3.29.4 to 3.29.5 (#4096)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.4 to 3.29.5.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](4e828ff8d4...51f77329af)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): update tools to latest versions (#4108)

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): update CPE dictionary index (#4112)

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): update tools to latest versions (#4111)

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump actions/cache in /.github/actions/bootstrap (#4120)

Bumps [actions/cache](https://github.com/actions/cache) from 4.2.3 to 4.2.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](5a3ec84eff...0400d5f644)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump actions/cache from 4.2.3 to 4.2.4 (#4119)

Bumps [actions/cache](https://github.com/actions/cache) from 4.2.3 to 4.2.4.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](5a3ec84eff...0400d5f644)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump docker/login-action from 3.4.0 to 3.5.0 (#4115)

Bumps [docker/login-action](https://github.com/docker/login-action) from 3.4.0 to 3.5.0.
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](74a5d14239...184bdaa072)

---
updated-dependencies:
- dependency-name: docker/login-action
  dependency-version: 3.5.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>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* fix: nondeterministic Java archive cataloging and improve groupID (#4118)

Signed-off-by: Keith Zantow <kzantow@gmail.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* feat: add binary classifier for hashicorp vault (#4121)

* add binary classifier for hashicorp vault

The Go Binary Cataloger isn't able to parse the version out of the
binary shipped in the DockerHub images of hashicorp/vault because the
version of the main module isn't set in the binary. Therefore, add a
binary classifier cataloger for this binary.

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

* chore: add test fixtures, update vault

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* chore: set binary classifier package type based on PURL

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* chore: use github.com/hashicorp/vault as package name

Signed-off-by: Keith Zantow <kzantow@gmail.com>

* chore: update tests

Signed-off-by: Keith Zantow <kzantow@gmail.com>

---------

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Co-authored-by: Keith Zantow <kzantow@gmail.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump github/codeql-action from 3.29.7 to 3.29.8 (#4124)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.7 to 3.29.8.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](51f77329af...76621b61de)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump golang.org/x/mod from 0.26.0 to 0.27.0 (#4123)

Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.26.0 to 0.27.0.
- [Commits](https://github.com/golang/mod/compare/v0.26.0...v0.27.0)

---
updated-dependencies:
- dependency-name: golang.org/x/mod
  dependency-version: 0.27.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>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump golang.org/x/net from 0.42.0 to 0.43.0 (#4122)

Bumps [golang.org/x/net](https://github.com/golang/net) from 0.42.0 to 0.43.0.
- [Commits](https://github.com/golang/net/compare/v0.42.0...v0.43.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.43.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>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): update CPE dictionary index (#4126)

Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore: update GoReleaser configurations (#4128)

Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump actions/checkout from 4.2.2 to 5.0.0 (#4130)

Bumps [actions/checkout](https://github.com/actions/checkout) from 4.2.2 to 5.0.0.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](11bd71901b...08c6903cd8)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* fix: closed reader during java binary detection (#4129)

Signed-off-by: Keith Zantow <kzantow@gmail.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* fix: support multiple letters in openssl patch version (#4106)

Signed-off-by: honigbot <thesoftbear@gmail.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* chore(deps): bump github/codeql-action from 3.29.8 to 3.29.9 (#4134)

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.8 to 3.29.9.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](76621b61de...df559355d5)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* feat: update syft license construction to be able to look up by URL (#4132)

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

* feat: add package supplier flag (#4131)

---------

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

* chore(deps): bump zizmorcore/zizmor-action from 0.1.1 to 0.1.2 (#4135)

Bumps [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action) from 0.1.1 to 0.1.2.
- [Release notes](https://github.com/zizmorcore/zizmor-action/releases)
- [Commits](f52a838cfa...5ca5fc7a47)

---
updated-dependencies:
- dependency-name: zizmorcore/zizmor-action
  dependency-version: 0.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* feat: add support for authors, maintainers, and contributors in package.json.  (#4003)

Fixes #2250
---------
Signed-off-by: Alan Pope <alan.pope@anchore.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* feat(cpegentereate): added test for the addBinaryPackageDigitVariation function

Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* docs(cpegenerate): made the comment more verbose

Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>

* nit: separate digit variation concerns from case of use

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

---------

Signed-off-by: Parthib Mukherjee <parthibmukherjee@gmail.com>
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Signed-off-by: Keith Zantow <kzantow@gmail.com>
Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Signed-off-by: honigbot <thesoftbear@gmail.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Signed-off-by: Alan Pope <alan.pope@anchore.com>
Signed-off-by: Parthib Mukherjee <109328510+hawkaii@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: anchore-actions-token-generator[bot] <102182147+anchore-actions-token-generator[bot]@users.noreply.github.com>
Co-authored-by: wagoodman <590471+wagoodman@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Keith Zantow <kzantow@gmail.com>
Co-authored-by: Will Murphy <willmurphyscode@users.noreply.github.com>
Co-authored-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: honigbot <34426443+honigbot@users.noreply.github.com>
Co-authored-by: Alan Pope <alan.pope@anchore.com>
2025-10-06 10:09:38 -04:00
dependabot[bot]
8f1d45830d
chore(deps): bump github.com/iancoleman/orderedmap (#4258)
Bumps [github.com/iancoleman/orderedmap](https://github.com/iancoleman/orderedmap) from 0.0.0-20190318233801-ac98e3ecb4b0 to 0.3.0.
- [Commits](https://github.com/iancoleman/orderedmap/commits/v0.3.0)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 10:06:28 -04:00
dependabot[bot]
ea7dc8f468
chore(deps): bump github.com/go-git/go-git/v5 from 5.16.2 to 5.16.3 (#4259)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.16.2 to 5.16.3.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.16.2...v5.16.3)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-version: 5.16.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-06 10:06:17 -04:00
anchore-actions-token-generator[bot]
ff6a8b1802
chore(deps): update tools to latest versions (#4248)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-10-03 14:53:27 -04:00
Alex Goodman
a77d24e379
Improve struct and field comments and incorporate into json schema (#4252)
* improve struct and field comments and incorporate into json schema

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

* address review feedback

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2025-10-03 17:01:56 +00:00
dependabot[bot]
b96d3d20af
chore(deps): bump github/codeql-action from 3.30.5 to 3.30.6 (#4253)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.5 to 3.30.6.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](3599b3baa1...64d10c1313)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 12:07:20 -04:00
dependabot[bot]
5461a92337
chore(deps): bump github.com/hashicorp/go-getter from 1.8.1 to 1.8.2 (#4254)
Bumps [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) from 1.8.1 to 1.8.2.
- [Release notes](https://github.com/hashicorp/go-getter/releases)
- [Changelog](https://github.com/hashicorp/go-getter/blob/main/.goreleaser.yml)
- [Commits](https://github.com/hashicorp/go-getter/compare/v1.8.1...v1.8.2)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-getter
  dependency-version: 1.8.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-03 12:07:13 -04:00
dependabot[bot]
b9604cbf30
chore(deps): bump github.com/CycloneDX/cyclonedx-go from 0.9.2 to 0.9.3 (#4251) 2025-10-02 13:24:25 +00:00
Keith Zantow
9217f2099f
chore: update ffmpeg tests (#4249)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2025-10-01 13:11:36 +00:00
dependabot[bot]
605a275dd3
chore(deps): bump github/codeql-action from 3.30.4 to 3.30.5 (#4246) 2025-09-30 17:06:10 -04:00
Alan Pope
e1483e0285
Add support for identifying ffmpeg/libav libraries (#4227)
* Add support for identifying ffmpeg/libav libraries

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

* Undo my snippet-based confusion

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

* Put test fixture config back

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

---------

Signed-off-by: Alan Pope <alan.pope@anchore.com>
2025-09-26 10:43:47 -04:00
Alan Pope
0a36dabf23
feat(cataloger): add snap package cataloger for metadata extraction (#4151)
---------
Signed-off-by: Alan Pope <alan.pope@anchore.com>
Signed-off-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
Co-authored-by: Christopher Phillips <32073428+spiffcs@users.noreply.github.com>
2025-09-26 10:42:29 -04:00
dependabot[bot]
64b71ec04c
chore(deps): bump github.com/quasilyte/go-ruleguard/dsl (#4245)
Bumps [github.com/quasilyte/go-ruleguard/dsl](https://github.com/quasilyte/go-ruleguard) from 0.3.22 to 0.3.23.
- [Release notes](https://github.com/quasilyte/go-ruleguard/releases)
- [Commits](https://github.com/quasilyte/go-ruleguard/compare/dsl/v0.3.22...dsl/v0.3.23)

---
updated-dependencies:
- dependency-name: github.com/quasilyte/go-ruleguard/dsl
  dependency-version: 0.3.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-26 10:27:13 -04:00
anchore-actions-token-generator[bot]
8629080e80
chore(deps): update tools to latest versions (#4238)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-09-25 12:08:37 -04:00
dependabot[bot]
f0998de717
chore(deps): bump github/codeql-action from 3.30.3 to 3.30.4 (#4239)
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.30.3 to 3.30.4.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](192325c861...303c0aef88)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-25 12:06:49 -04:00
dependabot[bot]
261ab7c1fd
chore(deps): bump actions/cache from 4.2.4 to 4.3.0 (#4240)
Bumps [actions/cache](https://github.com/actions/cache) from 4.2.4 to 4.3.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](0400d5f644...0057852bfa)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-25 12:02:41 -04:00
dependabot[bot]
8232f5bd1b
chore(deps): bump actions/cache in /.github/actions/bootstrap (#4241)
Bumps [actions/cache](https://github.com/actions/cache) from 4.2.4 to 4.3.0.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](0400d5f644...0057852bfa)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-25 12:02:30 -04:00
Saleem Abdulrasool
21d50d7c31
feat: add ARM64 Windows build target (#4237)
Signed-off-by: Saleem Abdulrasool <compnerd@compnerd.org>
2025-09-24 15:29:03 -04:00
anchore-actions-token-generator[bot]
c28b90717b
chore(deps): update tools to latest versions (#4236)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-09-24 15:07:58 -04:00
Keith Zantow
323fd3e34c
docs: add GitHub actions to supported ecosystems (#4235)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2025-09-23 10:08:41 -04:00
anchore-actions-token-generator[bot]
af4d19f81d
chore(deps): update tools to latest versions (#4230)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-09-22 11:08:30 -04:00
dependabot[bot]
d820c3436b
chore(deps): bump github.com/charmbracelet/bubbletea (#4228)
Bumps [github.com/charmbracelet/bubbletea](https://github.com/charmbracelet/bubbletea) from 1.3.9 to 1.3.10.
- [Release notes](https://github.com/charmbracelet/bubbletea/releases)
- [Changelog](https://github.com/charmbracelet/bubbletea/blob/main/.goreleaser.yml)
- [Commits](https://github.com/charmbracelet/bubbletea/compare/v1.3.9...v1.3.10)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-18 15:15:52 -04:00
dependabot[bot]
409642c8f0
chore(deps): bump github.com/hashicorp/go-getter from 1.8.0 to 1.8.1 (#4229)
Bumps [github.com/hashicorp/go-getter](https://github.com/hashicorp/go-getter) from 1.8.0 to 1.8.1.
- [Release notes](https://github.com/hashicorp/go-getter/releases)
- [Changelog](https://github.com/hashicorp/go-getter/blob/main/.goreleaser.yml)
- [Commits](https://github.com/hashicorp/go-getter/compare/v1.8.0...v1.8.1)

---
updated-dependencies:
- dependency-name: github.com/hashicorp/go-getter
  dependency-version: 1.8.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-18 15:15:43 -04:00
dependabot[bot]
3abbd940e3
chore(deps): bump anchore/sbom-action from 0.20.5 to 0.20.6 (#4222)
Bumps [anchore/sbom-action](https://github.com/anchore/sbom-action) from 0.20.5 to 0.20.6.
- [Release notes](https://github.com/anchore/sbom-action/releases)
- [Changelog](https://github.com/anchore/sbom-action/blob/main/RELEASE.md)
- [Commits](da167eac91...f8bdd1d8ac)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-18 10:58:53 -04:00
anchore-actions-token-generator[bot]
22f6f8f880
chore(deps): update tools to latest versions (#4221)
Signed-off-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: spiffcs <32073428+spiffcs@users.noreply.github.com>
2025-09-18 07:16:16 -04:00
dependabot[bot]
6005fb3c20
chore(deps): bump github.com/gkampitakis/go-snaps from 0.5.14 to 0.5.15 (#4225)
Bumps [github.com/gkampitakis/go-snaps](https://github.com/gkampitakis/go-snaps) from 0.5.14 to 0.5.15.
- [Release notes](https://github.com/gkampitakis/go-snaps/releases)
- [Commits](https://github.com/gkampitakis/go-snaps/compare/v0.5.14...v0.5.15)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-17 10:07:37 -04:00
357 changed files with 37612 additions and 2931 deletions

View File

@ -2,7 +2,7 @@ tools:
# we want to use a pinned version of binny to manage the toolchain (so binny manages itself!)
- name: binny
version:
want: v0.9.0
want: v0.10.0
method: github-release
with:
repo: anchore/binny
@ -26,7 +26,7 @@ tools:
# used for linting
- name: golangci-lint
version:
want: v2.4.0
want: v2.6.2
method: github-release
with:
repo: golangci/golangci-lint
@ -42,7 +42,7 @@ tools:
# used for signing the checksums file at release
- name: cosign
version:
want: v2.6.0
want: v3.0.2
method: github-release
with:
repo: sigstore/cosign
@ -58,7 +58,7 @@ tools:
# used to release all artifacts
- name: goreleaser
version:
want: v2.12.0
want: v2.12.7
method: github-release
with:
repo: goreleaser/goreleaser
@ -90,7 +90,7 @@ tools:
# used for running all local and CI tasks
- name: task
version:
want: v3.44.1
want: v3.45.5
method: github-release
with:
repo: go-task/task
@ -98,7 +98,7 @@ tools:
# used for triggering a release
- name: gh
version:
want: v2.79.0
want: v2.83.1
method: github-release
with:
repo: cli/cli
@ -114,7 +114,7 @@ tools:
# used to upload test fixture cache
- name: yq
version:
want: v4.47.2
want: v4.48.2
method: github-release
with:
repo: mikefarah/yq

View File

@ -9,6 +9,9 @@ permit:
- Unlicense
ignore-packages:
# https://github.com/sorairolake/lzip-go/blob/34a2615d2abf740175c6b0a835baa08364e09430/go.sum.license#L3
# has `SPDX-License-Identifier: Apache-2.0 OR MIT`, both of which are acceptable
- github.com/sorairolake/lzip-go
# packageurl-go is released under the MIT license located in the root of the repo at /mit.LICENSE
- github.com/anchore/packageurl-go

View File

@ -37,7 +37,7 @@ runs:
- name: Restore tool cache
if: inputs.tools == 'true'
id: tool-cache
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ github.workspace }}/.tool
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
@ -63,7 +63,7 @@ runs:
- name: Restore ORAS cache from github actions
if: inputs.download-test-fixture-cache == 'true'
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ github.workspace }}/.tmp/oras-cache
key: ${{ inputs.cache-key-prefix }}-oras-cache

View File

@ -47,7 +47,7 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 #v3.29.5
uses: github/codeql-action/init@014f16e7ab1402f30e7c3329d33797e7948572db #v3.29.5
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@ -58,7 +58,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@192325c86100d080feab897ff886c34abd4c83a3 #v3.29.5
uses: github/codeql-action/autobuild@014f16e7ab1402f30e7c3329d33797e7948572db #v3.29.5
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -72,4 +72,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 #v3.29.5
uses: github/codeql-action/analyze@014f16e7ab1402f30e7c3329d33797e7948572db #v3.29.5

View File

@ -19,6 +19,16 @@ jobs:
with:
persist-credentials: false
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
- name: Validate Apple notarization credentials
run: .tool/quill submission list
env:
QUILL_NOTARY_ISSUER: ${{ secrets.APPLE_NOTARY_ISSUER }}
QUILL_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }}
QUILL_NOTARY_KEY: ${{ secrets.APPLE_NOTARY_KEY }}
- name: Check if running on main
if: github.ref != 'refs/heads/main'
# we are using the following flag when running `cosign blob-verify` for checksum signature verification:
@ -161,7 +171,7 @@ jobs:
# for updating brew formula in anchore/homebrew-syft
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
- uses: anchore/sbom-action@da167eac915b4e86f08b264dbdbc867b61be6f0c #v0.20.5
- uses: anchore/sbom-action@8e94d75ddd33f69f691467e42275782e4bfefe84 #v0.20.9
continue-on-error: true
with:
file: go.mod

View File

@ -31,11 +31,11 @@ jobs:
with:
repos: ${{ github.event.inputs.repos }}
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 #v2.1.4
id: generate-token
with:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
app-id: ${{ secrets.TOKEN_APP_ID }}
private-key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e #v7.0.8
with:

View File

@ -45,11 +45,11 @@ jobs:
echo "\`\`\`"
} >> $GITHUB_STEP_SUMMARY
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 #v2.1.4
id: generate-token
with:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
app-id: ${{ secrets.TOKEN_APP_ID }}
private-key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e #v7.0.8
with:

View File

@ -14,6 +14,9 @@ env:
jobs:
upgrade-cpe-dictionary-index:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
@ -22,18 +25,31 @@ jobs:
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
id: bootstrap
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
- name: Login to GitHub Container Registry
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | ${{ steps.bootstrap.outputs.oras }} login ghcr.io -u ${{ github.actor }} --password-stdin
- run: |
make generate-cpe-dictionary-index
- name: Pull CPE cache from registry
run: make generate:cpe-index:cache:pull
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
- name: Update CPE cache from NVD API
run: make generate:cpe-index:cache:update
env:
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
- name: Generate CPE dictionary index
run: make generate:cpe-index:build
- name: Push updated CPE cache to registry
run: make generate:cpe-index:cache:push
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 #v2.1.4
id: generate-token
with:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
app-id: ${{ secrets.TOKEN_APP_ID }}
private-key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e #v7.0.8
with:

View File

@ -0,0 +1,54 @@
name: PR to update SPDX license list
on:
schedule:
- cron: "0 6 * * 1" # every monday at 6 AM UTC
workflow_dispatch:
permissions:
contents: read
env:
SLACK_NOTIFICATIONS: true
jobs:
upgrade-spdx-license-list:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
with:
persist-credentials: false
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
- run: |
make generate-license-list
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
id: generate-token
with:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e #v7.0.8
with:
signoff: true
delete-branch: true
branch: auto/latest-spdx-license-list
labels: dependencies
commit-message: "chore(deps): update SPDX license list"
title: "chore(deps): update SPDX license list"
body: |
Update SPDX license list based on the latest available list from spdx.org
token: ${{ steps.generate-token.outputs.token }}
- uses: 8398a7/action-slack@77eaa4f1c608a7d68b38af4e3f739dcd8cba273e #v3.19.0
with:
status: ${{ job.status }}
fields: workflow,eventName,job
text: Syft SPDX license list update failed
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
if: ${{ failure() && env.SLACK_NOTIFICATIONS == 'true' }}

View File

@ -90,7 +90,7 @@ jobs:
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
# see https://github.com/actions/upload-artifact/issues/199 for more info
- name: Upload snapshot artifacts
uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
with:
# we need to preserve the snapshot data itself as well as the task data that confirms if the
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
@ -118,7 +118,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
with:
path: |
snapshot
@ -175,7 +175,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
with:
path: |
snapshot
@ -210,7 +210,7 @@ jobs:
runs-on: macos-latest
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
uses: sigstore/cosign-installer@faadad0cce49287aee09b3a48701e75088a2c6ad # v4.0.0
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
with:
@ -225,7 +225,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
with:
path: |
snapshot
@ -262,7 +262,7 @@ jobs:
- name: Download snapshot build
id: snapshot-cache
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
with:
path: |
snapshot

5
.gitignore vendored
View File

@ -2,6 +2,7 @@
go.work
go.work.sum
.tool-versions
.python-version
# app configuration
/.syft.yaml
@ -16,6 +17,8 @@ bin/
/snapshot
/.tool
/.task
/generate
/specs
# changelog generation
CHANGELOG.md
@ -70,3 +73,5 @@ cosign.pub
__pycache__/
*.py[cod]
*$py.class

View File

@ -42,7 +42,7 @@ builds:
dir: ./cmd/syft
binary: syft
goos: [windows]
goarch: [amd64]
goarch: [amd64, arm64]
mod_timestamp: *build-timestamp
ldflags: *build-ldflags
@ -337,6 +337,7 @@ signs:
certificate: "${artifact}.pem"
args:
- "sign-blob"
- "--use-signing-config=false"
- "--oidc-issuer=https://token.actions.githubusercontent.com"
- "--output-certificate=${certificate}"
- "--output-signature=${signature}"

View File

@ -27,7 +27,7 @@ Also double check that the docker context being used is the default context. If
`docker context use default`
After cloning the following step can help you get setup:
After cloning, the following steps can help you get setup:
1. run `make bootstrap` to download go mod dependencies, create the `/.tmp` dir, and download helper utilities.
2. run `make` to view the selection of developer commands in the Makefile
3. run `make build` to build the release snapshot binaries and packages
@ -120,7 +120,7 @@ sequenceDiagram
source-->>+sbom: add source to SBOM struct
source-->>+catalog: pass src to generate catalog
catalog-->-sbom: add cataloging results onto SBOM
sbom-->>encoder: pass SBOM and format desiered to syft encoder
sbom-->>encoder: pass SBOM and format desired to syft encoder
encoder-->>source: return bytes that are the SBOM of the original input
Note right of catalog: cataloger configuration is done based on src
@ -186,7 +186,7 @@ Cataloger names should be unique and named with the following rules of thumb in
- Use lowercase letters, numbers, and hyphens only
- Use hyphens to separate words
- Catalogers for language ecosystems should start with the language name (e.g. `python-` for a cataloger that raises up python packages)
- Distinct between when the cataloger is searching for evidence of installed packages vs declared packages. For example, there are currently two different gemspec-based catalogers, the `ruby-gemspec-cataloger` and `ruby-installed-gemspec-cataloger`, where the latter requires that the gemspec is found within a `specifications` directory (which means it was installed, not just at the root of a source repo).
- Distinguish between when the cataloger is searching for evidence of installed packages vs declared packages. For example, there are currently two different gemspec-based catalogers, the `ruby-gemspec-cataloger` and `ruby-installed-gemspec-cataloger`, where the latter requires that the gemspec is found within a `specifications` directory (which means it was installed, not just at the root of a source repo).
#### Building a new Cataloger

View File

@ -106,8 +106,8 @@ syft <image> -o <format>
Where the `formats` available are:
- `syft-json`: Use this to get as much information out of Syft as possible!
- `syft-text`: A row-oriented, human-and-machine-friendly output.
- `cyclonedx-xml`: A XML report conforming to the [CycloneDX 1.6 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-xml@1.5`: A XML report conforming to the [CycloneDX 1.5 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-xml`: An XML report conforming to the [CycloneDX 1.6 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-xml@1.5`: An XML report conforming to the [CycloneDX 1.5 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-json`: A JSON report conforming to the [CycloneDX 1.6 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-json@1.5`: A JSON report conforming to the [CycloneDX 1.5 specification](https://cyclonedx.org/specification/overview/).
- `spdx-tag-value`: A tag-value formatted report conforming to the [SPDX 2.3 specification](https://spdx.github.io/spdx-spec/v2.3/).
@ -116,7 +116,7 @@ Where the `formats` available are:
- `spdx-json@2.2`: A JSON report conforming to the [SPDX 2.2 JSON Schema](https://github.com/spdx/spdx-spec/blob/v2.2/schemas/spdx-schema.json).
- `github-json`: A JSON report conforming to GitHub's dependency snapshot format.
- `syft-table`: A columnar summary (default).
- `template`: Lets the user specify the output format. See ["Using templates"](#using-templates) below.
- `template`: Lets the user specify the output format. See ["Using templates"](https://github.com/anchore/syft/wiki/using-templates) below.
Note that flags using the @<version> can be used for earlier versions of each specification as well.
@ -133,8 +133,9 @@ Note that flags using the @<version> can be used for earlier versions of each sp
- Elixir (mix)
- Erlang (rebar3)
- Go (go.mod, Go binaries)
- GitHub (workflows, actions)
- Haskell (cabal, stack)
- Java (jar, ear, war, par, sar, nar, native-image)
- Java (jar, ear, war, par, sar, nar, rar, native-image)
- JavaScript (npm, yarn)
- Jenkins Plugins (jpi, hpi)
- Linux kernel archives (vmlinz)

View File

@ -29,4 +29,4 @@ To report a security issue, please email
with a description of the issue, the steps you took to create the issue,
affected versions, and, if known, mitigations for the issue.
All support will be made on the best effort base, so please indicate the "urgency level" of the vulnerability as Critical, High, Medium or Low.
All support will be made on a best effort basis, so please indicate the "urgency level" of the vulnerability as Critical, High, Medium or Low.

View File

@ -1,5 +1,9 @@
version: "3"
includes:
generate:cpe-index: ./task.d/generate/cpe-index.yaml
vars:
OWNER: anchore
PROJECT: syft
@ -502,7 +506,7 @@ tasks:
generate-json-schema:
desc: Generate a new JSON schema
cmds:
- "cd syft/internal && go generate . && cd jsonschema && go run . && go fmt ../..."
- "cd ./internal && go generate . && cd ./jsonschema && go run . && go fmt ../..."
generate-license-list:
desc: Generate an updated license processing code off of the latest available SPDX license list
@ -511,10 +515,11 @@ tasks:
- "gofmt -s -w ./internal/spdxlicense"
generate-cpe-dictionary-index:
desc: Generate the CPE index based off of the latest available CPE dictionary
dir: "syft/pkg/cataloger/internal/cpegenerate/dictionary"
desc: Generate the CPE index from local cache
cmds:
- "go generate"
- task: generate:cpe-index:cache:pull
- task: generate:cpe-index:cache:update
- task: generate:cpe-index:build
## Build-related targets #################################

View File

@ -253,7 +253,6 @@ func generateSBOMForAttestation(ctx context.Context, id clio.Identification, opt
}
src, err := getSource(ctx, opts, userInput, stereoscope.RegistryTag)
if err != nil {
return nil, err
}

View File

@ -87,8 +87,8 @@ func runCatalogerList(opts *catalogerListOptions) error {
}
func catalogerListReport(opts *catalogerListOptions, allTaskGroups [][]task.Task) (string, error) {
defaultCatalogers := options.Flatten(opts.DefaultCatalogers)
selectCatalogers := options.Flatten(opts.SelectCatalogers)
defaultCatalogers := options.FlattenAndSort(opts.DefaultCatalogers)
selectCatalogers := options.FlattenAndSort(opts.SelectCatalogers)
selectedTaskGroups, selectionEvidence, err := task.SelectInGroups(
allTaskGroups,
cataloging.NewSelectionRequest().

View File

@ -185,7 +185,6 @@ func runScan(ctx context.Context, id clio.Identification, opts *scanOptions, use
}
src, err := getSource(ctx, &opts.Catalog, userInput, sources...)
if err != nil {
return err
}

View File

@ -198,9 +198,10 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
},
Nix: nix.DefaultConfig().
WithCaptureOwnedFiles(cfg.Nix.CaptureOwnedFiles),
Python: python.CatalogerConfig{
GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
},
Python: python.DefaultCatalogerConfig().
WithSearchRemoteLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Python), cfg.Python.SearchRemoteLicenses)).
WithPypiBaseURL(cfg.Python.PypiBaseURL).
WithGuessUnpinnedRequirements(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Python), cfg.Python.GuessUnpinnedRequirements)),
JavaArchive: java.DefaultArchiveCatalogerConfig().
WithUseMavenLocalRepository(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Java, task.Maven), cfg.Java.UseMavenLocalRepository)).
WithMavenLocalRepositoryDir(cfg.Java.MavenLocalRepositoryDir).
@ -283,10 +284,10 @@ func (cfg *Catalog) PostLoad() error {
cfg.From = Flatten(cfg.From)
cfg.Catalogers = Flatten(cfg.Catalogers)
cfg.DefaultCatalogers = Flatten(cfg.DefaultCatalogers)
cfg.SelectCatalogers = Flatten(cfg.SelectCatalogers)
cfg.Enrich = Flatten(cfg.Enrich)
cfg.Catalogers = FlattenAndSort(cfg.Catalogers)
cfg.DefaultCatalogers = FlattenAndSort(cfg.DefaultCatalogers)
cfg.SelectCatalogers = FlattenAndSort(cfg.SelectCatalogers)
cfg.Enrich = FlattenAndSort(cfg.Enrich)
// for backwards compatibility
cfg.DefaultCatalogers = append(cfg.DefaultCatalogers, cfg.Catalogers...)
@ -311,6 +312,11 @@ func Flatten(commaSeparatedEntries []string) []string {
out = append(out, strings.TrimSpace(s))
}
}
return out
}
func FlattenAndSort(commaSeparatedEntries []string) []string {
out := Flatten(commaSeparatedEntries)
sort.Strings(out)
return out
}
@ -320,6 +326,7 @@ var publicisedEnrichmentOptions = []string{
task.Golang,
task.Java,
task.JavaScript,
task.Python,
}
func enrichmentEnabled(enrichDirectives []string, features ...string) *bool {

View File

@ -79,6 +79,98 @@ func TestCatalog_PostLoad(t *testing.T) {
}
}
func TestFlatten(t *testing.T) {
tests := []struct {
name string
input []string
expected []string
}{
{
name: "preserves order of comma-separated values",
input: []string{"registry,docker,oci-dir"},
expected: []string{"registry", "docker", "oci-dir"},
},
{
name: "preserves order across multiple entries",
input: []string{"registry,docker", "oci-dir"},
expected: []string{"registry", "docker", "oci-dir"},
},
{
name: "trims whitespace",
input: []string{" registry , docker ", " oci-dir "},
expected: []string{"registry", "docker", "oci-dir"},
},
{
name: "handles single value",
input: []string{"registry"},
expected: []string{"registry"},
},
{
name: "handles empty input",
input: []string{},
expected: nil,
},
{
name: "preserves reverse alphabetical order",
input: []string{"zebra,yankee,xray"},
expected: []string{"zebra", "yankee", "xray"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := Flatten(tt.input)
assert.Equal(t, tt.expected, got)
})
}
}
func TestFlattenAndSort(t *testing.T) {
tests := []struct {
name string
input []string
expected []string
}{
{
name: "sorts comma-separated values",
input: []string{"registry,docker,oci-dir"},
expected: []string{"docker", "oci-dir", "registry"},
},
{
name: "sorts across multiple entries",
input: []string{"registry,docker", "oci-dir"},
expected: []string{"docker", "oci-dir", "registry"},
},
{
name: "trims whitespace and sorts",
input: []string{" registry , docker ", " oci-dir "},
expected: []string{"docker", "oci-dir", "registry"},
},
{
name: "handles single value",
input: []string{"registry"},
expected: []string{"registry"},
},
{
name: "handles empty input",
input: []string{},
expected: nil,
},
{
name: "sorts reverse alphabetical order",
input: []string{"zebra,yankee,xray"},
expected: []string{"xray", "yankee", "zebra"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := FlattenAndSort(tt.input)
assert.Equal(t, tt.expected, got)
})
}
}
func Test_enrichmentEnabled(t *testing.T) {
tests := []struct {
directives string
@ -139,7 +231,7 @@ func Test_enrichmentEnabled(t *testing.T) {
for _, test := range tests {
t.Run(test.directives, func(t *testing.T) {
got := enrichmentEnabled(Flatten([]string{test.directives}), test.test)
got := enrichmentEnabled(FlattenAndSort([]string{test.directives}), test.test)
assert.Equal(t, test.expected, got)
})
}

View File

@ -3,7 +3,9 @@ package options
import "github.com/anchore/clio"
type pythonConfig struct {
GuessUnpinnedRequirements bool `json:"guess-unpinned-requirements" yaml:"guess-unpinned-requirements" mapstructure:"guess-unpinned-requirements"`
SearchRemoteLicenses *bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
PypiBaseURL string `json:"pypi-base-url" yaml:"pypi-base-url" mapstructure:"pypi-base-url"`
GuessUnpinnedRequirements *bool `json:"guess-unpinned-requirements" yaml:"guess-unpinned-requirements" mapstructure:"guess-unpinned-requirements"`
}
var _ interface {
@ -11,6 +13,8 @@ var _ interface {
} = (*pythonConfig)(nil)
func (o *pythonConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.SearchRemoteLicenses, `enables Syft to use the network to fill in more detailed license information`)
descriptions.Add(&o.PypiBaseURL, `base Pypi url to use`)
descriptions.Add(&o.GuessUnpinnedRequirements, `when running across entries in requirements.txt that do not specify a specific version
(e.g. "sqlalchemy >= 1.0.0, <= 2.0.0, != 3.0.0, <= 3.0.0"), attempt to guess what the version could
be based on the version requirements specified (e.g. "1.0.0"). When enabled the lowest expressible version

View File

@ -25,7 +25,6 @@ func BenchmarkImagePackageCatalogers(b *testing.B) {
// get the source object for the image
theSource, err := syft.GetSource(context.Background(), tarPath, syft.DefaultGetSourceConfig().WithSources("docker-archive"))
require.NoError(b, err)
b.Cleanup(func() {
require.NoError(b, theSource.Close())
})
@ -88,6 +87,7 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Remove(string(pkg.TerraformPkg))
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // we have coverage for pear instead
definedPkgs.Remove(string(pkg.CondaPkg))
definedPkgs.Remove(string(pkg.ModelPkg))
var cases []testCase
cases = append(cases, commonTestCases...)
@ -162,6 +162,7 @@ func TestPkgCoverageDirectory(t *testing.T) {
definedPkgs.Remove(string(pkg.UnknownPkg))
definedPkgs.Remove(string(pkg.CondaPkg))
definedPkgs.Remove(string(pkg.PhpPeclPkg)) // this is covered as pear packages
definedPkgs.Remove(string(pkg.ModelPkg))
// for directory scans we should not expect to see any of the following package types
definedPkgs.Remove(string(pkg.KbPkg))

View File

@ -30,10 +30,10 @@ func TestPackageDeduplication(t *testing.T) {
locationCount: map[string]int{
"basesystem-11-13.el9": 5, // in all layers
"curl-minimal-7.76.1-26.el9_3.2.0.1": 2, // base + wget layer
"curl-minimal-7.76.1-31.el9": 3, // curl upgrade layer + all above layers
"curl-minimal-7.76.1-31.el9_6.1": 3, // curl upgrade layer + all above layers
"wget-1.21.1-8.el9_4": 4, // wget + all above layers
"vsftpd-3.0.5-6.el9": 2, // vsftpd + all above layers
"httpd-2.4.62-4.el9": 1, // last layer
"httpd-2.4.62-4.el9_6.4": 1, // last layer
},
},
{
@ -47,11 +47,11 @@ func TestPackageDeduplication(t *testing.T) {
"httpd": 1, // rpm, binary is now excluded by overlap
},
locationCount: map[string]int{
"basesystem-11-13.el9": 1,
"curl-minimal-7.76.1-31.el9": 1, // upgrade
"wget-1.21.1-8.el9_4": 1,
"vsftpd-3.0.5-6.el9": 1,
"httpd-2.4.62-4.el9": 1,
"basesystem-11-13.el9": 1,
"curl-minimal-7.76.1-31.el9_6.1": 1, // upgrade
"wget-1.21.1-8.el9_4": 1,
"vsftpd-3.0.5-6.el9": 1,
"httpd-2.4.62-4.el9_6.4": 1,
},
},
}

View File

@ -7,16 +7,16 @@ FROM --platform=linux/amd64 rockylinux:9.3.20231119@sha256:d644d203142cd5b54ad2a
# copying the RPM DB from each stage to a final stage in separate layers. This will result in a much smaller image.
FROM base AS stage1
RUN dnf install -y wget
RUN dnf install -y wget-1.21.1-8.el9_4
FROM stage1 AS stage2
RUN dnf update -y curl-minimal
RUN dnf update -y curl-minimal-7.76.1-31.el9_6.1
FROM stage2 AS stage3
RUN dnf install -y vsftpd
RUN dnf install -y vsftpd-3.0.5-6.el9
FROM stage3 AS stage4
RUN dnf install -y httpd
RUN dnf install -y httpd-2.4.62-4.el9_6.4
FROM scratch

View File

@ -38,11 +38,11 @@ func catalogFixtureImageWithConfig(t *testing.T, fixtureImageName string, cfg *s
// get the source to build an SBOM against
theSource, err := syft.GetSource(context.Background(), tarPath, syft.DefaultGetSourceConfig().WithSources("docker-archive"))
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, theSource.Close())
})
// build the SBOM
s, err := syft.CreateSBOM(context.Background(), theSource, cfg)
require.NoError(t, err)
@ -66,7 +66,7 @@ func catalogDirectory(t *testing.T, dir string, catalogerSelection ...string) (s
func catalogDirectoryWithConfig(t *testing.T, dir string, cfg *syft.CreateSBOMConfig) (sbom.SBOM, source.Source) {
cfg.CatalogerSelection = cfg.CatalogerSelection.WithDefaults(pkgcataloging.DirectoryTag)
// get the source to build an sbom against
// get the source to build an SBOM against
theSource, err := syft.GetSource(context.Background(), dir, syft.DefaultGetSourceConfig().WithSources("dir"))
require.NoError(t, err)
t.Cleanup(func() {

View File

@ -23,6 +23,7 @@ const defaultImage = "alpine:3.19"
func main() {
// automagically get a source.Source for arbitrary string input
src := getSource(imageReference())
defer src.Close()
// will catalog the given source and return a SBOM keeping in mind several configurable options
sbom := getSBOM(src)
@ -46,7 +47,6 @@ func getSource(input string) source.Source {
fmt.Println("detecting source type for input:", input, "...")
src, err := syft.GetSource(context.Background(), input, nil)
if err != nil {
panic(err)
}

View File

@ -19,6 +19,7 @@ const defaultImage = "alpine:3.19"
func main() {
// automagically get a source.Source for arbitrary string input
src := getSource(imageReference())
defer src.Close()
// catalog the given source and return a SBOM
sbom := getSBOM(src)
@ -40,7 +41,6 @@ func imageReference() string {
func getSource(input string) source.Source {
src, err := syft.GetSource(context.Background(), input, nil)
if err != nil {
panic(err)
}

View File

@ -19,6 +19,7 @@ const defaultImage = "alpine:3.19"
func main() {
// automagically get a source.Source for arbitrary string input
src := getSource(imageReference())
defer src.Close()
// catalog the given source and return a SBOM
// let's explicitly use catalogers that are:
@ -44,7 +45,6 @@ func imageReference() string {
func getSource(input string) source.Source {
src, err := syft.GetSource(context.Background(), input, nil)
if err != nil {
panic(err)
}

View File

@ -15,6 +15,7 @@ func main() {
image := "alpine:3.19"
src, _ := syft.GetSource(context.Background(), image, syft.DefaultGetSourceConfig().WithSources("registry"))
defer src.Close()
sbom, _ := syft.CreateSBOM(context.Background(), src, syft.DefaultCreateSBOMConfig())

98
go.mod
View File

@ -4,18 +4,17 @@ go 1.24.1
require (
github.com/BurntSushi/toml v1.5.0
github.com/CycloneDX/cyclonedx-go v0.9.2
github.com/CycloneDX/cyclonedx-go v0.9.3
github.com/Masterminds/semver/v3 v3.4.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/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9
github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716
github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537
github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c
github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d
github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
@ -24,14 +23,14 @@ require (
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
github.com/anchore/stereoscope v0.1.10
github.com/anchore/stereoscope v0.1.12
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
github.com/aquasecurity/go-pep440-version v0.0.1
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
github.com/bmatcuk/doublestar/v4 v4.9.1
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.9
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
github.com/dave/jennifer v1.7.1
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
@ -40,35 +39,35 @@ require (
github.com/dustin/go-humanize v1.0.1
github.com/elliotchance/phpserialize v1.4.0
github.com/facebookincubator/nvdtools v0.1.5
github.com/github/go-spdx/v2 v2.3.3
github.com/gkampitakis/go-snaps v0.5.14
github.com/github/go-spdx/v2 v2.3.4
github.com/gkampitakis/go-snaps v0.5.15
github.com/go-git/go-billy/v5 v5.6.2
github.com/go-git/go-git/v5 v5.16.2
github.com/go-git/go-git/v5 v5.16.3
github.com/go-test/deep v1.1.1
github.com/go-viper/mapstructure/v2 v2.4.0
github.com/gohugoio/hashstructure v0.5.0
github.com/gohugoio/hashstructure v0.6.0
github.com/google/go-cmp v0.7.0
github.com/google/go-containerregistry v0.20.6
github.com/google/licensecheck v0.3.1
github.com/google/uuid v1.6.0
github.com/gookit/color v1.6.0
github.com/hashicorp/go-cleanhttp v0.5.2
github.com/hashicorp/go-getter v1.8.0
github.com/hashicorp/go-getter v1.8.3
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.7.0
github.com/jedib0t/go-pretty/v6 v6.6.8
github.com/jedib0t/go-pretty/v6 v6.7.1
github.com/jinzhu/copier v0.4.0
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/magiconair/properties v1.8.10
github.com/mholt/archives v0.1.3
github.com/mholt/archives v0.1.5
github.com/moby/sys/mountinfo v0.7.2
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1
github.com/olekukonko/tablewriter v1.0.9
github.com/olekukonko/tablewriter v1.1.1
github.com/opencontainers/go-digest v1.0.0
github.com/pelletier/go-toml v1.9.5
github.com/quasilyte/go-ruleguard/dsl v0.3.22
github.com/quasilyte/go-ruleguard/dsl v0.3.23
github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
github.com/sanity-io/litter v1.5.8
@ -90,9 +89,9 @@ require (
go.uber.org/goleak v1.3.0
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
golang.org/x/mod v0.28.0
golang.org/x/net v0.44.0
modernc.org/sqlite v1.39.0
golang.org/x/mod v0.30.0
golang.org/x/net v0.46.0
modernc.org/sqlite v1.40.0
)
require (
@ -110,11 +109,11 @@ require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.11.7 // indirect
github.com/ProtonMail/go-crypto v1.3.0 // indirect
github.com/STARRY-S/zip v0.2.1 // indirect
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/agext/levenshtein v1.2.1 // indirect; indirectt
github.com/anchore/go-lzo v0.1.0 // indirect
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // 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
github.com/atotto/clipboard v0.1.4 // indirect
@ -122,7 +121,7 @@ require (
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.6.0 // indirect
github.com/bodgit/sevenzip v1.6.1 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
@ -131,7 +130,7 @@ require (
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/containerd/containerd v1.7.28 // indirect
github.com/containerd/containerd v1.7.29 // indirect
github.com/containerd/containerd/api v1.8.0 // indirect
github.com/containerd/continuity v0.4.4 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
@ -142,11 +141,11 @@ require (
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/typeurl/v2 v2.2.0 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v28.4.0+incompatible // indirect
github.com/docker/cli v28.5.1+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker v28.4.0+incompatible // indirect
github.com/docker/docker v28.5.1+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.3 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
@ -168,17 +167,15 @@ require (
github.com/goccy/go-yaml v1.18.0
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
github.com/google/s2a-go v0.1.8 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-safetemp v1.0.0 // indirect
github.com/hashicorp/go-version v1.6.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
github.com/iancoleman/orderedmap v0.3.0
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
@ -192,10 +189,10 @@ require (
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.16 // indirect
github.com/mattn/go-runewidth v0.0.19 // 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.0 // indirect
github.com/minio/minlz v1.0.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
@ -210,13 +207,9 @@ require (
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect
github.com/opencontainers/selinux v1.11.0 // indirect
github.com/opencontainers/selinux v1.13.0 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
@ -233,7 +226,7 @@ require (
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/sorairolake/lzip-go v0.3.5 // indirect
github.com/sorairolake/lzip-go v0.3.8 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.9 // indirect
@ -265,14 +258,14 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/crypto v0.43.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/term v0.35.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.37.0
golang.org/x/sys v0.37.0 // indirect
golang.org/x/term v0.36.0 // indirect
golang.org/x/text v0.30.0 // indirect
golang.org/x/time v0.14.0
golang.org/x/tools v0.38.0
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
google.golang.org/api v0.203.0 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
@ -281,13 +274,19 @@ require (
google.golang.org/grpc v1.67.3 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.66.3 // indirect
gopkg.in/yaml.v3 v3.0.1
modernc.org/libc v1.66.10 // indirect
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)
require (
github.com/cespare/xxhash/v2 v2.3.0
github.com/gpustack/gguf-parser-go v0.22.1
)
require (
cyphar.com/go-pathrs v0.2.1 // indirect
github.com/aws/aws-sdk-go-v2 v1.36.5 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect
github.com/aws/aws-sdk-go-v2/config v1.29.17 // indirect
@ -306,7 +305,20 @@ require (
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect
github.com/aws/smithy-go v1.22.4 // indirect
github.com/clipperhouse/displaywidth v0.3.1 // indirect
github.com/clipperhouse/stringish v0.1.1 // indirect
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.65 // indirect
github.com/henvic/httpretty v0.1.4 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.1.2 // indirect
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d // indirect
gonum.org/v1/gonum v0.15.1 // indirect
)
retract (

190
go.sum
View File

@ -59,6 +59,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs=
cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0=
cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
@ -73,8 +75,8 @@ github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CycloneDX/cyclonedx-go v0.9.2 h1:688QHn2X/5nRezKe2ueIVCt+NRqf7fl3AVQk+vaFcIo=
github.com/CycloneDX/cyclonedx-go v0.9.2/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg=
github.com/CycloneDX/cyclonedx-go v0.9.3 h1:Pyk/lwavPz7AaZNvugKFkdWOm93MzaIyWmBwmBo3aUI=
github.com/CycloneDX/cyclonedx-go v0.9.3/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg=
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=
@ -94,8 +96,8 @@ 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.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg=
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
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=
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
@ -108,16 +110,14 @@ 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/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 h1:yhk+P8lF3ZiROjmaVRao9WGTRo4b/wYjoKEiAHWrKwc=
github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51/go.mod h1:nwuGSd7aZp0rtYt79YggCGafz1RYsclE7pi3fhLwvuw=
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 h1:p0ZIe0htYOX284Y4axJaGBvXHU0VCCzLN5Wf5XbKStU=
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9/go.mod h1:3ZsFB9tzW3vl4gEiUeuSOMDnwroWxIxJelOOHUp8dSw=
github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716 h1:2sIdYJlQESEnyk3Y0WD2vXWW5eD2iMz9Ev8fj1Z8LNA=
github.com/anchore/clio v0.0.0-20250319180342-2cfe4b0cb716/go.mod h1:Utb9i4kwiCWvqAIxZaJeMIXFO9uOgQXlvH2BfbfO/zI=
github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2 h1:GC2QaO0YsmjpsZ4rtVKv9DnproIxqqn+qkskpc+i8MA=
github.com/anchore/fangs v0.0.0-20250319222917-446a1e748ec2/go.mod h1:XUbUECwVKuD3qYRUj+QZIOHjyyXua2gFmVjKA40iHXA=
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 h1:GjNGuwK5jWjJMyVppBjYS54eOiiSNv4Ba869k4wh72Q=
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c h1:eoJXyC0n7DZ4YvySG/ETdYkTar2Due7eH+UmLK6FbrA=
github.com/anchore/go-collections v0.0.0-20251016125210-a3c352120e8c/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d h1:gT69osH9AsdpOfqxbRwtxcNnSZ1zg4aKy2BevO3ZBdc=
github.com/anchore/go-homedir v0.0.0-20250319154043-c29668562e4d/go.mod h1:PhSnuFYknwPZkOWKB1jXBNToChBA+l0FjwOxtViIc50=
github.com/anchore/go-logger v0.0.0-20250318195838-07ae343dd722 h1:2SqmFgE7h+Ql4VyBzhjLkRF/3gDrcpUBj8LjvvO6OOM=
@ -138,11 +138,11 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
github.com/anchore/stereoscope v0.1.10 h1:BogafIMaW/L1lOUoVS96Hu1jTSP2JktxIayVqcxvcBI=
github.com/anchore/stereoscope v0.1.10/go.mod h1:RWFAkQE8tp8yyaf4V83Kq1bO6hX3bzi8gpLCcKgZLIk=
github.com/anchore/stereoscope v0.1.12 h1:4T/10G7Nb98UoJBKVvAIhsAtrR63lZXxMJb/Qfw5inw=
github.com/anchore/stereoscope v0.1.12/go.mod h1:G3PZlzPbxFhylj9pQwtqfVPaahuWmy/UCtv5FTIIMvg=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ=
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
@ -217,8 +217,8 @@ github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4=
github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8=
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
@ -227,7 +227,6 @@ github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqy
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@ -235,8 +234,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
github.com/charmbracelet/bubbletea v1.3.9 h1:OBYdfRo6QnlIcXNmcoI2n1NNS65Nk6kI2L2FO1puS/4=
github.com/charmbracelet/bubbletea v1.3.9/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
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.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
@ -263,6 +262,12 @@ 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.3.1 h1:k07iN9gD32177o1y4O1jQMzbLdCrsGJh+blirVYybsk=
github.com/clipperhouse/displaywidth v0.3.1/go.mod h1:tgLJKKyaDOCadywag3agw4snxS5kYEuYR6Y9+qWDDYM=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
@ -277,8 +282,8 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
github.com/containerd/containerd v1.7.28 h1:Nsgm1AtcmEh4AHAJ4gGlNSaKgXiNccU270Dnf81FQ3c=
github.com/containerd/containerd v1.7.28/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs=
github.com/containerd/containerd v1.7.29 h1:90fWABQsaN9mJhGkoVnuzEY+o1XDPbg9BTC9QTAHnuE=
github.com/containerd/containerd v1.7.29/go.mod h1:azUkWcOvHrWvaiUjSQH0fjzuHIwSPg1WL5PshGP4Szs=
github.com/containerd/containerd/api v1.8.0 h1:hVTNJKR8fMc/2Tiw60ZRijntNMd1U+JVMyTRdsD2bS0=
github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc=
github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII=
@ -304,8 +309,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@ -322,12 +327,12 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
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 v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY=
github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY=
github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk=
github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
@ -385,14 +390,14 @@ github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.3.3 h1:QI7evnHWEfWkT54eJwkoV/f3a0xD3gLlnVmT5wQG6LE=
github.com/github/go-spdx/v2 v2.3.3/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
github.com/github/go-spdx/v2 v2.3.4 h1:6VNAsYWvQge+SOeoubTlH81MY21d5uekXNIRGfXMNXo=
github.com/github/go-spdx/v2 v2.3.4/go.mod h1:7LYNCshU2Gj17qZ0heJ5CQUKWWmpd98K7o93K8fJSMk=
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-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.14 h1:3fAqdB6BCPKHDMHAKRwtPUwYexKtGrNuw8HX/T/4neo=
github.com/gkampitakis/go-snaps v0.5.14/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
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=
@ -403,8 +408,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8=
github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@ -434,8 +439,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg=
github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec=
github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg=
github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -472,8 +477,6 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -541,6 +544,8 @@ github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj
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.22.1 h1:FRnEDWqT0Rcplr/R9ctCRSN2+3DhVsf6dnR5/i9JA4E=
github.com/gpustack/gguf-parser-go v0.22.1/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
@ -556,8 +561,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-getter v1.8.0 h1:GMRdoMBDz12Mim366pWsRVIrrkugJ19rrmykkv0Nhzo=
github.com/hashicorp/go-getter v1.8.0/go.mod h1:/K0O5zR6R72O3r2x3z2UHadiC0XHMbbzHO9pS8ZeJPA=
github.com/hashicorp/go-getter v1.8.3 h1:gIS+oTNv3kyYAvlUVgMR46MiG0bM0KuSON/KZEvRoRg=
github.com/hashicorp/go-getter v1.8.3/go.mod h1:CUTt9x2bCtJ/sV8ihgrITL3IUE+0BE1j/e4n5P/GIM4=
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
@ -569,8 +574,6 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
@ -592,10 +595,13 @@ github.com/hashicorp/memberlist v0.2.2/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOn
github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE=
github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk=
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
github.com/henvic/httpretty v0.1.4 h1:Jo7uwIRWVFxkqOnErcoYfH90o3ddQyVrSANeS4cxYmU=
github.com/henvic/httpretty v0.1.4/go.mod h1:Dn60sQTZfbt2dYsdUSNsCljyF4AfdqnuJFDLJA1I4AM=
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk=
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
@ -610,14 +616,15 @@ github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy77
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jedib0t/go-pretty/v6 v6.6.8 h1:JnnzQeRz2bACBobIaa/r+nqjvws4yEhcmaZ4n1QzsEc=
github.com/jedib0t/go-pretty/v6 v6.6.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
github.com/jedib0t/go-pretty/v6 v6.7.1 h1:bHDSsj93NuJ563hHuM7ohk/wpX7BmRFNIsVv1ssI2/M=
github.com/jedib0t/go-pretty/v6 v6.7.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=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
@ -677,20 +684,20 @@ 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.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
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/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mholt/archives v0.1.3 h1:aEAaOtNra78G+TvV5ohmXrJOAzf++dIlYeDW3N9q458=
github.com/mholt/archives v0.1.3/go.mod h1:LUCGp++/IbV/I0Xq4SzcIR6uwgeh2yjnQWamjRQfLTU=
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
@ -723,9 +730,11 @@ github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcY
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
@ -742,16 +751,16 @@ github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 h1:kpt9ZfKcm+
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1/go.mod h1:qgCw4bBKZX8qMgGeEZzGFVT3notl42dBjNqO2jut0M0=
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE=
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=
github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
github.com/olekukonko/tablewriter v1.0.9 h1:XGwRsYLC2bY7bNd93Dk51bcPZksWZmLYuaTHR0FqfL8=
github.com/olekukonko/tablewriter v1.0.9/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
github.com/olekukonko/ll v0.1.2 h1:lkg/k/9mlsy0SxO5aC+WEpbdT5K83ddnNhAepz7TQc0=
github.com/olekukonko/ll v0.1.2/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
github.com/olekukonko/tablewriter v1.1.1 h1:b3reP6GCfrHwmKkYwNRFh2rxidGHcT6cgxj/sHiDDx0=
github.com/olekukonko/tablewriter v1.1.1/go.mod h1:De/bIcTF+gpBDB3Alv3fEsZA+9unTsSzAg/ZGADCtn4=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@ -760,8 +769,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
github.com/opencontainers/selinux v1.13.0 h1:Zza88GWezyT7RLql12URvoxsbLfjFx988+LGaWfbL84=
github.com/opencontainers/selinux v1.13.0/go.mod h1:XxWTed+A/s5NNq4GmYScVy+9jzXhGBVEOAyucdRUY8s=
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=
@ -806,11 +815,10 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY=
github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@ -852,8 +860,10 @@ github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yf
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg=
github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d h1:3VwvTjiRPA7cqtgOWddEL+JrcijMlXUmj99c/6YyZoY=
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d/go.mod h1:tAG61zBM1DYRaGIPloumExGvScf08oHuo0kFoOqdbT0=
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
@ -1021,8 +1031,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.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
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=
@ -1062,8 +1072,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.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
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=
@ -1108,8 +1118,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.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
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=
@ -1218,13 +1228,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.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
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.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
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=
@ -1235,13 +1245,13 @@ 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.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
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=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@ -1297,14 +1307,16 @@ 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.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gonum.org/v1/gonum v0.15.1 h1:FNy7N6OUZVUaWG9pTiD+jlhdQ3lMP+/LcTpJ6+a8sQ0=
gonum.org/v1/gonum v0.15.1/go.mod h1:eZTZuRFrzu5pcyjN5wJhcIhnUdNijYxX1T2IcrOGY0o=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
@ -1490,18 +1502,18 @@ 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.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/cc/v4 v4.26.5 h1:xM3bX7Mve6G8K8b+T11ReenJOT+BmVqQj0FY5T4+5Y4=
modernc.org/cc/v4 v4.26.5/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A=
modernc.org/ccgo/v4 v4.28.1/go.mod h1:uD+4RnfrVgE6ec9NGguUNdhqzNIeeomeXf6CL0GTE5Q=
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
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=
@ -1510,8 +1522,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
modernc.org/sqlite v1.39.0 h1:6bwu9Ooim0yVYA7IZn9demiQk/Ejp0BtTjBWFLymSeY=
modernc.org/sqlite v1.39.0/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
modernc.org/sqlite v1.40.0 h1:bNWEDlYhNPAUdUdBzjAvn8icAs/2gaKlj4vM+tQ6KdQ=
modernc.org/sqlite v1.40.0/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
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=

View File

@ -18,7 +18,7 @@ based on the _structure_ of the provided type.
If the structure changes in any way it will end up with a new version key and all will re populate this new key,
ignoring cached values from older, different versions.
The resolver will store items using the `json` package to serialize/deserialize values, so to save space
it is encouraged to use `omitempty`. For example:
it is encouraged to use `omitempty`. For example:
```go
type myCacheItem struct {

View File

@ -0,0 +1,46 @@
package pkgtestobservation
import "time"
// Observations represents capability observations during testing
type Observations struct {
License bool `json:"license"`
Relationships Relationship `json:"relationships"`
FileListing Count `json:"file_listing"`
FileDigests Count `json:"file_digests"`
IntegrityHash Count `json:"integrity_hash"`
}
// Relationship tracks dependency relationship observations
type Relationship struct {
Found bool `json:"found"`
Count int `json:"count"`
}
// Count tracks whether a capability was found and how many times
type Count struct {
Found bool `json:"found"`
Count int `json:"count"`
}
// Test is the root structure for test-observations.json
type Test struct {
Package string `json:"package"`
UpdatedAt time.Time `json:"updated_at"`
Catalogers map[string]*Cataloger `json:"catalogers"`
Parsers map[string]*Parser `json:"parsers"`
}
// Parser captures all observations for a parser
type Parser struct {
MetadataTypes []string `json:"metadata_types"`
PackageTypes []string `json:"package_types"`
Observations Observations `json:"observations"`
}
// Cataloger captures all observations for a cataloger
type Cataloger struct {
MetadataTypes []string `json:"metadata_types"`
PackageTypes []string `json:"package_types"`
Observations Observations `json:"observations"`
}

View File

@ -3,5 +3,9 @@ package internal
const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "16.0.39"
JSONSchemaVersion = "16.1.0"
// Changelog
// 16.1.0 - reformulated the python pdm fields (added "URL" and removed the unused "path" field).
)

View File

@ -1,17 +1,40 @@
package file
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/bmatcuk/doublestar/v4"
"github.com/mholt/archives"
"github.com/anchore/archiver/v3"
"github.com/anchore/syft/internal"
)
// TraverseFilesInTar enumerates all paths stored within a tar archive using the visitor pattern.
func TraverseFilesInTar(ctx context.Context, archivePath string, visitor archives.FileHandler) error {
tarReader, err := os.Open(archivePath)
if err != nil {
return fmt.Errorf("unable to open tar archive (%s): %w", archivePath, err)
}
defer internal.CloseAndLogError(tarReader, archivePath)
format, _, err := archives.Identify(ctx, archivePath, nil)
if err != nil {
return fmt.Errorf("failed to identify tar compression format: %w", err)
}
extractor, ok := format.(archives.Extractor)
if !ok {
return fmt.Errorf("file format does not support extraction: %s", archivePath)
}
return extractor.Extract(ctx, tarReader, visitor)
}
// ExtractGlobsFromTarToUniqueTempFile extracts paths matching the given globs within the given archive to a temporary directory, returning file openers for each file extracted.
func ExtractGlobsFromTarToUniqueTempFile(archivePath, dir string, globs ...string) (map[string]Opener, error) {
func ExtractGlobsFromTarToUniqueTempFile(ctx context.Context, archivePath, dir string, globs ...string) (map[string]Opener, error) {
results := make(map[string]Opener)
// don't allow for full traversal, only select traversal from given paths
@ -19,9 +42,7 @@ func ExtractGlobsFromTarToUniqueTempFile(archivePath, dir string, globs ...strin
return results, nil
}
visitor := func(file archiver.File) error {
defer file.Close()
visitor := func(_ context.Context, file archives.FileInfo) error {
// ignore directories
if file.IsDir() {
return nil
@ -43,7 +64,13 @@ func ExtractGlobsFromTarToUniqueTempFile(archivePath, dir string, globs ...strin
// provides a ReadCloser. It is up to the caller to handle closing the file explicitly.
defer tempFile.Close()
if err := safeCopy(tempFile, file.ReadCloser); err != nil {
packedFile, err := file.Open()
if err != nil {
return fmt.Errorf("unable to read file=%q from tar=%q: %w", file.NameInArchive, archivePath, err)
}
defer internal.CloseAndLogError(packedFile, archivePath)
if err := safeCopy(tempFile, packedFile); err != nil {
return fmt.Errorf("unable to copy source=%q for tar=%q: %w", file.Name(), archivePath, err)
}
@ -52,7 +79,7 @@ func ExtractGlobsFromTarToUniqueTempFile(archivePath, dir string, globs ...strin
return nil
}
return results, archiver.Walk(archivePath, visitor)
return results, TraverseFilesInTar(ctx, archivePath, visitor)
}
func matchesAnyGlob(name string, globs ...string) bool {

View File

@ -1,10 +1,12 @@
package file
import (
"context"
"os"
"sort"
"strings"
"github.com/mholt/archives"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/internal/log"
@ -14,22 +16,25 @@ import (
type ZipFileManifest map[string]os.FileInfo
// NewZipFileManifest creates and returns a new ZipFileManifest populated with path and metadata from the given zip archive path.
func NewZipFileManifest(archivePath string) (ZipFileManifest, error) {
zipReader, err := OpenZip(archivePath)
func NewZipFileManifest(ctx context.Context, archivePath string) (ZipFileManifest, error) {
zipReader, err := os.Open(archivePath)
manifest := make(ZipFileManifest)
if err != nil {
log.Debugf("unable to open zip archive (%s): %v", archivePath, err)
return manifest, err
}
defer func() {
err = zipReader.Close()
if err != nil {
if err = zipReader.Close(); err != nil {
log.Debugf("unable to close zip archive (%s): %+v", archivePath, err)
}
}()
for _, file := range zipReader.File {
manifest.Add(file.Name, file.FileInfo())
err = archives.Zip{}.Extract(ctx, zipReader, func(_ context.Context, file archives.FileInfo) error {
manifest.Add(file.NameInArchive, file.FileInfo)
return nil
})
if err != nil {
return manifest, err
}
return manifest, nil
}

View File

@ -4,6 +4,7 @@
package file
import (
"context"
"encoding/json"
"os"
"path"
@ -24,7 +25,7 @@ func TestNewZipFileManifest(t *testing.T) {
archiveFilePath := setupZipFileTest(t, sourceDirPath, false)
actual, err := NewZipFileManifest(archiveFilePath)
actual, err := NewZipFileManifest(context.Background(), archiveFilePath)
if err != nil {
t.Fatalf("unable to extract from unzip archive: %+v", err)
}
@ -59,7 +60,7 @@ func TestNewZip64FileManifest(t *testing.T) {
sourceDirPath := path.Join(cwd, "test-fixtures", "zip-source")
archiveFilePath := setupZipFileTest(t, sourceDirPath, true)
actual, err := NewZipFileManifest(archiveFilePath)
actual, err := NewZipFileManifest(context.Background(), archiveFilePath)
if err != nil {
t.Fatalf("unable to extract from unzip archive: %+v", err)
}
@ -99,7 +100,7 @@ func TestZipFileManifest_GlobMatch(t *testing.T) {
archiveFilePath := setupZipFileTest(t, sourceDirPath, false)
z, err := NewZipFileManifest(archiveFilePath)
z, err := NewZipFileManifest(context.Background(), archiveFilePath)
if err != nil {
t.Fatalf("unable to extract from unzip archive: %+v", err)
}

View File

@ -1,13 +1,15 @@
package file
import (
"archive/zip"
"bytes"
"context"
"fmt"
"os"
"path/filepath"
"strings"
"github.com/mholt/archives"
"github.com/anchore/syft/internal/log"
)
@ -25,7 +27,7 @@ type errZipSlipDetected struct {
}
func (e *errZipSlipDetected) Error() string {
return fmt.Sprintf("paths are not allowed to resolve outside of the root prefix (%q). Destination: %q", e.Prefix, e.JoinArgs)
return fmt.Sprintf("path traversal detected: paths are not allowed to resolve outside of the root prefix (%q). Destination: %q", e.Prefix, e.JoinArgs)
}
type zipTraversalRequest map[string]struct{}
@ -39,38 +41,34 @@ func newZipTraverseRequest(paths ...string) zipTraversalRequest {
}
// TraverseFilesInZip enumerates all paths stored within a zip archive using the visitor pattern.
func TraverseFilesInZip(archivePath string, visitor func(*zip.File) error, paths ...string) error {
func TraverseFilesInZip(ctx context.Context, archivePath string, visitor archives.FileHandler, paths ...string) error {
request := newZipTraverseRequest(paths...)
zipReader, err := OpenZip(archivePath)
zipReader, err := os.Open(archivePath)
if err != nil {
return fmt.Errorf("unable to open zip archive (%s): %w", archivePath, err)
}
defer func() {
err = zipReader.Close()
if err != nil {
if err := zipReader.Close(); err != nil {
log.Errorf("unable to close zip archive (%s): %+v", archivePath, err)
}
}()
for _, file := range zipReader.File {
return archives.Zip{}.Extract(ctx, zipReader, func(ctx context.Context, file archives.FileInfo) error {
// if no paths are given then assume that all files should be traversed
if len(paths) > 0 {
if _, ok := request[file.Name]; !ok {
if _, ok := request[file.NameInArchive]; !ok {
// this file path is not of interest
continue
return nil
}
}
if err = visitor(file); err != nil {
return err
}
}
return nil
return visitor(ctx, file)
})
}
// ExtractFromZipToUniqueTempFile extracts select paths for the given archive to a temporary directory, returning file openers for each file extracted.
func ExtractFromZipToUniqueTempFile(archivePath, dir string, paths ...string) (map[string]Opener, error) {
func ExtractFromZipToUniqueTempFile(ctx context.Context, archivePath, dir string, paths ...string) (map[string]Opener, error) {
results := make(map[string]Opener)
// don't allow for full traversal, only select traversal from given paths
@ -78,9 +76,8 @@ func ExtractFromZipToUniqueTempFile(archivePath, dir string, paths ...string) (m
return results, nil
}
visitor := func(file *zip.File) error {
tempfilePrefix := filepath.Base(filepath.Clean(file.Name)) + "-"
visitor := func(_ context.Context, file archives.FileInfo) error {
tempfilePrefix := filepath.Base(filepath.Clean(file.NameInArchive)) + "-"
tempFile, err := os.CreateTemp(dir, tempfilePrefix)
if err != nil {
return fmt.Errorf("unable to create temp file: %w", err)
@ -92,33 +89,32 @@ func ExtractFromZipToUniqueTempFile(archivePath, dir string, paths ...string) (m
zippedFile, err := file.Open()
if err != nil {
return fmt.Errorf("unable to read file=%q from zip=%q: %w", file.Name, archivePath, err)
return fmt.Errorf("unable to read file=%q from zip=%q: %w", file.NameInArchive, archivePath, err)
}
defer func() {
err := zippedFile.Close()
if err != nil {
log.Errorf("unable to close source file=%q from zip=%q: %+v", file.Name, archivePath, err)
if err := zippedFile.Close(); err != nil {
log.Errorf("unable to close source file=%q from zip=%q: %+v", file.NameInArchive, archivePath, err)
}
}()
if file.FileInfo().IsDir() {
return fmt.Errorf("unable to extract directories, only files: %s", file.Name)
if file.IsDir() {
return fmt.Errorf("unable to extract directories, only files: %s", file.NameInArchive)
}
if err := safeCopy(tempFile, zippedFile); err != nil {
return fmt.Errorf("unable to copy source=%q for zip=%q: %w", file.Name, archivePath, err)
return fmt.Errorf("unable to copy source=%q for zip=%q: %w", file.NameInArchive, archivePath, err)
}
results[file.Name] = Opener{path: tempFile.Name()}
results[file.NameInArchive] = Opener{path: tempFile.Name()}
return nil
}
return results, TraverseFilesInZip(archivePath, visitor, paths...)
return results, TraverseFilesInZip(ctx, archivePath, visitor, paths...)
}
// ContentsFromZip extracts select paths for the given archive and returns a set of string contents for each path.
func ContentsFromZip(archivePath string, paths ...string) (map[string]string, error) {
func ContentsFromZip(ctx context.Context, archivePath string, paths ...string) (map[string]string, error) {
results := make(map[string]string)
// don't allow for full traversal, only select traversal from given paths
@ -126,37 +122,38 @@ func ContentsFromZip(archivePath string, paths ...string) (map[string]string, er
return results, nil
}
visitor := func(file *zip.File) error {
visitor := func(_ context.Context, file archives.FileInfo) error {
zippedFile, err := file.Open()
if err != nil {
return fmt.Errorf("unable to read file=%q from zip=%q: %w", file.Name, archivePath, err)
return fmt.Errorf("unable to read file=%q from zip=%q: %w", file.NameInArchive, archivePath, err)
}
defer func() {
if err := zippedFile.Close(); err != nil {
log.Errorf("unable to close source file=%q from zip=%q: %+v", file.NameInArchive, archivePath, err)
}
}()
if file.FileInfo().IsDir() {
return fmt.Errorf("unable to extract directories, only files: %s", file.Name)
if file.IsDir() {
return fmt.Errorf("unable to extract directories, only files: %s", file.NameInArchive)
}
var buffer bytes.Buffer
if err := safeCopy(&buffer, zippedFile); err != nil {
return fmt.Errorf("unable to copy source=%q for zip=%q: %w", file.Name, archivePath, err)
return fmt.Errorf("unable to copy source=%q for zip=%q: %w", file.NameInArchive, archivePath, err)
}
results[file.Name] = buffer.String()
results[file.NameInArchive] = buffer.String()
err = zippedFile.Close()
if err != nil {
return fmt.Errorf("unable to close source file=%q from zip=%q: %w", file.Name, archivePath, err)
}
return nil
}
return results, TraverseFilesInZip(archivePath, visitor, paths...)
return results, TraverseFilesInZip(ctx, archivePath, visitor, paths...)
}
// UnzipToDir extracts a zip archive to a target directory.
func UnzipToDir(archivePath, targetDir string) error {
visitor := func(file *zip.File) error {
joinedPath, err := safeJoin(targetDir, file.Name)
func UnzipToDir(ctx context.Context, archivePath, targetDir string) error {
visitor := func(_ context.Context, file archives.FileInfo) error {
joinedPath, err := SafeJoin(targetDir, file.NameInArchive)
if err != nil {
return err
}
@ -164,11 +161,11 @@ func UnzipToDir(archivePath, targetDir string) error {
return extractSingleFile(file, joinedPath, archivePath)
}
return TraverseFilesInZip(archivePath, visitor)
return TraverseFilesInZip(ctx, archivePath, visitor)
}
// safeJoin ensures that any destinations do not resolve to a path above the prefix path.
func safeJoin(prefix string, dest ...string) (string, error) {
// SafeJoin ensures that any destinations do not resolve to a path above the prefix path.
func SafeJoin(prefix string, dest ...string) (string, error) {
joinResult := filepath.Join(append([]string{prefix}, dest...)...)
cleanJoinResult := filepath.Clean(joinResult)
if !strings.HasPrefix(cleanJoinResult, filepath.Clean(prefix)) {
@ -181,13 +178,18 @@ func safeJoin(prefix string, dest ...string) (string, error) {
return joinResult, nil
}
func extractSingleFile(file *zip.File, expandedFilePath, archivePath string) error {
func extractSingleFile(file archives.FileInfo, expandedFilePath, archivePath string) error {
zippedFile, err := file.Open()
if err != nil {
return fmt.Errorf("unable to read file=%q from zip=%q: %w", file.Name, archivePath, err)
return fmt.Errorf("unable to read file=%q from zip=%q: %w", file.NameInArchive, archivePath, err)
}
defer func() {
if err := zippedFile.Close(); err != nil {
log.Errorf("unable to close source file=%q from zip=%q: %+v", file.NameInArchive, archivePath, err)
}
}()
if file.FileInfo().IsDir() {
if file.IsDir() {
err = os.MkdirAll(expandedFilePath, file.Mode())
if err != nil {
return fmt.Errorf("unable to create dir=%q from zip=%q: %w", expandedFilePath, archivePath, err)
@ -202,20 +204,16 @@ func extractSingleFile(file *zip.File, expandedFilePath, archivePath string) err
if err != nil {
return fmt.Errorf("unable to create dest file=%q from zip=%q: %w", expandedFilePath, archivePath, err)
}
defer func() {
if err := outputFile.Close(); err != nil {
log.Errorf("unable to close dest file=%q from zip=%q: %+v", outputFile.Name(), archivePath, err)
}
}()
if err := safeCopy(outputFile, zippedFile); err != nil {
return fmt.Errorf("unable to copy source=%q to dest=%q for zip=%q: %w", file.Name, outputFile.Name(), archivePath, err)
}
err = outputFile.Close()
if err != nil {
return fmt.Errorf("unable to close dest file=%q from zip=%q: %w", outputFile.Name(), archivePath, err)
return fmt.Errorf("unable to copy source=%q to dest=%q for zip=%q: %w", file.NameInArchive, outputFile.Name(), archivePath, err)
}
}
err = zippedFile.Close()
if err != nil {
return fmt.Errorf("unable to close source file=%q from zip=%q: %w", file.Name, archivePath, err)
}
return nil
}

View File

@ -4,6 +4,8 @@
package file
import (
"archive/zip"
"context"
"crypto/sha256"
"encoding/json"
"errors"
@ -17,6 +19,7 @@ import (
"github.com/go-test/deep"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func equal(r1, r2 io.Reader) (bool, error) {
@ -55,7 +58,7 @@ func TestUnzipToDir(t *testing.T) {
expectedPaths := len(expectedZipArchiveEntries)
observedPaths := 0
err = UnzipToDir(archiveFilePath, unzipDestinationDir)
err = UnzipToDir(context.Background(), archiveFilePath, unzipDestinationDir)
if err != nil {
t.Fatalf("unable to unzip archive: %+v", err)
}
@ -145,7 +148,7 @@ func TestContentsFromZip(t *testing.T) {
paths = append(paths, p)
}
actual, err := ContentsFromZip(archivePath, paths...)
actual, err := ContentsFromZip(context.Background(), archivePath, paths...)
if err != nil {
t.Fatalf("unable to extract from unzip archive: %+v", err)
}
@ -307,9 +310,528 @@ func TestSafeJoin(t *testing.T) {
for _, test := range tests {
t.Run(fmt.Sprintf("%+v:%+v", test.prefix, test.args), func(t *testing.T) {
actual, err := safeJoin(test.prefix, test.args...)
actual, err := SafeJoin(test.prefix, test.args...)
test.errAssertion(t, err)
assert.Equal(t, test.expected, actual)
})
}
}
// TestSymlinkProtection demonstrates that SafeJoin protects against symlink-based
// directory traversal attacks by validating that archive entry paths cannot escape
// the extraction directory.
func TestSafeJoin_SymlinkProtection(t *testing.T) {
tests := []struct {
name string
archivePath string // Path as it would appear in the archive
expectError bool
description string
}{
{
name: "path traversal via ../",
archivePath: "../../../outside/file.txt",
expectError: true,
description: "Archive entry with ../ trying to escape extraction dir",
},
{
name: "absolute path symlink target",
archivePath: "../../../sensitive.txt",
expectError: true,
description: "Simulates symlink pointing outside via relative path",
},
{
name: "safe relative path within extraction dir",
archivePath: "subdir/safe.txt",
expectError: false,
description: "Normal file path that stays within extraction directory",
},
{
name: "safe path with internal ../",
archivePath: "dir1/../dir2/file.txt",
expectError: false,
description: "Path with ../ that still resolves within extraction dir",
},
{
name: "deeply nested traversal",
archivePath: "../../../../../../tmp/evil.txt",
expectError: true,
description: "Multiple levels of ../ trying to escape",
},
{
name: "single parent directory escape",
archivePath: "../",
expectError: true,
description: "Simple one-level escape attempt",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Create temp directories to simulate extraction scenario
tmpDir := t.TempDir()
extractDir := filepath.Join(tmpDir, "extract")
outsideDir := filepath.Join(tmpDir, "outside")
require.NoError(t, os.MkdirAll(extractDir, 0755))
require.NoError(t, os.MkdirAll(outsideDir, 0755))
// Create a file outside extraction dir that an attacker might target
outsideFile := filepath.Join(outsideDir, "sensitive.txt")
require.NoError(t, os.WriteFile(outsideFile, []byte("sensitive data"), 0644))
// Test SafeJoin - this is what happens when processing archive entries
result, err := SafeJoin(extractDir, tt.archivePath)
if tt.expectError {
// Should block malicious paths
require.Error(t, err, "Expected SafeJoin to reject malicious path")
var zipSlipErr *errZipSlipDetected
assert.ErrorAs(t, err, &zipSlipErr, "Error should be errZipSlipDetected type")
assert.Empty(t, result, "Result should be empty for blocked paths")
} else {
// Should allow safe paths
require.NoError(t, err, "Expected SafeJoin to allow safe path")
assert.NotEmpty(t, result, "Result should not be empty for safe paths")
assert.True(t, strings.HasPrefix(filepath.Clean(result), filepath.Clean(extractDir)),
"Safe path should resolve within extraction directory")
}
})
}
}
// TestUnzipToDir_SymlinkAttacks tests UnzipToDir function with malicious ZIP archives
// containing symlink entries that attempt path traversal attacks.
//
// EXPECTED BEHAVIOR: UnzipToDir should either:
// 1. Detect and reject symlinks explicitly with a security error, OR
// 2. Extract them safely (library converts symlinks to regular files)
func TestUnzipToDir_SymlinkAttacks(t *testing.T) {
tests := []struct {
name string
symlinkName string
fileName string
errContains string
}{
{
name: "direct symlink to outside directory",
symlinkName: "evil_link",
fileName: "evil_link/payload.txt",
errContains: "not a directory", // attempt to write through symlink leaf (which is not a directory)
},
{
name: "directory symlink attack",
symlinkName: "safe_dir/link",
fileName: "safe_dir/link/payload.txt",
errContains: "not a directory", // attempt to write through symlink (which is not a directory)
},
{
name: "symlink without payload file",
symlinkName: "standalone_link",
fileName: "", // no payload file
errContains: "", // no error expected, symlink without payload is safe
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
// create outside target directory
outsideDir := filepath.Join(tempDir, "outside_target")
require.NoError(t, os.MkdirAll(outsideDir, 0755))
// create extraction directory
extractDir := filepath.Join(tempDir, "extract")
require.NoError(t, os.MkdirAll(extractDir, 0755))
maliciousZip := createMaliciousZipWithSymlink(t, tempDir, tt.symlinkName, outsideDir, tt.fileName)
err := UnzipToDir(context.Background(), maliciousZip, extractDir)
// check error expectations
if tt.errContains != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.errContains)
} else {
require.NoError(t, err)
}
analyzeExtractionDirectory(t, extractDir)
// check if payload file escaped extraction directory
if tt.fileName != "" {
maliciousFile := filepath.Join(outsideDir, filepath.Base(tt.fileName))
checkFileOutsideExtraction(t, maliciousFile)
}
// check if symlink was created pointing outside
symlinkPath := filepath.Join(extractDir, tt.symlinkName)
checkSymlinkCreation(t, symlinkPath, extractDir, outsideDir)
})
}
}
// TestContentsFromZip_SymlinkAttacks tests the ContentsFromZip function with malicious
// ZIP archives containing symlink entries.
//
// EXPECTED BEHAVIOR: ContentsFromZip should either:
// 1. Reject symlinks explicitly, OR
// 2. Return empty content for symlinks (library behavior)
//
// Though ContentsFromZip doesn't write to disk, but if symlinks are followed, it could read sensitive
// files from outside the archive.
func TestContentsFromZip_SymlinkAttacks(t *testing.T) {
tests := []struct {
name string
symlinkName string
symlinkTarget string
requestPath string
errContains string
}{
{
name: "request symlink entry directly",
symlinkName: "evil_link",
symlinkTarget: "/etc/hosts", // attempt to read sensitive file
requestPath: "evil_link",
errContains: "", // no error expected - library returns symlink metadata
},
{
name: "symlink in nested directory",
symlinkName: "nested/link",
symlinkTarget: "/etc/hosts",
requestPath: "nested/link",
errContains: "", // no error expected - library returns symlink metadata
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
// create malicious ZIP with symlink entry (no payload file needed)
maliciousZip := createMaliciousZipWithSymlink(t, tempDir, tt.symlinkName, tt.symlinkTarget, "")
contents, err := ContentsFromZip(context.Background(), maliciousZip, tt.requestPath)
// check error expectations
if tt.errContains != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.errContains)
return
}
require.NoError(t, err)
// verify symlink handling - library should return symlink target as content (metadata)
content, found := contents[tt.requestPath]
require.True(t, found, "symlink entry should be found in results")
// verify symlink was NOT followed (content should be target path or empty)
if content != "" && content != tt.symlinkTarget {
// content is not empty and not the symlink target - check if actual file was read
if _, statErr := os.Stat(tt.symlinkTarget); statErr == nil {
targetContent, readErr := os.ReadFile(tt.symlinkTarget)
if readErr == nil && string(targetContent) == content {
t.Errorf("critical issue!... symlink was FOLLOWED and external file content was read!")
t.Logf(" symlink: %s → %s", tt.requestPath, tt.symlinkTarget)
t.Logf(" content length: %d bytes", len(content))
}
}
}
})
}
}
// TestExtractFromZipToUniqueTempFile_SymlinkAttacks tests the ExtractFromZipToUniqueTempFile
// function with malicious ZIP archives containing symlink entries.
//
// EXPECTED BEHAVIOR: ExtractFromZipToUniqueTempFile should either:
// 1. Reject symlinks explicitly, OR
// 2. Extract them safely (library converts to empty files, filepath.Base sanitizes names)
//
// This function uses filepath.Base() on the archive entry name for temp file prefix and
// os.CreateTemp() which creates files in the specified directory, so it should be protected.
func TestExtractFromZipToUniqueTempFile_SymlinkAttacks(t *testing.T) {
tests := []struct {
name string
symlinkName string
symlinkTarget string
requestPath string
errContains string
}{
{
name: "extract symlink entry to temp file",
symlinkName: "evil_link",
symlinkTarget: "/etc/passwd",
requestPath: "evil_link",
errContains: "", // no error expected - library extracts symlink metadata
},
{
name: "extract nested symlink",
symlinkName: "nested/dir/link",
symlinkTarget: "/tmp/outside",
requestPath: "nested/dir/link",
errContains: "", // no error expected
},
{
name: "extract path traversal symlink name",
symlinkName: "../../escape",
symlinkTarget: "/tmp/outside",
requestPath: "../../escape",
errContains: "", // no error expected - filepath.Base sanitizes name
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
maliciousZip := createMaliciousZipWithSymlink(t, tempDir, tt.symlinkName, tt.symlinkTarget, "")
// create temp directory for extraction
extractTempDir := filepath.Join(tempDir, "temp_extract")
require.NoError(t, os.MkdirAll(extractTempDir, 0755))
openers, err := ExtractFromZipToUniqueTempFile(context.Background(), maliciousZip, extractTempDir, tt.requestPath)
// check error expectations
if tt.errContains != "" {
require.Error(t, err)
require.Contains(t, err.Error(), tt.errContains)
return
}
require.NoError(t, err)
// verify symlink was extracted
opener, found := openers[tt.requestPath]
require.True(t, found, "symlink entry should be extracted")
// verify temp file is within temp directory
tempFilePath := opener.path
cleanTempDir := filepath.Clean(extractTempDir)
cleanTempFile := filepath.Clean(tempFilePath)
require.True(t, strings.HasPrefix(cleanTempFile, cleanTempDir),
"temp file must be within temp directory: %s not in %s", cleanTempFile, cleanTempDir)
// verify symlink was NOT followed (content should be target path or empty)
f, openErr := opener.Open()
require.NoError(t, openErr)
defer f.Close()
content, readErr := io.ReadAll(f)
require.NoError(t, readErr)
// check if symlink was followed (content matches actual file)
if len(content) > 0 && string(content) != tt.symlinkTarget {
if _, statErr := os.Stat(tt.symlinkTarget); statErr == nil {
targetContent, readErr := os.ReadFile(tt.symlinkTarget)
if readErr == nil && string(targetContent) == string(content) {
t.Errorf("critical issue!... symlink was FOLLOWED and external file content was copied!")
t.Logf(" symlink: %s → %s", tt.requestPath, tt.symlinkTarget)
t.Logf(" content length: %d bytes", len(content))
}
}
}
})
}
}
// forensicFindings contains the results of analyzing an extraction directory
type forensicFindings struct {
symlinksFound []forensicSymlink
regularFiles []string
directories []string
symlinkVulnerabilities []string
}
type forensicSymlink struct {
path string
target string
escapesExtraction bool
resolvedPath string
}
// analyzeExtractionDirectory walks the extraction directory and detects symlinks that point
// outside the extraction directory. It is silent unless vulnerabilities are found.
func analyzeExtractionDirectory(t *testing.T, extractDir string) forensicFindings {
t.Helper()
findings := forensicFindings{}
filepath.Walk(extractDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
// only log if there's an error walking the directory
t.Logf("Error walking %s: %v", path, err)
return nil
}
relPath := strings.TrimPrefix(path, extractDir+"/")
if relPath == "" {
relPath = "."
}
// use Lstat to detect symlinks without following them
linfo, lerr := os.Lstat(path)
if lerr == nil && linfo.Mode()&os.ModeSymlink != 0 {
target, _ := os.Readlink(path)
// resolve to see where it actually points
var resolvedPath string
var escapesExtraction bool
if filepath.IsAbs(target) {
// absolute symlink
resolvedPath = target
cleanExtractDir := filepath.Clean(extractDir)
escapesExtraction = !strings.HasPrefix(filepath.Clean(target), cleanExtractDir)
if escapesExtraction {
t.Errorf("critical issue!... absolute symlink created: %s → %s", relPath, target)
t.Logf(" this symlink points outside the extraction directory")
findings.symlinkVulnerabilities = append(findings.symlinkVulnerabilities,
fmt.Sprintf("absolute symlink: %s → %s", relPath, target))
}
} else {
// relative symlink - resolve it
resolvedPath = filepath.Join(filepath.Dir(path), target)
cleanResolved := filepath.Clean(resolvedPath)
cleanExtractDir := filepath.Clean(extractDir)
escapesExtraction = !strings.HasPrefix(cleanResolved, cleanExtractDir)
if escapesExtraction {
t.Errorf("critical issue!... symlink escapes extraction dir: %s → %s", relPath, target)
t.Logf(" symlink resolves to: %s (outside extraction directory)", cleanResolved)
findings.symlinkVulnerabilities = append(findings.symlinkVulnerabilities,
fmt.Sprintf("relative symlink escape: %s → %s (resolves to %s)", relPath, target, cleanResolved))
}
}
findings.symlinksFound = append(findings.symlinksFound, forensicSymlink{
path: relPath,
target: target,
escapesExtraction: escapesExtraction,
resolvedPath: resolvedPath,
})
} else {
// regular file or directory - collect silently
if info.IsDir() {
findings.directories = append(findings.directories, relPath)
} else {
findings.regularFiles = append(findings.regularFiles, relPath)
}
}
return nil
})
return findings
}
// checkFileOutsideExtraction checks if a file was written outside the extraction directory.
// Returns true if the file exists (vulnerability), false otherwise. Silent on success.
func checkFileOutsideExtraction(t *testing.T, filePath string) bool {
t.Helper()
if stat, err := os.Stat(filePath); err == nil {
content, _ := os.ReadFile(filePath)
t.Errorf("critical issue!... file written OUTSIDE extraction directory!")
t.Logf(" location: %s", filePath)
t.Logf(" size: %d bytes", stat.Size())
t.Logf(" content: %s", string(content))
t.Logf(" ...this means an attacker can write files to arbitrary locations on the filesystem")
return true
}
// no file found outside extraction directory...
return false
}
// checkSymlinkCreation verifies if a symlink was created at the expected path and reports
// whether it points outside the extraction directory. Silent unless a symlink is found.
func checkSymlinkCreation(t *testing.T, symlinkPath, extractDir, expectedTarget string) bool {
t.Helper()
if linfo, err := os.Lstat(symlinkPath); err == nil {
if linfo.Mode()&os.ModeSymlink != 0 {
target, _ := os.Readlink(symlinkPath)
if expectedTarget != "" && target == expectedTarget {
t.Errorf("critical issue!... symlink pointing outside extraction dir was created!")
t.Logf(" Symlink: %s → %s", symlinkPath, target)
return true
}
// Check if it escapes even if target doesn't match expected
if filepath.IsAbs(target) {
cleanExtractDir := filepath.Clean(extractDir)
if !strings.HasPrefix(filepath.Clean(target), cleanExtractDir) {
t.Errorf("critical issue!... absolute symlink escapes extraction dir!")
t.Logf(" symlink: %s → %s", symlinkPath, target)
return true
}
}
}
// if it exists but is not a symlink, that's good (attack was thwarted)...
}
return false
}
// createMaliciousZipWithSymlink creates a ZIP archive containing a symlink entry pointing to an arbitrary target,
// followed by a file entry that attempts to write through that symlink.
// returns the path to the created ZIP archive.
func createMaliciousZipWithSymlink(t *testing.T, tempDir, symlinkName, symlinkTarget, fileName string) string {
t.Helper()
maliciousZip := filepath.Join(tempDir, "malicious.zip")
zipFile, err := os.Create(maliciousZip)
require.NoError(t, err)
defer zipFile.Close()
zw := zip.NewWriter(zipFile)
// create parent directories if the symlink is nested
if dir := filepath.Dir(symlinkName); dir != "." {
dirHeader := &zip.FileHeader{
Name: dir + "/",
Method: zip.Store,
}
dirHeader.SetMode(os.ModeDir | 0755)
_, err = zw.CreateHeader(dirHeader)
require.NoError(t, err)
}
// create symlink entry pointing outside extraction directory
// note: ZIP format stores symlinks as regular files with the target path as content
symlinkHeader := &zip.FileHeader{
Name: symlinkName,
Method: zip.Store,
}
symlinkHeader.SetMode(os.ModeSymlink | 0755)
symlinkWriter, err := zw.CreateHeader(symlinkHeader)
require.NoError(t, err)
// write the symlink target as the file content (this is how ZIP stores symlinks)
_, err = symlinkWriter.Write([]byte(symlinkTarget))
require.NoError(t, err)
// create file entry that will be written through the symlink
if fileName != "" {
payloadContent := []byte("MALICIOUS PAYLOAD - This should NOT be written outside extraction dir!")
payloadHeader := &zip.FileHeader{
Name: fileName,
Method: zip.Deflate,
}
payloadHeader.SetMode(0644)
payloadWriter, err := zw.CreateHeader(payloadHeader)
require.NoError(t, err)
_, err = payloadWriter.Write(payloadContent)
require.NoError(t, err)
}
require.NoError(t, zw.Close())
require.NoError(t, zipFile.Close())
return maliciousZip
}

View File

@ -1,229 +0,0 @@
package file
import (
"archive/zip"
"encoding/binary"
"errors"
"fmt"
"io"
"math"
"os"
"github.com/anchore/syft/internal/log"
)
// directoryEndLen, readByf, directoryEnd, and findSignatureInBlock were copied from the golang stdlib, specifically:
// - https://github.com/golang/go/blob/go1.16.4/src/archive/zip/struct.go
// - https://github.com/golang/go/blob/go1.16.4/src/archive/zip/reader.go
// findArchiveStartOffset is derived from the same stdlib utils, specifically the readDirectoryEnd function.
const (
directoryEndLen = 22
directory64LocLen = 20
directory64EndLen = 56
directory64LocSignature = 0x07064b50
directory64EndSignature = 0x06064b50
)
// ZipReadCloser is a drop-in replacement for zip.ReadCloser (from zip.OpenReader) that additionally considers zips
// that have bytes prefixed to the front of the archive (common with self-extracting jars).
type ZipReadCloser struct {
*zip.Reader
io.Closer
}
// OpenZip provides a ZipReadCloser for the given filepath.
func OpenZip(filepath string) (*ZipReadCloser, error) {
f, err := os.Open(filepath)
if err != nil {
return nil, err
}
fi, err := f.Stat()
if err != nil {
f.Close()
return nil, err
}
// some archives may have bytes prepended to the front of the archive, such as with self executing JARs. We first
// need to find the start of the archive and keep track of this offset.
offset, err := findArchiveStartOffset(f, fi.Size())
if err != nil {
log.Debugf("cannot find beginning of zip archive=%q : %v", filepath, err)
return nil, err
}
if _, err := f.Seek(0, io.SeekStart); err != nil {
return nil, fmt.Errorf("unable to seek to beginning of archive: %w", err)
}
if offset > math.MaxInt64 {
return nil, fmt.Errorf("archive start offset too large: %v", offset)
}
offset64 := int64(offset)
size := fi.Size() - offset64
r, err := zip.NewReader(io.NewSectionReader(f, offset64, size), size)
if err != nil {
log.Debugf("unable to open ZipReadCloser @ %q: %v", filepath, err)
return nil, err
}
return &ZipReadCloser{
Reader: r,
Closer: f,
}, nil
}
type readBuf []byte
func (b *readBuf) uint16() uint16 {
v := binary.LittleEndian.Uint16(*b)
*b = (*b)[2:]
return v
}
func (b *readBuf) uint32() uint32 {
v := binary.LittleEndian.Uint32(*b)
*b = (*b)[4:]
return v
}
func (b *readBuf) uint64() uint64 {
v := binary.LittleEndian.Uint64(*b)
*b = (*b)[8:]
return v
}
type directoryEnd struct {
diskNbr uint32 // unused
dirDiskNbr uint32 // unused
dirRecordsThisDisk uint64 // unused
directoryRecords uint64
directorySize uint64
directoryOffset uint64 // relative to file
}
// note: this is derived from readDirectoryEnd within the archive/zip package
func findArchiveStartOffset(r io.ReaderAt, size int64) (startOfArchive uint64, err error) {
// look for directoryEndSignature in the last 1k, then in the last 65k
var buf []byte
var directoryEndOffset int64
for i, bLen := range []int64{1024, 65 * 1024} {
if bLen > size {
bLen = size
}
buf = make([]byte, int(bLen))
if _, err := r.ReadAt(buf, size-bLen); err != nil && !errors.Is(err, io.EOF) {
return 0, err
}
if p := findSignatureInBlock(buf); p >= 0 {
buf = buf[p:]
directoryEndOffset = size - bLen + int64(p)
break
}
if i == 1 || bLen == size {
return 0, zip.ErrFormat
}
}
if buf == nil {
// we were unable to find the directoryEndSignature block
return 0, zip.ErrFormat
}
// read header into struct
b := readBuf(buf[4:]) // skip signature
d := &directoryEnd{
diskNbr: uint32(b.uint16()),
dirDiskNbr: uint32(b.uint16()),
dirRecordsThisDisk: uint64(b.uint16()),
directoryRecords: uint64(b.uint16()),
directorySize: uint64(b.uint32()),
directoryOffset: uint64(b.uint32()),
}
// Calculate where the zip data actually begins
// These values mean that the file can be a zip64 file
if d.directoryRecords == 0xffff || d.directorySize == 0xffff || d.directoryOffset == 0xffffffff {
p, err := findDirectory64End(r, directoryEndOffset)
if err == nil && p >= 0 {
directoryEndOffset = p
err = readDirectory64End(r, p, d)
}
if err != nil {
return 0, err
}
}
startOfArchive = uint64(directoryEndOffset) - d.directorySize - d.directoryOffset
// Make sure directoryOffset points to somewhere in our file.
if d.directoryOffset >= uint64(size) {
return 0, zip.ErrFormat
}
return startOfArchive, nil
}
// findDirectory64End tries to read the zip64 locator just before the
// directory end and returns the offset of the zip64 directory end if
// found.
func findDirectory64End(r io.ReaderAt, directoryEndOffset int64) (int64, error) {
locOffset := directoryEndOffset - directory64LocLen
if locOffset < 0 {
return -1, nil // no need to look for a header outside the file
}
buf := make([]byte, directory64LocLen)
if _, err := r.ReadAt(buf, locOffset); err != nil {
return -1, err
}
b := readBuf(buf)
if sig := b.uint32(); sig != directory64LocSignature {
return -1, nil
}
if b.uint32() != 0 { // number of the disk with the start of the zip64 end of central directory
return -1, nil // the file is not a valid zip64-file
}
p := b.uint64() // relative offset of the zip64 end of central directory record
if b.uint32() != 1 { // total number of disks
return -1, nil // the file is not a valid zip64-file
}
return int64(p), nil
}
// readDirectory64End reads the zip64 directory end and updates the
// directory end with the zip64 directory end values.
func readDirectory64End(r io.ReaderAt, offset int64, d *directoryEnd) (err error) {
buf := make([]byte, directory64EndLen)
if _, err := r.ReadAt(buf, offset); err != nil {
return err
}
b := readBuf(buf)
if sig := b.uint32(); sig != directory64EndSignature {
return errors.New("could not read directory64End")
}
b = b[12:] // skip dir size, version and version needed (uint64 + 2x uint16)
d.diskNbr = b.uint32() // number of this disk
d.dirDiskNbr = b.uint32() // number of the disk with the start of the central directory
d.dirRecordsThisDisk = b.uint64() // total number of entries in the central directory on this disk
d.directoryRecords = b.uint64() // total number of entries in the central directory
d.directorySize = b.uint64() // size of the central directory
d.directoryOffset = b.uint64() // offset of start of central directory with respect to the starting disk number
return nil
}
func findSignatureInBlock(b []byte) int {
for i := len(b) - directoryEndLen; i >= 0; i-- {
// defined from directoryEndSignature
if b[i] == 'P' && b[i+1] == 'K' && b[i+2] == 0x05 && b[i+3] == 0x06 {
// n is length of comment
n := int(b[i+directoryEndLen-2]) | int(b[i+directoryEndLen-1])<<8
if n+directoryEndLen+i <= len(b) {
return i
}
}
}
return -1
}

View File

@ -1,50 +0,0 @@
//go:build !windows
// +build !windows
package file
import (
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func TestFindArchiveStartOffset(t *testing.T) {
tests := []struct {
name string
archivePrep func(tb testing.TB) string
expected uint64
}{
{
name: "standard, non-nested zip",
archivePrep: prepZipSourceFixture,
expected: 0,
},
{
name: "zip with prepended bytes",
archivePrep: prependZipSourceFixtureWithString(t, "junk at the beginning of the file..."),
expected: 36,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
archivePath := test.archivePrep(t)
f, err := os.Open(archivePath)
if err != nil {
t.Fatalf("could not open archive %q: %+v", archivePath, err)
}
fi, err := os.Stat(f.Name())
if err != nil {
t.Fatalf("unable to stat archive: %+v", err)
}
actual, err := findArchiveStartOffset(f, fi.Size())
if err != nil {
t.Fatalf("unable to find offset: %+v", err)
}
assert.Equal(t, test.expected, actual)
})
}
}

View File

@ -0,0 +1 @@
Please see [schema/json/README.md](../../schema/json/README.md) for more information on the JSON schema files in this directory.

View File

@ -0,0 +1,159 @@
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
"path/filepath"
"strings"
"github.com/invopop/jsonschema"
)
func copyAliasFieldComments(commentMap map[string]string, repoRoot string) {
// find all type aliases by parsing Go source files
aliases := findTypeAliases(repoRoot)
// for each alias, copy field comments from the source type
for aliasName, sourceName := range aliases {
// find all field comments for the source type
for key, comment := range commentMap {
// check if this is a field comment for the source type
// format: "github.com/anchore/syft/syft/pkg.SourceType.FieldName"
if strings.Contains(key, "."+sourceName+".") {
// create the corresponding key for the alias
aliasKey := strings.Replace(key, "."+sourceName+".", "."+aliasName+".", 1)
commentMap[aliasKey] = comment
}
}
}
}
func findTypeAliases(repoRoot string) map[string]string {
aliases := make(map[string]string)
fset := token.NewFileSet()
// walk through all Go files in the repo
err := filepath.Walk(repoRoot, func(path string, info os.FileInfo, err error) error {
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".go") {
return nil
}
// parse the file
file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
if err != nil {
return nil
}
// look for type alias declarations
ast.Inspect(file, func(n ast.Node) bool {
typeSpec, ok := n.(*ast.TypeSpec)
if !ok {
return true
}
// check if this is a type alias (e.g., type A B where B is an identifier)
ident, ok := typeSpec.Type.(*ast.Ident)
if !ok {
return true
}
// store the alias mapping: aliasName -> sourceName
aliases[typeSpec.Name.Name] = ident.Name
return true
})
return nil
})
if err != nil {
fmt.Fprintf(os.Stderr, "error: failed to find type aliases: %v\n", err)
panic(err)
}
return aliases
}
func hasDescriptionInAlternatives(schema *jsonschema.Schema) bool {
// check oneOf alternatives
for _, alt := range schema.OneOf {
if alt.Description != "" {
return true
}
}
// check anyOf alternatives
for _, alt := range schema.AnyOf {
if alt.Description != "" {
return true
}
}
return false
}
func warnMissingDescriptions(schema *jsonschema.Schema, metadataNames []string) { //nolint:gocognit
var missingTypeDescriptions []string
var missingFieldDescriptions []string
// check metadata types for missing descriptions
for _, name := range metadataNames {
def, ok := schema.Definitions[name]
if !ok {
continue
}
// check if type has a description
if def.Description == "" {
missingTypeDescriptions = append(missingTypeDescriptions, name)
}
// check if fields have descriptions
if def.Properties != nil {
for _, fieldName := range def.Properties.Keys() {
fieldSchemaRaw, _ := def.Properties.Get(fieldName)
fieldSchema, ok := fieldSchemaRaw.(*jsonschema.Schema)
if !ok {
continue
}
// skip if field has a description
if fieldSchema.Description != "" {
continue
}
// skip if field is a reference (descriptions come from the referenced type)
if fieldSchema.Ref != "" {
continue
}
// skip if field is an array/object with items that are references
if fieldSchema.Items != nil && fieldSchema.Items.Ref != "" {
continue
}
// skip if field uses oneOf/anyOf with descriptions in the alternatives
if hasDescriptionInAlternatives(fieldSchema) {
continue
}
missingFieldDescriptions = append(missingFieldDescriptions, fmt.Sprintf("%s.%s", name, fieldName))
}
}
}
// report findings
if len(missingTypeDescriptions) > 0 {
fmt.Fprintf(os.Stderr, "\nwarning: %d metadata types are missing descriptions:\n", len(missingTypeDescriptions))
for _, name := range missingTypeDescriptions {
fmt.Fprintf(os.Stderr, " - %s\n", name)
}
}
if len(missingFieldDescriptions) > 0 {
fmt.Fprintf(os.Stderr, "\nwarning: %d fields are missing descriptions:\n", len(missingFieldDescriptions))
for _, field := range missingFieldDescriptions {
fmt.Fprintf(os.Stderr, " - %s\n", field)
}
}
}

View File

@ -0,0 +1,382 @@
package main
import (
"os"
"path/filepath"
"testing"
"github.com/iancoleman/orderedmap"
"github.com/invopop/jsonschema"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// TestCopyAliasFieldComments verifies that field comments from source types are correctly copied to alias types.
// This is important for type aliases like `type RpmArchive RpmDBEntry` where the alias should inherit all field descriptions.
func TestCopyAliasFieldComments(t *testing.T) {
tests := []struct {
name string
commentMap map[string]string
aliases map[string]string
wantComments map[string]string
}{
{
name: "copies field comments from source type to alias",
commentMap: map[string]string{
"github.com/anchore/syft/syft/pkg.RpmDBEntry": "RpmDBEntry represents all captured data from a RPM DB package entry.",
"github.com/anchore/syft/syft/pkg.RpmDBEntry.Name": "Name is the RPM package name.",
"github.com/anchore/syft/syft/pkg.RpmDBEntry.Epoch": "Epoch is the version epoch.",
},
aliases: map[string]string{
"RpmArchive": "RpmDBEntry",
},
wantComments: map[string]string{
"github.com/anchore/syft/syft/pkg.RpmDBEntry": "RpmDBEntry represents all captured data from a RPM DB package entry.",
"github.com/anchore/syft/syft/pkg.RpmDBEntry.Name": "Name is the RPM package name.",
"github.com/anchore/syft/syft/pkg.RpmDBEntry.Epoch": "Epoch is the version epoch.",
"github.com/anchore/syft/syft/pkg.RpmArchive.Name": "Name is the RPM package name.",
"github.com/anchore/syft/syft/pkg.RpmArchive.Epoch": "Epoch is the version epoch.",
},
},
{
name: "handles multiple aliases",
commentMap: map[string]string{
"github.com/anchore/syft/syft/pkg.DpkgDBEntry": "DpkgDBEntry represents data from dpkg.",
"github.com/anchore/syft/syft/pkg.DpkgDBEntry.Package": "Package is the package name.",
"github.com/anchore/syft/syft/pkg.DpkgDBEntry.Architecture": "Architecture is the target arch.",
},
aliases: map[string]string{
"DpkgArchiveEntry": "DpkgDBEntry",
"DpkgSnapshot": "DpkgDBEntry",
},
wantComments: map[string]string{
"github.com/anchore/syft/syft/pkg.DpkgDBEntry": "DpkgDBEntry represents data from dpkg.",
"github.com/anchore/syft/syft/pkg.DpkgDBEntry.Package": "Package is the package name.",
"github.com/anchore/syft/syft/pkg.DpkgDBEntry.Architecture": "Architecture is the target arch.",
"github.com/anchore/syft/syft/pkg.DpkgArchiveEntry.Package": "Package is the package name.",
"github.com/anchore/syft/syft/pkg.DpkgArchiveEntry.Architecture": "Architecture is the target arch.",
"github.com/anchore/syft/syft/pkg.DpkgSnapshot.Package": "Package is the package name.",
"github.com/anchore/syft/syft/pkg.DpkgSnapshot.Architecture": "Architecture is the target arch.",
},
},
{
name: "does not copy non-field comments",
commentMap: map[string]string{
"github.com/anchore/syft/syft/pkg.SomeType": "SomeType struct comment.",
"github.com/anchore/syft/syft/pkg.SomeType.Field": "Field comment.",
},
aliases: map[string]string{
"AliasType": "SomeType",
},
wantComments: map[string]string{
"github.com/anchore/syft/syft/pkg.SomeType": "SomeType struct comment.",
"github.com/anchore/syft/syft/pkg.SomeType.Field": "Field comment.",
"github.com/anchore/syft/syft/pkg.AliasType.Field": "Field comment.",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// create temp dir for testing
tmpDir := t.TempDir()
// create a test go file with type aliases
testFile := filepath.Join(tmpDir, "test.go")
content := "package test\n\n"
for alias, source := range tt.aliases {
content += "type " + alias + " " + source + "\n"
}
err := os.WriteFile(testFile, []byte(content), 0644)
require.NoError(t, err)
// make a copy of the comment map since the function modifies it
commentMap := make(map[string]string)
for k, v := range tt.commentMap {
commentMap[k] = v
}
// run the function
copyAliasFieldComments(commentMap, tmpDir)
// verify results
assert.Equal(t, tt.wantComments, commentMap)
})
}
}
func TestFindTypeAliases(t *testing.T) {
tests := []struct {
name string
fileContent string
wantAliases map[string]string
}{
{
name: "finds simple type alias",
fileContent: `package test
type RpmArchive RpmDBEntry
type DpkgArchiveEntry DpkgDBEntry
`,
wantAliases: map[string]string{
"RpmArchive": "RpmDBEntry",
"DpkgArchiveEntry": "DpkgDBEntry",
},
},
{
name: "ignores struct definitions",
fileContent: `package test
type MyStruct struct {
Field string
}
type AliasType BaseType
`,
wantAliases: map[string]string{
"AliasType": "BaseType",
},
},
{
name: "ignores interface definitions",
fileContent: `package test
type MyInterface interface {
Method()
}
type AliasType BaseType
`,
wantAliases: map[string]string{
"AliasType": "BaseType",
},
},
{
name: "handles multiple files",
fileContent: `package test
type Alias1 Base1
type Alias2 Base2
`,
wantAliases: map[string]string{
"Alias1": "Base1",
"Alias2": "Base2",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// create temp dir
tmpDir := t.TempDir()
// write test file
testFile := filepath.Join(tmpDir, "test.go")
err := os.WriteFile(testFile, []byte(tt.fileContent), 0644)
require.NoError(t, err)
// run function
aliases := findTypeAliases(tmpDir)
// verify
assert.Equal(t, tt.wantAliases, aliases)
})
}
}
func TestHasDescriptionInAlternatives(t *testing.T) {
tests := []struct {
name string
schema *jsonschema.Schema
want bool
}{
{
name: "returns true when oneOf has description",
schema: &jsonschema.Schema{
OneOf: []*jsonschema.Schema{
{Description: "First alternative"},
{Type: "null"},
},
},
want: true,
},
{
name: "returns true when anyOf has description",
schema: &jsonschema.Schema{
AnyOf: []*jsonschema.Schema{
{Description: "First alternative"},
{Type: "null"},
},
},
want: true,
},
{
name: "returns false when no alternatives have descriptions",
schema: &jsonschema.Schema{
OneOf: []*jsonschema.Schema{
{Type: "integer"},
{Type: "null"},
},
},
want: false,
},
{
name: "returns false when no oneOf or anyOf",
schema: &jsonschema.Schema{
Type: "string",
},
want: false,
},
{
name: "returns true when any alternative in oneOf has description",
schema: &jsonschema.Schema{
OneOf: []*jsonschema.Schema{
{Type: "integer"},
{Type: "string", Description: "Second alternative"},
{Type: "null"},
},
},
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := hasDescriptionInAlternatives(tt.schema)
assert.Equal(t, tt.want, got)
})
}
}
func TestWarnMissingDescriptions(t *testing.T) {
tests := []struct {
name string
schema *jsonschema.Schema
metadataNames []string
wantTypeWarnings int
wantFieldWarnings int
}{
{
name: "no warnings when all types have descriptions",
schema: &jsonschema.Schema{
Definitions: map[string]*jsonschema.Schema{
"TypeA": {
Description: "Type A description",
Properties: newOrderedMap(map[string]*jsonschema.Schema{
"field1": {Type: "string", Description: "Field 1"},
}),
},
},
},
metadataNames: []string{"TypeA"},
wantTypeWarnings: 0,
wantFieldWarnings: 0,
},
{
name: "warns about missing type description",
schema: &jsonschema.Schema{
Definitions: map[string]*jsonschema.Schema{
"TypeA": {
Properties: newOrderedMap(map[string]*jsonschema.Schema{
"field1": {Type: "string", Description: "Field 1"},
}),
},
},
},
metadataNames: []string{"TypeA"},
wantTypeWarnings: 1,
wantFieldWarnings: 0,
},
{
name: "warns about missing field description",
schema: &jsonschema.Schema{
Definitions: map[string]*jsonschema.Schema{
"TypeA": {
Description: "Type A description",
Properties: newOrderedMap(map[string]*jsonschema.Schema{
"field1": {Type: "string"},
}),
},
},
},
metadataNames: []string{"TypeA"},
wantTypeWarnings: 0,
wantFieldWarnings: 1,
},
{
name: "skips fields with references",
schema: &jsonschema.Schema{
Definitions: map[string]*jsonschema.Schema{
"TypeA": {
Description: "Type A description",
Properties: newOrderedMap(map[string]*jsonschema.Schema{
"field1": {Ref: "#/$defs/OtherType"},
}),
},
},
},
metadataNames: []string{"TypeA"},
wantTypeWarnings: 0,
wantFieldWarnings: 0,
},
{
name: "skips fields with items that are references",
schema: &jsonschema.Schema{
Definitions: map[string]*jsonschema.Schema{
"TypeA": {
Description: "Type A description",
Properties: newOrderedMap(map[string]*jsonschema.Schema{
"field1": {
Type: "array",
Items: &jsonschema.Schema{Ref: "#/$defs/OtherType"},
},
}),
},
},
},
metadataNames: []string{"TypeA"},
wantTypeWarnings: 0,
wantFieldWarnings: 0,
},
{
name: "skips fields with oneOf containing descriptions",
schema: &jsonschema.Schema{
Definitions: map[string]*jsonschema.Schema{
"TypeA": {
Description: "Type A description",
Properties: newOrderedMap(map[string]*jsonschema.Schema{
"field1": {
OneOf: []*jsonschema.Schema{
{Type: "integer", Description: "Integer value"},
{Type: "null"},
},
},
}),
},
},
},
metadataNames: []string{"TypeA"},
wantTypeWarnings: 0,
wantFieldWarnings: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// capture stderr output would require more complex testing
// for now, just verify the function runs without panicking
require.NotPanics(t, func() {
warnMissingDescriptions(tt.schema, tt.metadataNames)
})
})
}
}
// helper to create an ordered map from a regular map
func newOrderedMap(m map[string]*jsonschema.Schema) *orderedmap.OrderedMap {
om := orderedmap.New()
for k, v := range m {
om.Set(k, v)
}
return om
}

View File

@ -15,8 +15,8 @@ import (
"github.com/invopop/jsonschema"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/packagemetadata"
syftJsonModel "github.com/anchore/syft/syft/format/syftjson/model"
"github.com/anchore/syft/syft/internal/packagemetadata"
)
/*
@ -26,6 +26,17 @@ are not captured (empty interfaces). This means that pkg.Package.Metadata is not
can be extended to include specific package metadata struct shapes in the future.
*/
var repoRoot string
func init() {
var err error
repoRoot, err = packagemetadata.RepoRoot()
if err != nil {
fmt.Println("unable to determine repo root")
os.Exit(1)
}
}
func main() {
write(encode(build()))
}
@ -60,7 +71,7 @@ func assembleTypeContainer(items []any) (any, map[string]string) {
}
if len(typesMissingNames) > 0 {
fmt.Println("the following types are missing JSON names (manually curated in ./syft/internal/packagemetadata/names.go):")
fmt.Println("the following types are missing JSON names (manually curated in ./internal/packagemetadata/names.go):")
for _, t := range typesMissingNames {
fmt.Println(" - ", t.Name())
}
@ -78,6 +89,38 @@ func build() *jsonschema.Schema {
Namer: func(r reflect.Type) string {
return strings.TrimPrefix(r.Name(), "JSON")
},
CommentMap: make(map[string]string),
}
// extract comments from Go source files to enrich schema descriptions
//
// note: AddGoComments parses from the module root and creates keys like "syft/pkg.TypeName",
// but the reflector expects fully qualified paths like "github.com/anchore/syft/syft/pkg.TypeName".
// We fix up the keys after extraction to match the expected format.
if err := reflector.AddGoComments("github.com/anchore/syft", repoRoot); err != nil {
fmt.Fprintf(os.Stderr, "warning: failed to extract Go comments: %v\n", err)
} else {
// fix up comment map keys to use fully qualified import paths
// note: AddGoComments includes the absolute repo path WITHOUT the leading slash
repoRootNoSlash := strings.TrimPrefix(repoRoot, "/")
fixedMap := make(map[string]string)
for k, v := range reflector.CommentMap {
newKey := k
if !strings.HasPrefix(k, "github.com/") {
// key doesn't have module prefix, add it
newKey = "github.com/anchore/syft/" + k
} else if strings.Contains(k, repoRootNoSlash) {
// key has the absolute repo path embedded, strip it
// format: github.com/anchore/syft/Users/wagoodman/code/syft-manual/syft/pkg.Type
// should be: github.com/anchore/syft/syft/pkg.Type
newKey = strings.Replace(k, repoRootNoSlash+"/", "", 1)
}
fixedMap[newKey] = v
}
reflector.CommentMap = fixedMap
// copy field comments for type aliases (e.g., type RpmArchive RpmDBEntry)
copyAliasFieldComments(reflector.CommentMap, repoRoot)
}
pkgMetadataContainer, pkgMetadataMapping := assembleTypeContainer(packagemetadata.AllTypes())
@ -130,6 +173,9 @@ func build() *jsonschema.Schema {
"anyOf": metadataTypes,
})
// warn about missing descriptions
warnMissingDescriptions(documentSchema, metadataNames)
return documentSchema
}
@ -148,11 +194,6 @@ func encode(schema *jsonschema.Schema) []byte {
}
func write(schema []byte) {
repoRoot, err := packagemetadata.RepoRoot()
if err != nil {
fmt.Println("unable to determine repo root")
os.Exit(1)
}
schemaPath := filepath.Join(repoRoot, "schema", "json", fmt.Sprintf("schema-%s.json", internal.JSONSchemaVersion))
latestSchemaPath := filepath.Join(repoRoot, "schema", "json", "schema-latest.json")

View File

@ -81,6 +81,10 @@ func Test_EnvironmentTask(t *testing.T) {
// get the source
theSource, err := syft.GetSource(context.Background(), tarPath, syft.DefaultGetSourceConfig().WithSources("docker-archive"))
require.NoError(t, err)
t.Cleanup(func() {
require.NoError(t, theSource.Close())
})
resolver, err := theSource.FileResolver(source.SquashedScope)
require.NoError(t, err)

View File

@ -24,7 +24,7 @@ var knownNonMetadataTypeNames = strset.New(
// these are names that would be removed due to common convention (e.g. used within another metadata type) but are
// known to be metadata types themselves. Adding to this list will prevent the removal of the type from the schema.
var knownMetadaTypeNames = strset.New(
var knownMetadataTypeNames = strset.New(
"DotnetPortableExecutableEntry",
)
@ -72,7 +72,7 @@ func findMetadataDefinitionNames(paths ...string) ([]string, error) {
}
// any definition that is used within another struct should not be considered a top-level metadata definition
removeNames := strset.Difference(usedNames, knownMetadaTypeNames)
removeNames := strset.Difference(usedNames, knownMetadataTypeNames)
names.Remove(removeNames.List()...)
// remove known exceptions, that is, types exported in the pkg Package that are not used

View File

@ -7,7 +7,7 @@ import (
"github.com/dave/jennifer/jen"
"github.com/anchore/syft/syft/internal/packagemetadata"
"github.com/anchore/syft/internal/packagemetadata"
)
// This program is invoked from syft/internal and generates packagemetadata/generated.go
@ -31,7 +31,7 @@ func main() {
fmt.Printf("updating package metadata type list with %+v types\n", len(typeNames))
f := jen.NewFile("packagemetadata")
f.HeaderComment("DO NOT EDIT: generated by syft/internal/packagemetadata/generate/main.go")
f.HeaderComment("DO NOT EDIT: generated by internal/packagemetadata/generate/main.go")
f.ImportName(pkgImport, "pkg")
f.Comment("AllTypes returns a list of all pkg metadata types that syft supports (that are represented in the pkg.Package.Metadata field).")

View File

@ -1,4 +1,4 @@
// DO NOT EDIT: generated by syft/internal/packagemetadata/generate/main.go
// DO NOT EDIT: generated by internal/packagemetadata/generate/main.go
package packagemetadata
@ -27,6 +27,7 @@ func AllTypes() []any {
pkg.ELFBinaryPackageNoteJSONPayload{},
pkg.ElixirMixLockEntry{},
pkg.ErlangRebarLockEntry{},
pkg.GGUFFileHeader{},
pkg.GitHubActionsUseStatement{},
pkg.GolangBinaryBuildinfoEntry{},
pkg.GolangModuleEntry{},
@ -49,8 +50,10 @@ func AllTypes() []any {
pkg.PhpComposerLockEntry{},
pkg.PhpPearEntry{},
pkg.PhpPeclEntry{},
pkg.PnpmLockEntry{},
pkg.PortageEntry{},
pkg.PythonPackage{},
pkg.PythonPdmLockEntry{},
pkg.PythonPipfileLockEntry{},
pkg.PythonPoetryLockEntry{},
pkg.PythonRequirementsEntry{},
@ -61,6 +64,7 @@ func AllTypes() []any {
pkg.RubyGemspec{},
pkg.RustBinaryAuditEntry{},
pkg.RustCargoLockEntry{},
pkg.SnapEntry{},
pkg.SwiftPackageManagerResolvedEntry{},
pkg.SwiplPackEntry{},
pkg.TerraformLockProviderEntry{},

View File

@ -95,13 +95,15 @@ var jsonTypes = makeJSONTypes(
jsonNames(pkg.NpmPackage{}, "javascript-npm-package", "NpmPackageJsonMetadata"),
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.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
jsonNames(pkg.PhpPeclEntry{}, "php-pecl-entry", "PhpPeclMetadata"),
jsonNames(pkg.PhpPeclEntry{}, "php-pecl-entry", "PhpPeclMetadata"), //nolint:staticcheck
jsonNames(pkg.PhpPearEntry{}, "php-pear-entry"),
jsonNames(pkg.PortageEntry{}, "portage-db-entry", "PortageMetadata"),
jsonNames(pkg.PythonPackage{}, "python-package", "PythonPackageMetadata"),
jsonNames(pkg.PythonPdmLockEntry{}, "python-pdm-lock-entry"),
jsonNames(pkg.PythonPipfileLockEntry{}, "python-pipfile-lock-entry", "PythonPipfileLockMetadata"),
jsonNames(pkg.PythonPoetryLockEntry{}, "python-poetry-lock-entry", "PythonPoetryLockMetadata"),
jsonNames(pkg.PythonRequirementsEntry{}, "python-pip-requirements-entry", "PythonRequirementsMetadata"),
@ -115,12 +117,14 @@ var jsonTypes = makeJSONTypes(
jsonNames(pkg.OpamPackage{}, "opam-package"),
jsonNames(pkg.RustCargoLockEntry{}, "rust-cargo-lock-entry", "RustCargoPackageMetadata"),
jsonNamesWithoutLookup(pkg.RustBinaryAuditEntry{}, "rust-cargo-audit-entry", "RustCargoPackageMetadata"), // the legacy value is split into two types, where the other is preferred
jsonNames(pkg.SnapEntry{}, "snap-entry"),
jsonNames(pkg.WordpressPluginEntry{}, "wordpress-plugin-entry", "WordpressMetadata"),
jsonNames(pkg.HomebrewFormula{}, "homebrew-formula"),
jsonNames(pkg.LuaRocksPackage{}, "luarocks-package"),
jsonNames(pkg.TerraformLockProviderEntry{}, "terraform-lock-provider-entry"),
jsonNames(pkg.DotnetPackagesLockEntry{}, "dotnet-packages-lock-entry"),
jsonNames(pkg.CondaMetaPackage{}, "conda-metadata-entry", "CondaPackageMetadata"),
jsonNames(pkg.GGUFFileHeader{}, "gguf-file-header"),
)
func expandLegacyNameVariants(names ...string) []string {

View File

@ -6,7 +6,7 @@ import (
"github.com/dave/jennifer/jen"
"github.com/anchore/syft/syft/internal/sourcemetadata"
"github.com/anchore/syft/internal/sourcemetadata"
)
// This program is invoked from syft/internal and generates sourcemetadata/generated.go
@ -25,7 +25,7 @@ func main() {
fmt.Printf("updating source metadata type list with %+v types\n", len(typeNames))
f := jen.NewFile("sourcemetadata")
f.HeaderComment("DO NOT EDIT: generated by syft/internal/sourcemetadata/generate/main.go")
f.HeaderComment("DO NOT EDIT: generated by internal/sourcemetadata/generate/main.go")
f.ImportName(srcImport, "source")
f.Comment("AllTypes returns a list of all source metadata types that syft supports (that are represented in the source.Description.Metadata field).")

View File

@ -1,4 +1,4 @@
// DO NOT EDIT: generated by syft/internal/sourcemetadata/generate/main.go
// DO NOT EDIT: generated by internal/sourcemetadata/generate/main.go
package sourcemetadata

View File

@ -10,7 +10,6 @@ import (
"sort"
"strings"
"text/template"
"time"
)
// This program generates license_list.go.
@ -20,8 +19,7 @@ const (
)
var tmp = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
// This file was generated by robots at {{ .Timestamp }}
// using data from {{ .URL }}
// This file was generated using data from {{ .URL }}
package spdxlicense
const Version = {{ printf "%q" .Version }}
@ -78,13 +76,11 @@ func run() error {
urlToLicense := buildURLToLicenseMap(result)
err = tmp.Execute(f, struct {
Timestamp time.Time
URL string
Version string
LicenseIDs map[string]string
URLToLicense map[string]string
}{
Timestamp: time.Now(),
URL: url,
Version: result.Version,
LicenseIDs: licenseIDs,

View File

@ -3,6 +3,7 @@ package task
import (
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/ai"
"github.com/anchore/syft/syft/pkg/cataloger/alpine"
"github.com/anchore/syft/syft/pkg/cataloger/arch"
"github.com/anchore/syft/syft/pkg/cataloger/binary"
@ -32,6 +33,7 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/ruby"
"github.com/anchore/syft/syft/pkg/cataloger/rust"
sbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/sbom"
"github.com/anchore/syft/syft/pkg/cataloger/snap"
"github.com/anchore/syft/syft/pkg/cataloger/swift"
"github.com/anchore/syft/syft/pkg/cataloger/swipl"
"github.com/anchore/syft/syft/pkg/cataloger/terraform"
@ -51,6 +53,9 @@ const (
JavaScript = "javascript"
Node = "node"
NPM = "npm"
// Python ecosystem labels
Python = "python"
)
//nolint:funlen
@ -108,7 +113,7 @@ func DefaultPackageTaskFactories() Factories {
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return python.NewPackageCataloger(cfg.PackagesConfig.Python)
},
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "python",
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, Python,
),
newSimplePackageTaskFactory(ruby.NewGemFileLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "ruby", "gem"),
newSimplePackageTaskFactory(ruby.NewGemSpecCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "ruby", "gem", "gemspec"),
@ -126,7 +131,7 @@ func DefaultPackageTaskFactories() Factories {
pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "dotnet", "c#",
),
newSimplePackageTaskFactory(dotnet.NewDotnetPackagesLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.ImageTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "dotnet", "c#"),
newSimplePackageTaskFactory(python.NewInstalledPackageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "python"),
newSimplePackageTaskFactory(python.NewInstalledPackageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, Python),
newPackageTaskFactory(
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
return golang.NewGoModuleBinaryCataloger(cfg.PackagesConfig.Golang)
@ -173,12 +178,14 @@ func DefaultPackageTaskFactories() Factories {
newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"),
newSimplePackageTaskFactory(homebrew.NewCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "homebrew"),
newSimplePackageTaskFactory(conda.NewCondaMetaCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.PackageTag, "conda"),
newSimplePackageTaskFactory(snap.NewCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "snap"),
newSimplePackageTaskFactory(ai.NewGGUFCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "ai", "model", "gguf", "ml"),
// deprecated catalogers ////////////////////////////////////////
// these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible)
newSimplePackageTaskFactory(dotnet.NewDotnetDepsCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
newSimplePackageTaskFactory(php.NewPeclCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
newSimplePackageTaskFactory(nix.NewStoreCataloger, pkgcataloging.DeprecatedTag), // TODO: remove in syft v2.0
newSimplePackageTaskFactory(dotnet.NewDotnetDepsCataloger, pkgcataloging.DeprecatedTag), //nolint:staticcheck // TODO: remove in syft v2.0
newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DeprecatedTag), //nolint:staticcheck // TODO: remove in syft v2.0
newSimplePackageTaskFactory(php.NewPeclCataloger, pkgcataloging.DeprecatedTag), //nolint:staticcheck // TODO: remove in syft v2.0
newSimplePackageTaskFactory(nix.NewStoreCataloger, pkgcataloging.DeprecatedTag), //nolint:staticcheck // TODO: remove in syft v2.0
}
}

View File

@ -4,7 +4,8 @@ import (
"context"
"strings"
"github.com/anchore/archiver/v3"
"github.com/mholt/archives"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/sbomsync"
"github.com/anchore/syft/syft/cataloging"
@ -57,9 +58,10 @@ func (c unknownsLabelerTask) finalize(resolver file.Resolver, s *sbom.SBOM) {
}
if c.IncludeUnexpandedArchives {
ctx := context.Background()
for coords := range s.Artifacts.FileMetadata {
unarchiver, notArchiveErr := archiver.ByExtension(coords.RealPath)
if unarchiver != nil && notArchiveErr == nil && !hasPackageReference(coords) {
format, _, notArchiveErr := archives.Identify(ctx, coords.RealPath, nil)
if format != nil && notArchiveErr == nil && !hasPackageReference(coords) {
s.Artifacts.Unknowns[coords] = append(s.Artifacts.Unknowns[coords], "archive not cataloged")
}
}

View File

@ -13,6 +13,6 @@ prior spec version (e.g. if updating to 1.7, compare the files in this directory
equivalents).
One can also update the schemas and observe the errors in order to make the necessary updates.
At the time of writing, the cyclonedx.xsd needed modifications to link to the local spdx.xsd,
and also to changes the minOccurs for a license tag to 0. (The json schema does not require
At the time of writing, the cyclonedx.xsd needed modifications to link to the local spdx.xsd,
and also to change the minOccurs for a license tag to 0. (The json schema does not require
modification for the generated file to lint properly, but can simply be copy/pasted).

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -3,6 +3,7 @@ package executable
import (
"debug/macho"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
)
@ -19,20 +20,38 @@ const (
func findMachoFeatures(data *file.Executable, reader unionreader.UnionReader) error {
// TODO: support security features
// TODO: support multi-architecture binaries
f, err := macho.NewFile(reader)
// a universal binary may have multiple architectures, so we need to check each one
readers, err := unionreader.GetReaders(reader)
if err != nil {
return err
}
libs, err := f.ImportedLibraries()
if err != nil {
return err
var libs []string
for _, r := range readers {
f, err := macho.NewFile(r)
if err != nil {
return err
}
rLibs, err := f.ImportedLibraries()
if err != nil {
return err
}
libs = append(libs, rLibs...)
// TODO handle only some having entrypoints/exports? If that is even practical
// only check for entrypoint if we don't already have one
if !data.HasEntrypoint {
data.HasEntrypoint = machoHasEntrypoint(f)
}
// only check for exports if we don't already have them
if !data.HasExports {
data.HasExports = machoHasExports(f)
}
}
data.ImportedLibraries = libs
data.HasEntrypoint = machoHasEntrypoint(f)
data.HasExports = machoHasExports(f)
// de-duplicate libraries
data.ImportedLibraries = internal.NewSet(libs...).ToSlice()
return nil
}

View File

@ -9,6 +9,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
)
@ -83,3 +84,39 @@ func Test_machoHasExports(t *testing.T) {
})
}
}
func Test_machoUniversal(t *testing.T) {
readerForFixture := func(t *testing.T, fixture string) unionreader.UnionReader {
t.Helper()
f, err := os.Open(filepath.Join("test-fixtures/shared-info", fixture))
require.NoError(t, err)
return f
}
tests := []struct {
name string
fixture string
want file.Executable
}{
{
name: "universal lib",
fixture: "bin/libhello_universal.dylib",
want: file.Executable{HasExports: true, HasEntrypoint: false},
},
{
name: "universal application",
fixture: "bin/hello_mac_universal",
want: file.Executable{HasExports: false, HasEntrypoint: true},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var data file.Executable
err := findMachoFeatures(&data, readerForFixture(t, tt.fixture))
require.NoError(t, err)
assert.Equal(t, tt.want.HasEntrypoint, data.HasEntrypoint)
assert.Equal(t, tt.want.HasExports, data.HasExports)
})
}
}

View File

@ -2,13 +2,13 @@
BIN=../../bin
all: $(BIN)/hello_linux $(BIN)/hello.exe $(BIN)/hello_mac
all: $(BIN)/hello_linux $(BIN)/hello.exe $(BIN)/hello_mac $(BIN)/hello_mac_universal
linux: $(BIN)/libhello.so
windows: $(BIN)/libhello.dll
mac: $(BIN)/libhello.dylib
mac: $(BIN)/libhello.dylib $(BIN)/hello_mac_universal
$(BIN)/hello_linux:
gcc hello.c -o $(BIN)/hello_linux
@ -19,5 +19,8 @@ $(BIN)/hello.exe:
$(BIN)/hello_mac:
o64-clang hello.c -o $(BIN)/hello_mac
$(BIN)/hello_mac_universal:
o64-clang -arch arm64 -arch x86_64 hello.c -o $(BIN)/hello_mac_universal
clean:
rm -f $(BIN)/hello_linux $(BIN)/hello.exe $(BIN)/hello_mac
rm -f $(BIN)/hello_linux $(BIN)/hello.exe $(BIN)/hello_mac $(BIN)/hello_mac_universal

View File

@ -2,13 +2,13 @@
BIN=../../bin
all: $(BIN)/libhello.so $(BIN)/libhello.dll $(BIN)/libhello.dylib
all: $(BIN)/libhello.so $(BIN)/libhello.dll $(BIN)/libhello.dylib $(BIN)/libhello_universal.dylib
linux: $(BIN)/libhello.so
windows: $(BIN)/libhello.dll
mac: $(BIN)/libhello.dylib
mac: $(BIN)/libhello.dylib $(BIN)/libhello_universal.dylib
$(BIN)/libhello.so:
gcc -shared -fPIC -o $(BIN)/libhello.so hello.c
@ -19,5 +19,8 @@ $(BIN)/libhello.dll:
$(BIN)/libhello.dylib:
o64-clang -dynamiclib -o $(BIN)/libhello.dylib hello.c
$(BIN)/libhello_universal.dylib:
o64-clang -dynamiclib -arch arm64 -arch x86_64 hello.c -o $(BIN)/libhello_universal.dylib
clean:
rm -f $(BIN)/libhello.so $(BIN)/hello.dll $(BIN)/libhello.dylib $(BIN)/libhello.a
rm -f $(BIN)/libhello.so $(BIN)/hello.dll $(BIN)/libhello.dylib $(BIN)/libhello.a $(BIN)/libhello_universal.dylib

View File

@ -7,6 +7,7 @@ import (
"github.com/scylladb/go-set/strset"
)
// CoordinateSet provides a unique collection of Coordinates with set operations.
type CoordinateSet struct {
set map[Coordinates]struct{}
}

View File

@ -9,8 +9,11 @@ import (
// Coordinates contains the minimal information needed to describe how to find a file within any possible source object (e.g. image and directory sources)
type Coordinates struct {
RealPath string `json:"path" cyclonedx:"path"` // The path where all path ancestors have no hardlinks / symlinks
FileSystemID string `json:"layerID,omitempty" cyclonedx:"layerID"` // An ID representing the filesystem. For container images, this is a layer digest. For directories or a root filesystem, this is blank.
// RealPath is the canonical absolute form of the path accessed (all symbolic links have been followed and relative path components like '.' and '..' have been removed).
RealPath string `json:"path" cyclonedx:"path"`
// FileSystemID is an ID representing and entire filesystem. For container images, this is a layer digest. For directories or a root filesystem, this is blank.
FileSystemID string `json:"layerID,omitempty" cyclonedx:"layerID"`
}
func NewCoordinates(realPath, fsID string) Coordinates {

View File

@ -1,6 +1,10 @@
package file
// Digest represents a cryptographic hash of file contents.
type Digest struct {
// Algorithm specifies the hash algorithm used (e.g., "sha256", "md5").
Algorithm string `json:"algorithm"`
Value string `json:"value"`
// Value is the hexadecimal string representation of the hash.
Value string `json:"value"`
}

View File

@ -1,40 +1,60 @@
package file
type (
ExecutableFormat string
// ExecutableFormat represents the binary executable format type.
ExecutableFormat string
// RelocationReadOnly indicates the RELRO security protection level applied to an ELF binary.
RelocationReadOnly string
)
const (
ELF ExecutableFormat = "elf"
MachO ExecutableFormat = "macho"
PE ExecutableFormat = "pe"
ELF ExecutableFormat = "elf" // Executable and Linkable Format used on Unix-like systems
MachO ExecutableFormat = "macho" // Mach object file format used on macOS and iOS
PE ExecutableFormat = "pe" // Portable Executable format used on Windows
RelocationReadOnlyNone RelocationReadOnly = "none"
RelocationReadOnlyPartial RelocationReadOnly = "partial"
RelocationReadOnlyFull RelocationReadOnly = "full"
RelocationReadOnlyNone RelocationReadOnly = "none" // no RELRO protection
RelocationReadOnlyPartial RelocationReadOnly = "partial" // partial RELRO protection
RelocationReadOnlyFull RelocationReadOnly = "full" // full RELRO protection
)
// Executable contains metadata about binary files and their security features.
type Executable struct {
// Format denotes either ELF, Mach-O, or PE
Format ExecutableFormat `json:"format" yaml:"format" mapstructure:"format"`
HasExports bool `json:"hasExports" yaml:"hasExports" mapstructure:"hasExports"`
HasEntrypoint bool `json:"hasEntrypoint" yaml:"hasEntrypoint" mapstructure:"hasEntrypoint"`
ImportedLibraries []string `json:"importedLibraries" yaml:"importedLibraries" mapstructure:"importedLibraries"`
// HasExports indicates whether the binary exports symbols.
HasExports bool `json:"hasExports" yaml:"hasExports" mapstructure:"hasExports"`
// HasEntrypoint indicates whether the binary has an entry point function.
HasEntrypoint bool `json:"hasEntrypoint" yaml:"hasEntrypoint" mapstructure:"hasEntrypoint"`
// ImportedLibraries lists the shared libraries required by this executable.
ImportedLibraries []string `json:"importedLibraries" yaml:"importedLibraries" mapstructure:"importedLibraries"`
// ELFSecurityFeatures contains ELF-specific security hardening information when Format is ELF.
ELFSecurityFeatures *ELFSecurityFeatures `json:"elfSecurityFeatures,omitempty" yaml:"elfSecurityFeatures" mapstructure:"elfSecurityFeatures"`
}
// ELFSecurityFeatures captures security hardening and protection mechanisms in ELF binaries.
type ELFSecurityFeatures struct {
// SymbolTableStripped indicates whether debugging symbols have been removed.
SymbolTableStripped bool `json:"symbolTableStripped" yaml:"symbolTableStripped" mapstructure:"symbolTableStripped"`
// classic protections
// StackCanary indicates whether stack smashing protection is enabled.
StackCanary *bool `json:"stackCanary,omitempty" yaml:"stackCanary" mapstructure:"stackCanary"`
StackCanary *bool `json:"stackCanary,omitempty" yaml:"stackCanary" mapstructure:"stackCanary"`
NoExecutable bool `json:"nx" yaml:"nx" mapstructure:"nx"`
RelocationReadOnly RelocationReadOnly `json:"relRO" yaml:"relRO" mapstructure:"relRO"`
PositionIndependentExecutable bool `json:"pie" yaml:"pie" mapstructure:"pie"`
DynamicSharedObject bool `json:"dso" yaml:"dso" mapstructure:"dso"`
// NoExecutable indicates whether NX (no-execute) protection is enabled for the stack.
NoExecutable bool `json:"nx" yaml:"nx" mapstructure:"nx"`
// RelocationReadOnly indicates the RELRO protection level.
RelocationReadOnly RelocationReadOnly `json:"relRO" yaml:"relRO" mapstructure:"relRO"`
// PositionIndependentExecutable indicates whether the binary is compiled as PIE.
PositionIndependentExecutable bool `json:"pie" yaml:"pie" mapstructure:"pie"`
// DynamicSharedObject indicates whether the binary is a shared library.
DynamicSharedObject bool `json:"dso" yaml:"dso" mapstructure:"dso"`
// LlvmSafeStack represents a compiler-based security mechanism that separates the stack into a safe stack for storing return addresses and other critical data, and an unsafe stack for everything else, to mitigate stack-based memory corruption errors
// see https://clang.llvm.org/docs/SafeStack.html

View File

@ -5,18 +5,33 @@ import (
"github.com/anchore/syft/syft/license"
)
// License represents license information discovered within a file.
type License struct {
Value string
SPDXExpression string
Type license.Type
LicenseEvidence *LicenseEvidence // evidence from license classifier
Contents string `hash:"ignore"`
// Value is the raw license string as found in the file.
Value string
// SPDXExpression is the parsed SPDX license expression if available.
SPDXExpression string
// Type categorizes how the license was determined (e.g., declared, concluded -- following the same semantics as SPDX).
Type license.Type
LicenseEvidence *LicenseEvidence
// Contents optionally stores the full license text.
Contents string `hash:"ignore"`
}
// LicenseEvidence contains details from license classifier analysis.
type LicenseEvidence struct {
// Confidence is a score indicating certainty of the license match.
Confidence int
Offset int
Extent int
// Offset is the byte position where the license text begins in the file.
Offset int
// Extent is the length in bytes of the matched license text.
Extent int
}
func NewLicense(value string) License {

View File

@ -27,18 +27,24 @@ type Location struct {
LocationMetadata `cyclonedx:""`
}
// LocationData contains the core identifying information for a file location.
type LocationData struct {
Coordinates `cyclonedx:""` // Empty string here means there is no intermediate property name, e.g. syft:locations:0:path without "coordinates"
// note: it is IMPORTANT to ignore anything but the coordinates for a Location when considering the ID (hash value)
// since the coordinates are the minimally correct ID for a location (symlinks should not come into play)
AccessPath string `hash:"ignore" json:"accessPath"` // The path to the file which may or may not have hardlinks / symlinks
ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location.
// AccessPath is the path used to retrieve file contents (which may or may not have hardlinks / symlinks in the path)
AccessPath string `hash:"ignore" json:"accessPath"`
// ref is the stereoscope file reference relative to the stereoscope.FileCatalog that has more information about this location.
ref file.Reference `hash:"ignore"`
}
func (l LocationData) Reference() file.Reference {
return l.ref
}
// LocationMetadata provides additional contextual information about a file location.
type LocationMetadata struct {
Annotations map[string]string `json:"annotations,omitempty"` // Arbitrary key-value pairs that can be used to annotate a location
}

View File

@ -2,6 +2,7 @@ package file
import "io"
// LocationReadCloser combines a Location with a ReadCloser for accessing file content with location metadata.
type LocationReadCloser struct {
Location
io.ReadCloser

View File

@ -9,6 +9,7 @@ import (
"github.com/anchore/syft/internal/log"
)
// LocationSet provides a unique collection of Locations with metadata and set operations.
type LocationSet struct {
set map[LocationData]LocationMetadata
}

View File

@ -8,6 +8,7 @@ import (
var locationSorterWithoutLayers = LocationSorter(nil)
// Locations is a sortable slice of Location values.
type Locations []Location
func (l Locations) Len() int {

View File

@ -18,6 +18,7 @@ type ContentResolver interface {
FileContentsByLocation(Location) (io.ReadCloser, error)
}
// MetadataResolver provides file metadata lookup by location.
type MetadataResolver interface {
FileMetadataByLocation(Location) (Metadata, error)
}
@ -51,6 +52,7 @@ type PathResolver interface {
RelativeFileByPath(_ Location, path string) *Location
}
// LocationResolver provides iteration over all file locations in a source.
type LocationResolver interface {
// AllLocations returns a channel of all file references from the underlying source.
// The implementation for this may vary, however, generally the following considerations should be made:
@ -59,6 +61,7 @@ type LocationResolver interface {
AllLocations(ctx context.Context) <-chan Location
}
// WritableResolver extends Resolver with the ability to write file content.
type WritableResolver interface {
Resolver

View File

@ -4,13 +4,25 @@ import (
"fmt"
)
// SearchResult represents a match found during content scanning, such as secret detection.
type SearchResult struct {
// Classification identifies the type or category of the matched content.
Classification string `json:"classification"`
LineNumber int64 `json:"lineNumber"`
LineOffset int64 `json:"lineOffset"`
SeekPosition int64 `json:"seekPosition"`
Length int64 `json:"length"`
Value string `json:"value,omitempty"`
// LineNumber is the 1-indexed line number where the match was found.
LineNumber int64 `json:"lineNumber"`
// LineOffset is the character offset from the start of the line where the match begins.
LineOffset int64 `json:"lineOffset"`
// SeekPosition is the absolute byte offset from the start of the file.
SeekPosition int64 `json:"seekPosition"`
// Length is the size in bytes of the matched content.
Length int64 `json:"length"`
// Value optionally contains the actual matched content.
Value string `json:"value,omitempty"`
}
func (s SearchResult) String() string {

View File

@ -1,9 +1,10 @@
package file
const (
NoFilesSelection Selection = "none"
FilesOwnedByPackageSelection Selection = "owned-by-package"
AllFilesSelection Selection = "all"
NoFilesSelection Selection = "none" // no files are selected
FilesOwnedByPackageSelection Selection = "owned-by-package" // only files owned by packages are selected
AllFilesSelection Selection = "all" // all files are selected
)
// Selection defines which files should be included during cataloging operations.
type Selection string

View File

@ -15,10 +15,10 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/relationship"
"github.com/anchore/syft/internal/sourcemetadata"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/format/internal/spdxutil/helpers"
"github.com/anchore/syft/syft/internal/sourcemetadata"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"

View File

@ -0,0 +1,95 @@
package cpes
import (
"bufio"
"errors"
"fmt"
"io"
"strings"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/format/internal"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
const ID sbom.FormatID = "cpes"
const version = "1"
var _ sbom.FormatDecoder = (*decoder)(nil)
type decoder struct{}
func NewFormatDecoder() sbom.FormatDecoder {
return decoder{}
}
func (d decoder) Decode(r io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) {
if r == nil {
return nil, "", "", fmt.Errorf("no reader provided")
}
s, err := toSyftModel(r)
return s, ID, version, err
}
func (d decoder) Identify(r io.Reader) (sbom.FormatID, string) {
if r == nil {
return "", ""
}
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
// skip whitespace only lines
continue
}
err := cpe.ValidateString(line)
if err != nil {
return "", ""
}
return ID, version
}
return "", ""
}
func toSyftModel(r io.Reader) (*sbom.SBOM, error) {
var errs []error
pkgs := pkg.NewCollection()
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
continue
}
// skip invalid CPEs
c, err := cpe.New(line, "")
if err != nil {
log.WithFields("error", err, "line", line).Debug("unable to parse cpe")
continue
}
p := pkg.Package{
Name: c.Attributes.Product,
Version: c.Attributes.Version,
CPEs: []cpe.CPE{c},
}
internal.Backfill(&p)
p.SetID()
pkgs.Add(p)
}
return &sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkgs,
},
}, errors.Join(errs...)
}

View File

@ -0,0 +1,171 @@
package cpes
import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"
)
func Test_CPEProvider(t *testing.T) {
tests := []struct {
name string
userInput string
sbom *sbom.SBOM
}{
{
name: "takes a single cpe",
userInput: "cpe:/a:apache:log4j:2.14.1",
sbom: &sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(pkg.Package{
Name: "log4j",
Version: "2.14.1",
CPEs: []cpe.CPE{
cpe.Must("cpe:/a:apache:log4j:2.14.1", ""),
},
}),
},
},
},
{
name: "takes multiple cpes",
userInput: `cpe:/a:apache:log4j:2.14.1
cpe:2.3:a:f5:nginx:*:*:*:*:*:*:*:*;
cpe:2.3:a:f5:nginx:0.5.2:*:*:*:*:*:*:*;
cpe:2.3:a:f5:nginx:0.5.3:*:*:*:*:*:*:*;`,
sbom: &sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(
pkg.Package{
Name: "log4j",
Version: "2.14.1",
CPEs: []cpe.CPE{
cpe.Must("cpe:/a:apache:log4j:2.14.1", ""),
},
},
pkg.Package{
Name: "nginx",
Version: "",
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:f5:nginx:*:*:*:*:*:*:*:*;", ""),
},
},
pkg.Package{
Name: "nginx",
Version: "0.5.2",
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:f5:nginx:0.5.2:*:*:*:*:*:*:*;", ""),
},
},
pkg.Package{
Name: "nginx",
Version: "0.5.3",
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:f5:nginx:0.5.3:*:*:*:*:*:*:*;", ""),
},
},
),
},
},
},
{
name: "takes cpe with no version",
userInput: "cpe:/a:apache:log4j",
sbom: &sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(pkg.Package{
Name: "log4j",
CPEs: []cpe.CPE{
cpe.Must("cpe:/a:apache:log4j", ""),
},
}),
},
},
},
{
name: "takes CPE 2.3 format",
userInput: "cpe:2.3:a:apache:log4j:2.14.1:*:*:*:*:*:*:*",
sbom: &sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(pkg.Package{
Name: "log4j",
Version: "2.14.1",
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:apache:log4j:2.14.1:*:*:*:*:*:*:*", ""),
},
}),
},
},
},
{
name: "deduces target SW from CPE - known target_sw",
userInput: "cpe:2.3:a:amazon:opensearch:*:*:*:*:*:ruby:*:*",
sbom: &sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(pkg.Package{
Name: "opensearch",
Type: pkg.GemPkg,
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:amazon:opensearch:*:*:*:*:*:ruby:*:*", ""),
},
}),
},
},
},
{
name: "handles unknown target_sw CPE field",
userInput: "cpe:2.3:a:amazon:opensearch:*:*:*:*:*:loremipsum:*:*",
sbom: &sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(pkg.Package{
Name: "opensearch",
Type: "",
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:amazon:opensearch:*:*:*:*:*:loremipsum:*:*", ""),
},
}),
},
},
},
{
name: "invalid prefix",
userInput: "dir:test-fixtures/cpe",
sbom: &sbom.SBOM{
Artifacts: sbom.Artifacts{
Packages: pkg.NewCollection(),
},
},
},
}
syftPkgOpts := []cmp.Option{
cmpopts.IgnoreFields(pkg.Package{}, "id", "Language"),
cmpopts.IgnoreUnexported(pkg.Package{}, file.LocationSet{}, pkg.LicenseSet{}),
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
dec := NewFormatDecoder()
decodedSBOM, _, _, err := dec.Decode(strings.NewReader(tc.userInput))
require.NoError(t, err)
gotSyftPkgs := decodedSBOM.Artifacts.Packages.Sorted()
wantSyftPkgs := tc.sbom.Artifacts.Packages.Sorted()
require.Equal(t, len(gotSyftPkgs), len(wantSyftPkgs))
for idx, wantPkg := range wantSyftPkgs {
if d := cmp.Diff(wantPkg, gotSyftPkgs[idx], syftPkgOpts...); d != "" {
t.Errorf("unexpected Syft Package (-want +got):\n%s", d)
}
}
})
}
}

View File

@ -3,6 +3,7 @@ package format
import (
"io"
"github.com/anchore/syft/syft/format/cpes"
"github.com/anchore/syft/syft/format/cyclonedxjson"
"github.com/anchore/syft/syft/format/cyclonedxxml"
"github.com/anchore/syft/syft/format/purls"
@ -26,6 +27,7 @@ func Decoders() []sbom.FormatDecoder {
spdxtagvalue.NewFormatDecoder(),
spdxjson.NewFormatDecoder(),
purls.NewFormatDecoder(),
cpes.NewFormatDecoder(),
}
}

View File

@ -1,11 +1,13 @@
package model
import (
"context"
"fmt"
"strings"
"time"
"github.com/anchore/archiver/v3"
"github.com/mholt/archives"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/pkg"
@ -87,6 +89,9 @@ func toGithubManifests(s *sbom.SBOM) Manifests {
}
name := dependencyName(p)
if name == "" || p.PURL == "" {
continue
}
manifest.Resolved[name] = DependencyNode{
PackageURL: p.PURL,
Metadata: toDependencyMetadata(p),
@ -150,8 +155,8 @@ func trimRelative(s string) string {
// isArchive returns true if the path appears to be an archive
func isArchive(path string) bool {
_, err := archiver.ByExtension(path)
return err == nil
format, _, err := archives.Identify(context.Background(), path, nil)
return err == nil && format != nil
}
func toDependencies(s *sbom.SBOM, p pkg.Package) (out []string) {

View File

@ -8,8 +8,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/sourcemetadata"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/sourcemetadata"
"github.com/anchore/syft/syft/linux"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/sbom"

View File

@ -16,11 +16,6 @@
"source_location": "redacted/some/path/some/path/pkg1"
},
"resolved": {
"": {
"package_url": "a-purl-2",
"relationship": "direct",
"scope": "runtime"
},
"pkg:deb/debian/package-2@2.0.1": {
"package_url": "pkg:deb/debian/package-2@2.0.1",
"relationship": "direct",

View File

@ -17,13 +17,6 @@
},
"metadata": {
"syft:filesystem":"redacted"
},
"resolved": {
"": {
"package_url": "a-purl-1",
"relationship": "direct",
"scope": "runtime"
}
}
},
"user-image-input:/somefile-2.txt": {

View File

@ -10,13 +10,31 @@ import (
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
cataloger "github.com/anchore/syft/syft/pkg/cataloger/common/cpe"
)
// Backfill takes all information present in the package and attempts to fill in any missing information
// from any available sources, such as the Metadata and PURL.
// from any available sources, such as the Metadata, PURL, or CPEs.
//
// Backfill does not call p.SetID(), but this needs to be called later to ensure it's up to date
func Backfill(p *pkg.Package) {
backfillFromPurl(p)
backfillFromCPE(p)
}
func backfillFromCPE(p *pkg.Package) {
if len(p.CPEs) == 0 {
return
}
c := p.CPEs[0]
if p.Type == "" {
p.Type = cataloger.TargetSoftwareToPackageType(c.Attributes.TargetSW)
}
}
func backfillFromPurl(p *pkg.Package) {
if p.PURL == "" {
return
}
@ -29,6 +47,7 @@ func Backfill(p *pkg.Package) {
var cpes []cpe.CPE
epoch := ""
rpmmod := ""
for _, qualifier := range purl.Qualifiers {
switch qualifier.Key {
@ -44,6 +63,8 @@ func Backfill(p *pkg.Package) {
}
case pkg.PURLQualifierEpoch:
epoch = qualifier.Value
case pkg.PURLQualifierRpmModularity:
rpmmod = qualifier.Value
}
}
@ -63,6 +84,10 @@ func Backfill(p *pkg.Package) {
setJavaMetadataFromPurl(p, purl)
}
if p.Type == pkg.RpmPkg {
setRpmMetadataFromPurl(p, rpmmod)
}
for _, c := range cpes {
if slices.Contains(p.CPEs, c) {
continue
@ -82,6 +107,35 @@ func setJavaMetadataFromPurl(p *pkg.Package, _ packageurl.PackageURL) {
}
}
func setRpmMetadataFromPurl(p *pkg.Package, rpmmod string) {
if p.Type != pkg.RpmPkg {
return
}
if rpmmod == "" {
return
}
if p.Metadata == nil {
p.Metadata = pkg.RpmDBEntry{
ModularityLabel: &rpmmod,
}
return
}
switch m := p.Metadata.(type) {
case pkg.RpmDBEntry:
if m.ModularityLabel == nil {
m.ModularityLabel = &rpmmod
p.Metadata = m
}
case pkg.RpmArchive:
if m.ModularityLabel == nil {
m.ModularityLabel = &rpmmod
p.Metadata = m
}
}
}
func setVersionFromPurl(p *pkg.Package, purl packageurl.PackageURL, epoch string) {
if p.Version == "" {
p.Version = purl.Version

View File

@ -53,6 +53,21 @@ func Test_Backfill(t *testing.T) {
Version: "1:1.12.8-26.el8",
},
},
{
name: "rpm with rpmmod",
in: pkg.Package{
PURL: "pkg:rpm/redhat/httpd@2.4.37-51?arch=x86_64&distro=rhel-8.7&rpmmod=httpd:2.4",
},
expected: pkg.Package{
PURL: "pkg:rpm/redhat/httpd@2.4.37-51?arch=x86_64&distro=rhel-8.7&rpmmod=httpd:2.4",
Type: pkg.RpmPkg,
Name: "httpd",
Version: "2.4.37-51",
Metadata: pkg.RpmDBEntry{
ModularityLabel: strRef("httpd:2.4"),
},
},
},
{
name: "bad cpe",
in: pkg.Package{
@ -106,6 +121,20 @@ func Test_Backfill(t *testing.T) {
Metadata: pkg.JavaArchive{},
},
},
{
name: "target-sw from CPE",
in: pkg.Package{
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:amazon:opensearch:*:*:*:*:*:ruby:*:*", ""),
},
},
expected: pkg.Package{
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:amazon:opensearch:*:*:*:*:*:ruby:*:*", ""),
},
Type: pkg.GemPkg,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -171,3 +200,7 @@ func Test_nameFromPurl(t *testing.T) {
})
}
}
func strRef(s string) *string {
return &s
}

View File

@ -8,9 +8,9 @@ import (
"github.com/CycloneDX/cyclonedx-go"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/packagemetadata"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/format/internal"
"github.com/anchore/syft/syft/internal/packagemetadata"
"github.com/anchore/syft/syft/pkg"
)
@ -40,8 +40,11 @@ func EncodeComponent(p pkg.Package, supplier string, locationSorter func(a, b fi
}
componentType := cyclonedx.ComponentTypeLibrary
if p.Type == pkg.BinaryPkg {
switch p.Type {
case pkg.BinaryPkg:
componentType = cyclonedx.ComponentTypeApplication
case pkg.ModelPkg:
componentType = cyclonedx.ComponentTypeMachineLearningModel
}
return cyclonedx.Component{

View File

@ -62,7 +62,7 @@ func collectPackages(component *cyclonedx.Component, s *sbom.SBOM, idMap map[str
switch component.Type {
case cyclonedx.ComponentTypeOS:
case cyclonedx.ComponentTypeContainer:
case cyclonedx.ComponentTypeApplication, cyclonedx.ComponentTypeFramework, cyclonedx.ComponentTypeLibrary:
case cyclonedx.ComponentTypeApplication, cyclonedx.ComponentTypeFramework, cyclonedx.ComponentTypeLibrary, cyclonedx.ComponentTypeMachineLearningModel:
p := decodeComponent(component)
idMap[component.BOMRef] = p
if component.BOMRef != "" {

View File

@ -7,7 +7,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/anchore/syft/syft/internal/sourcemetadata"
"github.com/anchore/syft/internal/sourcemetadata"
"github.com/anchore/syft/syft/source"
)

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