Compare commits

..
38 Commits
Author SHA1 Message Date
Will Murphy 9673f867e5 Pass contents: read to check-gate (#4951)
Otherwise check-gate doesn't have enough permissions to do its job and
fails.

Signed-off-by: Will Murphy <willmurphyscode@users.noreply.github.com>
2026-06-02 16:05:42 -04:00
Matias Insaurralde a4fb2c0396 perf(python): hoist name normalization regexp to package level (#4926)
Avoid recompiling the separator pattern on every normalize() call during cataloging.

Signed-off-by: Matías Insaurralde <matias@insaurral.de>
2026-06-01 21:17:43 -04:00
witchcraze cf2ce643c3 update helm classifier (#4922)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2026-06-01 10:21:57 -04:00
dependabot[bot] 524a44b70d chore(deps): bump the actions-minor-patch group across 1 directory with 6 updates (#4946)
Bumps the actions-minor-patch group with 6 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [anchore/workflows/.github/workflows/codeql.yaml](https://github.com/anchore/workflows) | `0.6.0` | `0.7.0` |
| [anchore/workflows/.github/workflows/check-version-available.yaml](https://github.com/anchore/workflows) | `0.6.0` | `0.7.0` |
| [anchore/workflows/.github/workflows/check-gate.yaml](https://github.com/anchore/workflows) | `0.6.0` | `0.7.0` |
| [docker/login-action](https://github.com/docker/login-action) | `4.1.0` | `4.2.0` |
| [anchore/workflows/.github/workflows/release-install-script.yaml](https://github.com/anchore/workflows) | `0.6.0` | `0.7.0` |
| [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action) | `0.5.5` | `0.5.6` |



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

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

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

Updates `docker/login-action` from 4.1.0 to 4.2.0
- [Release notes](https://github.com/docker/login-action/releases)
- [Commits](https://github.com/docker/login-action/compare/4907a6ddec9925e35a0a9e82d7399ccc52663121...650006c6eb7dba73a995cc03b0b2d7f5ca915bee)

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

Updates `zizmorcore/zizmor-action` from 0.5.5 to 0.5.6
- [Release notes](https://github.com/zizmorcore/zizmor-action/releases)
- [Commits](https://github.com/zizmorcore/zizmor-action/compare/a16621b09c6db4281f81a93cb393b05dcd7b7165...5f14fd08f7cf1cb1609c1e344975f152c7ee938d)

---
updated-dependencies:
- dependency-name: anchore/workflows/.github/workflows/codeql.yaml
  dependency-version: 0.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor-patch
- dependency-name: anchore/workflows/.github/workflows/check-version-available.yaml
  dependency-version: 0.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor-patch
- dependency-name: anchore/workflows/.github/workflows/check-gate.yaml
  dependency-version: 0.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor-patch
- dependency-name: docker/login-action
  dependency-version: 4.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor-patch
- dependency-name: anchore/workflows/.github/workflows/release-install-script.yaml
  dependency-version: 0.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor-patch
- dependency-name: zizmorcore/zizmor-action
  dependency-version: 0.5.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-29 16:35:04 +00:00
witchcraze 4e86715c1a fix: improve julia classifier to find shared libs and beta versions (#4945)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2026-05-29 12:05:46 -04:00
Alex Goodman e8c6b7151e swap postgres signature check for rocky linux baseline rpm (#4941)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-26 10:11:38 -04:00
witchcraze 0fb8762f41 fix: improve deno classifier (#4939)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2026-05-25 11:58:10 -04:00
dependabot[bot] 58ddf74140 chore(deps): bump the actions-minor-patch group across 2 directories with 2 updates (#4936)
Bumps the actions-minor-patch group with 1 update in the / directory: [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action).
Bumps the actions-minor-patch group with 1 update in the /.github/actions/bootstrap directory: [anchore/go-make](https://github.com/anchore/go-make).


Updates `zizmorcore/zizmor-action` from 0.5.3 to 0.5.5
- [Release notes](https://github.com/zizmorcore/zizmor-action/releases)
- [Commits](https://github.com/zizmorcore/zizmor-action/compare/b1d7e1fb5de872772f31590499237e7cce841e8e...a16621b09c6db4281f81a93cb393b05dcd7b7165)

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

---
updated-dependencies:
- dependency-name: zizmorcore/zizmor-action
  dependency-version: 0.5.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: anchore/go-make
  dependency-version: 0.5.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: actions-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-22 13:42:36 +00:00
dependabot[bot] b5d828ee14 chore(deps): bump github.com/containerd/containerd/v2 (#4935)
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.3.0 to 2.3.1.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.3.0...v2.3.1)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-21 22:17:36 +00:00
Doug Clarke 1c4394fed0 fix: enhancement to java cataloger to consider .zap files as jar files (#4932)
* Enhancements to java cataloger to consider .zap files as jar files - Issue #4654
Signed-off-by: Doug Clarke <douglas.clarke@oracle.com>
2026-05-21 15:24:38 -04:00
anchore-oss-update-botandanchore-oss-update-bot f5c1a0befc chore(deps): update anchore dependencies (#4821)
Signed-off-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
Co-authored-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
2026-05-20 19:50:47 +00:00
dependabot[bot] b1287d45d8 chore(deps): bump github.com/go-git/go-git/v5 from 5.19.0 to 5.19.1 (#4930)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.19.0 to 5.19.1.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Changelog](https://github.com/go-git/go-git/blob/main/HISTORY.md)
- [Commits](https://github.com/go-git/go-git/compare/v5.19.0...v5.19.1)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-version: 5.19.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-19 16:11:18 +00:00
Alex Goodman d97216ff70 Remediate audit (#4929)
* remove slack notification on release

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

* restrict cache usage

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-18 15:01:37 -04:00
dependabot[bot] c09a009bda chore(deps): bump the actions-minor-patch group across 1 directory with 4 updates (#4927)
Bumps the actions-minor-patch group with 4 updates in the / directory: [anchore/workflows/.github/workflows/codeql.yaml](https://github.com/anchore/workflows), [anchore/workflows/.github/workflows/check-version-available.yaml](https://github.com/anchore/workflows), [anchore/workflows/.github/workflows/check-gate.yaml](https://github.com/anchore/workflows) and [anchore/workflows/.github/workflows/release-install-script.yaml](https://github.com/anchore/workflows).


Updates `anchore/workflows/.github/workflows/codeql.yaml` from 0.5.0 to 0.6.0
- [Commits](https://github.com/anchore/workflows/compare/e8cee3a5916cebb68cda68b54c180f43394c1910...15122524ced7906bfa9685eeae12e22647773ea6)

Updates `anchore/workflows/.github/workflows/check-version-available.yaml` from 0.4.0 to 0.6.0
- [Commits](https://github.com/anchore/workflows/compare/8b2b1caf40e03933c6807e03b99e883e2ceb5ac8...15122524ced7906bfa9685eeae12e22647773ea6)

Updates `anchore/workflows/.github/workflows/check-gate.yaml` from 0.4.0 to 0.6.0
- [Commits](https://github.com/anchore/workflows/compare/8b2b1caf40e03933c6807e03b99e883e2ceb5ac8...15122524ced7906bfa9685eeae12e22647773ea6)

Updates `anchore/workflows/.github/workflows/release-install-script.yaml` from 0.4.0 to 0.6.0
- [Commits](https://github.com/anchore/workflows/compare/8b2b1caf40e03933c6807e03b99e883e2ceb5ac8...15122524ced7906bfa9685eeae12e22647773ea6)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-18 16:13:54 +00:00
Alex Goodman d61af0abab Port to go-make (#4923)
* port to go-make

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

* refresh fixtures on running unit tests

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

* address refresh cache issues with old now-gitignored files

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

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-18 11:59:55 -04:00
anchore-oss-update-botandanchore-oss-update-bot 89cda82263 chore(deps): update CPE dictionary index (#4925)
Signed-off-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
Co-authored-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
2026-05-18 10:21:30 -04:00
dependabot[bot] ee6ace36d1 chore(deps): bump the actions-minor-patch group across 1 directory with 2 updates (#4920)
Bumps the actions-minor-patch group with 2 updates in the / directory: [runs-on/action](https://github.com/runs-on/action) and [sigstore/cosign-installer](https://github.com/sigstore/cosign-installer).


Updates `runs-on/action` from 2.1.0 to 2.1.2
- [Release notes](https://github.com/runs-on/action/releases)
- [Commits](https://github.com/runs-on/action/compare/742bf56072eb4845a0f94b3394673e4903c90ff0...d141ef83eb66d096ce8afc767e09115a65c63b60)

Updates `sigstore/cosign-installer` from 4.1.1 to 4.1.2
- [Release notes](https://github.com/sigstore/cosign-installer/releases)
- [Commits](https://github.com/sigstore/cosign-installer/compare/cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003...6f9f17788090df1f26f669e9d70d6ae9567deba6)

---
updated-dependencies:
- dependency-name: runs-on/action
  dependency-version: 2.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: sigstore/cosign-installer
  dependency-version: 4.1.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-15 13:34:58 +00:00
witchcraze e2e5e223ab feat: mysqld, ndbd, ndbmtd and ndb_mgmd classifier (#4907)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2026-05-14 11:29:42 -04:00
4579d11abc fix: detect compressed kernel modules (.ko.gz, .ko.xz, .ko.zst) (#4740)
* fix: detect compressed kernel modules (.ko.gz, .ko.xz, .ko.zst)

The linux-kernel-cataloger only matched plain *.ko files, missing
compressed modules produced when CONFIG_MODULE_COMPRESS is enabled
(common on Debian 13 / Ubuntu 24.04+). This resulted in near-zero
module packages being reported for such filesystems.

Changes:
- Add *.ko.gz, *.ko.xz, *.ko.zst glob patterns to both the cataloger
  and capabilities.yaml so the file resolver picks up compressed modules
- Add decompressedModuleReader() which detects the extension and
  transparently decompresses via compress/gzip, ulikunitz/xz, or
  klauspost/compress/zstd before handing the ELF bytes to the existing
  parseLinuxKernelModuleMetadata parser
- Promote github.com/klauspost/compress from indirect to direct dependency
- Add unit tests covering all three compression formats plus the
  uncompressed baseline, using a programmatically generated minimal ELF

Fixes #4721

Signed-off-by: Will Bates <william.bates11@outlook.com>

* address reading archives into memory

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

---------

Signed-off-by: Will Bates <william.bates11@outlook.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Will Bates <william.bates11@outlook.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-13 13:44:18 -04:00
anchore-oss-update-botandanchore-oss-update-bot 07ae2ca08d chore(deps): update CPE dictionary index (#4909)
Signed-off-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
Co-authored-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
2026-05-13 10:03:11 -04:00
36969bdeff fix: Allow duplicates in Yarn "Berry" files (#4691) (#4838)
* fix: Allow duplicates in Yarn "Berry" files (#4691)

Yarn lockfiles can have multiple versions resolved for the same package
name. We correctly allow this in Yarn v1 lockfiles but the "Berry"
YAML-format lockfiles were doing deduplication by package name. This
change removes that deduplication.

Signed-off-by: Calum Leslie <cleslie@atlassian.com>

* fix linting

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

---------

Signed-off-by: Calum Leslie <cleslie@atlassian.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Calum Leslie <cleslie@atlassian.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-11 21:10:17 +00:00
Alex Goodman dfb6011083 pin and update fixture versions (#4913)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-11 16:30:35 -04:00
Alex Goodman 997a486e22 use released shared workflow (#4914)
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-11 16:21:41 -04:00
dependabot[bot] 4f64fbc004 chore(deps): bump github.com/go-git/go-git/v5 from 5.18.0 to 5.19.0 (#4911)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.18.0 to 5.19.0.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Changelog](https://github.com/go-git/go-git/blob/main/HISTORY.md)
- [Commits](https://github.com/go-git/go-git/compare/v5.18.0...v5.19.0)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-version: 5.19.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-11 16:25:44 +00:00
Alex Goodman 87d6a288d7 Tighten workflow permissions and update release shape (#4899)
* Rework release workflow to canonical shape

Replace the custom quality-gate job with the reusable check-version-available
and check-gate workflows from anchore/workflows. Remove the phase
workflow_dispatch input; the install-script-only path is now a standalone
workflow (release-install-script.yaml) that can be triggered independently.

- add version-available and check-gate jobs using pinned anchore/workflows SHA
- remove phase input and quality-gate job
- release job now needs [check-gate, version-available]
- release-install-script job no longer conditionally skips based on phase
- add release-install-script.yaml for standalone install script runs
- set permissions: {} at workflow level (contents pushed to release job)
- add concurrency: group: release

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

* Tighten workflow-level permissions to {}

Change top-level permissions from contents: read to {} in validations.yaml
and validate-github-actions.yaml, pushing the needed contents: read down
to each job that performs a checkout.

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

* keep install script phase, remove workflow

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

* remove schema detection workflow

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

---------

Signed-off-by: wagoodman <wagoodman@gmail.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-08 17:16:31 -04:00
dependabot[bot] 20987d30d0 chore(deps): bump the actions-minor-patch group across 1 directory with 2 updates (#4897)
Bumps the actions-minor-patch group with 2 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action) and [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action).


Updates `github/codeql-action` from 4.35.2 to 4.35.3
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/95e58e9a2cdfd71adc6e0353d5c52f41a045d225...e46ed2cbd01164d986452f91f178727624ae40d7)

Updates `slackapi/slack-github-action` from 3.0.2 to 3.0.3
- [Release notes](https://github.com/slackapi/slack-github-action/releases)
- [Changelog](https://github.com/slackapi/slack-github-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/slackapi/slack-github-action/compare/03ea5433c137af7c0495bc0cad1af10403fc800c...45a88b9581bfab2566dc881e2cd66d334e621e2c)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: slackapi/slack-github-action
  dependency-version: 3.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-08 13:38:31 +00:00
witchcraze e2007d9bf2 feat: add aws-lc classifier (#4882)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2026-05-06 16:43:34 -04:00
ChrisJr404andChris 4f0e32ab51 binary classifier: detect elixir release-candidate versions (#4851)
The elixir-binary and elixir-library classifiers' regexes only matched
the bare semver triplet (and a single sub-segment for the library), so
release-candidate elixir images were either missed entirely or had
their version truncated:

  $ syft -q elixir:1.12.0-rc | grep elixir   # nothing
  $ syft -q elixir:1.13.0-rc.0 | grep elixir
  elixir   1.13.0   binary                   # truncated, "-rc.0" lost

Extend the version capture group to optionally include
"-<a-z0-9>+(\\.<digits>)?" so "1.12.0-rc.1", "1.13.0-rc.0", etc. match
exactly as the elixir.app and the binary's ELIXIR_VERSION line have
them.

Add a logical fixture under testdata/classifiers/snippets/elixir/
1.12.0-rc.1/linux-amd64 (cloned from the existing 1.19.1 fixture with
just the version strings changed) and register it in
Test_Cataloger_PositiveCases.

Closes #4819

Signed-off-by: Chris (ChrisJr404) <11917633+ChrisJr404@users.noreply.github.com>
Co-authored-by: Chris (ChrisJr404) <11917633+ChrisJr404@users.noreply.github.com>
2026-05-06 15:14:09 +00:00
witchcraze 605391114c add ingress-nginx classifier (#4857)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2026-05-06 14:54:20 +00:00
ChrisJr404 1caf243d29 fix(source): treat exclude paths with trailing slash as directories (#4892)
A trailing slash on --exclude (e.g. './lib/') is dropped during pattern
normalization but doublestar.Match still requires an exact string match,
so the resulting pattern silently matches nothing and the directory is
not excluded. Strip a trailing slash so './lib/' and './lib' behave the
same.

Fixes #4839

Signed-off-by: ChrisJr404 <chris@hacknow.com>
2026-05-06 14:51:41 +00:00
PGrayandPGray 48e91312e8 fix(dotnet): align runtime CPEs with NVD (#4743)
Signed-off-by: PGray <PGrayCS@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: PGray <PGrayCS@users.noreply.github.com>
2026-05-06 13:07:49 +00:00
d81df67493 fix(debian): only parse machine-readable copyright files with Format header (#4754)
* fix(debian): only parse machine-readable copyright files with Format header

Only parse debian/copyright files as machine-readable DEP-5 format when
they contain the mandatory Format header field pointing to the copyright
specification URI. Files without this header are free-form text and
should not have License: regex patterns applied to them, which previously
produced nonsensical results like "#", "Permission", "This", "see" for
non-machine-readable files.

The fallback license classifier in the debian cataloger will handle
non-machine-readable files by doing full-text license identification.

Closes #4708

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

* decompose parseLicensesFromCopyright to address linting issues

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

---------

Signed-off-by: Bahtya <bahtya@users.noreply.github.com>
Signed-off-by: Bahtya <bahtayr@gmail.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Bahtya <bahtayr@gmail.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-06 13:02:27 +00:00
dependabot[bot] 47cda2b5ef chore(deps): bump the actions-minor-patch group across 2 directories with 5 updates (#4846)
Bumps the actions-minor-patch group with 4 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action), [marocchino/sticky-pull-request-comment](https://github.com/marocchino/sticky-pull-request-comment), [slackapi/slack-github-action](https://github.com/slackapi/slack-github-action) and [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action).
Bumps the actions-minor-patch group with 1 update in the /.github/actions/bootstrap directory: [actions/cache](https://github.com/actions/cache).


Updates `github/codeql-action` from 4.35.1 to 4.35.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/c10b8064de6f491fea524254123dbe5e09572f13...95e58e9a2cdfd71adc6e0353d5c52f41a045d225)

Updates `marocchino/sticky-pull-request-comment` from 3.0.3 to 3.0.4
- [Release notes](https://github.com/marocchino/sticky-pull-request-comment/releases)
- [Commits](https://github.com/marocchino/sticky-pull-request-comment/compare/d4d6b0936434b21bc8345ad45a440c5f7d2c40ff...0ea0beb66eb9baf113663a64ec522f60e49231c0)

Updates `slackapi/slack-github-action` from 3.0.1 to 3.0.2
- [Release notes](https://github.com/slackapi/slack-github-action/releases)
- [Changelog](https://github.com/slackapi/slack-github-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/slackapi/slack-github-action/compare/af78098f536edbc4de71162a307590698245be95...03ea5433c137af7c0495bc0cad1af10403fc800c)

Updates `zizmorcore/zizmor-action` from 0.5.2 to 0.5.3
- [Release notes](https://github.com/zizmorcore/zizmor-action/releases)
- [Commits](https://github.com/zizmorcore/zizmor-action/compare/71321a20a9ded102f6e9ce5718a2fcec2c4f70d8...b1d7e1fb5de872772f31590499237e7cce841e8e)

Updates `actions/cache` from 5.0.4 to 5.0.5
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/668228422ae6a00e4ad889ee87cd7109ec5666a7...27d5ce7f107fe9357f9df03efb73ab90386fccae)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.35.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: marocchino/sticky-pull-request-comment
  dependency-version: 3.0.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: slackapi/slack-github-action
  dependency-version: 3.0.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: zizmorcore/zizmor-action
  dependency-version: 0.5.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
- dependency-name: actions/cache
  dependency-version: 5.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: actions-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-05-05 11:42:04 -04:00
Rayan Salhabandcyphercodes ae711963d1 fix: parse arbitrary equality python requirements (#4835)
Signed-off-by: cyphercodes <cyphercodes@users.noreply.github.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: cyphercodes <cyphercodes@users.noreply.github.com>
2026-05-05 13:49:03 +00:00
Alex Goodman f878197150 chore: remove common workflows (#4881)
Removes deprecated common workflows now centralized elsewhere.

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
2026-05-04 14:31:07 -04:00
witchcraze 514efb03e0 fix: prevent redis classifier from detecting valkey (#4619)
Signed-off-by: witchcraze <witchcraze@gmail.com>
2026-05-04 14:07:29 -04:00
anchore-oss-update-botandanchore-oss-update-bot 1e4f424f09 chore(deps): update CPE dictionary index (#4831)
Signed-off-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
Co-authored-by: anchore-oss-update-bot <anchore-oss-update-bot@users.noreply.github.com>
2026-05-04 10:14:08 -04:00
ChrisJr404 b5f0877967 fix: map "nuget" purl type to DotnetPkg in TypeByName (#4848)
Signed-off-by: Chris (ChrisJr404) <11917633+ChrisJr404@users.noreply.github.com>
2026-05-04 08:51:19 -04:00
110 changed files with 3249 additions and 1349 deletions
+5 -101
View File
@@ -1,67 +1,11 @@
# only pull in version updates that were released more than a week ago (low-pass filter for quickly-retracted releases)
cooldown: 7d
# Most tools (binny, chronicle, cosign, golangci-lint, goreleaser, gosimports,
# bouncer, quill, syft, task, gh) are inherited from anchore/go-make's embedded
# .binny.yaml — see https://github.com/anchore/go-make. Only syft-specific tools
# or version overrides should live here.
tools:
## internal tools ############################################################################
# we want to use a pinned version of binny to manage the toolchain (so binny manages itself!)
- name: binny
version:
want: v0.13.0
method: github-release
with:
repo: anchore/binny
# used to produce SBOMs during release
- name: syft
version:
want: v1.42.3
method: github-release
with:
repo: anchore/syft
# used to sign mac binaries at release
- name: quill
version:
want: v0.7.1
method: github-release
with:
repo: anchore/quill
# used at release to generate the changelog
- name: chronicle
version:
want: v0.8.0
method: github-release
with:
repo: anchore/chronicle
## external tools ############################################################################
# used for linting
- name: golangci-lint
version:
want: v2.11.4
method: github-release
with:
repo: golangci/golangci-lint
# used for showing the changelog at release
- name: glow
version:
want: v2.1.1
method: github-release
with:
repo: charmbracelet/glow
# used for signing the checksums file at release
- name: cosign
version:
want: v3.0.5
method: github-release
with:
repo: sigstore/cosign
# used in integration tests to verify JSON schemas
- name: yajsv
version:
@@ -70,46 +14,6 @@ tools:
with:
repo: neilpa/yajsv
# used to release all artifacts
- name: goreleaser
version:
want: v2.15.2
method: github-release
with:
repo: goreleaser/goreleaser
# used for organizing imports during static analysis
- name: gosimports
version:
want: v0.3.8
method: github-release
with:
repo: rinchsan/gosimports
# used during static analysis for license compliance
- name: bouncer
version:
want: v0.4.0
method: github-release
with:
repo: wagoodman/go-bouncer
# used for running all local and CI tasks
- name: task
version:
want: v3.49.1
method: github-release
with:
repo: go-task/task
# used for triggering a release
- name: gh
version:
want: v2.89.0
method: github-release
with:
repo: cli/cli
# used to upload test fixture cache
- name: oras
version:
@@ -118,7 +22,7 @@ tools:
with:
repo: oras-project/oras
# used to upload test fixture cache
# used to parse JSON/YAML annotations on the fixture cache image
- name: yq
version:
want: v4.52.5
+35 -34
View File
@@ -1,55 +1,45 @@
name: "Bootstrap"
description: "Bootstrap all tools and dependencies"
description: "Bootstrap all syft tools and dependencies on top of go-make's setup action"
# This action is a thin wrapper around anchore/go-make/.github/actions/setup which
# already handles checkout, setup-go, restore-only build/mod cache, and tool cache.
# We add the syft-specific extras here: apt packages and the test fixture cache.
inputs:
go-version:
description: "Go version to install"
description: "Go version to install (passed to go-make/setup)"
required: true
default: "1.26.2"
go-dependencies:
description: "Download go dependencies"
cache-key-prefix:
description: "Prefix all cache keys with this value (passed to go-make/setup)"
required: true
default: "v1"
cache-enabled:
description: "Enable build/mod and tool caching (passed to go-make/setup)"
required: true
default: "true"
cache-key-prefix:
description: "Prefix all cache keys with this value"
required: true
default: "53ac821810"
download-test-fixture-cache:
description: "Download test fixture cache from OCI and github actions"
required: true
default: "false"
tools:
description: "whether to install tools"
default: "true"
bootstrap-apt-packages:
description: "Space delimited list of tools to install via apt"
default: "libxml2-utils"
runs:
using: "composite"
steps:
# note: go mod and build is automatically cached on default with v4+
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
if: inputs.go-version != ''
- name: Setup go + go-make tooling
uses: anchore/go-make/.github/actions/setup@9de27be11ed73e2f9d5406a836a492b7d8aa1225 # v0.5.0
with:
go-version: ${{ inputs.go-version }}
check-latest: true
- name: Restore tool cache
if: inputs.tools == 'true'
id: tool-cache
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
with:
path: ${{ github.workspace }}/.tool
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
- name: Install project tools
cache-key-prefix: ${{ inputs.cache-key-prefix }}
cache-enabled: ${{ inputs.cache-enabled }}
- name: Install binny-managed tools
shell: bash
if: inputs.tools == 'true'
run: |
make tools
.tool/binny list
.tool/binny check
- name: Install go dependencies
if: inputs.go-dependencies == 'true'
shell: bash
run: make ci-bootstrap-go
run: make binny:install
- name: Install apt packages
if: inputs.bootstrap-apt-packages != ''
shell: bash
@@ -58,12 +48,23 @@ runs:
run: |
IFS=' ' read -ra packages <<< "$APT_PACKAGES"
DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y "${packages[@]}"
# ORAS cache: restore-only on non-default branches / forks
- name: Restore ORAS cache from github actions
if: inputs.download-test-fixture-cache == 'true'
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
if: ${{ inputs.download-test-fixture-cache == 'true' && (github.ref != format('refs/heads/{0}', github.event.repository.default_branch) || github.event.repository.fork == true) }}
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ${{ github.workspace }}/.tmp/oras-cache
key: ${{ inputs.cache-key-prefix }}-oras-cache
# ORAS cache: restore + save on the default branch of the canonical repo only.
- name: Restore and save ORAS cache from github actions
if: ${{ inputs.download-test-fixture-cache == 'true' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.event.repository.fork == false }}
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
with:
path: ${{ github.workspace }}/.tmp/oras-cache
key: ${{ inputs.cache-key-prefix }}-oras-cache
- name: Download test fixture cache
if: inputs.download-test-fixture-cache == 'true'
shell: bash
+3 -1
View File
@@ -14,7 +14,9 @@ version: 2
updates:
- package-ecosystem: gomod
directory: "/"
directories:
- "/"
- "/.make"
cooldown:
default-days: 7
schedule:
-11
View File
@@ -1,11 +0,0 @@
#!/usr/bin/env bash
red=$(tput setaf 1)
bold=$(tput bold)
normal=$(tput sgr0)
# assert we are running in CI (or die!)
if [[ -z "$CI" ]]; then
echo "${bold}${red}This step should ONLY be run in CI. Exiting...${normal}"
exit 1
fi
-36
View File
@@ -1,36 +0,0 @@
#!/usr/bin/env python3
import subprocess
import sys
import shlex
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKCYAN = '\033[96m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
if len(sys.argv) < 3:
print("Usage: coverage.py [threshold] [go-coverage-report]")
sys.exit(1)
threshold = float(sys.argv[1])
report = sys.argv[2]
args = shlex.split(f"go tool cover -func {report}")
p = subprocess.run(args, capture_output=True, text=True)
percent_coverage = float(p.stdout.splitlines()[-1].split()[-1].replace("%", ""))
print(f"{bcolors.BOLD}Coverage: {percent_coverage}%{bcolors.ENDC}")
if percent_coverage < threshold:
print(f"{bcolors.BOLD}{bcolors.FAIL}Coverage below threshold of {threshold}%{bcolors.ENDC}")
sys.exit(1)
+51 -34
View File
@@ -1,35 +1,45 @@
#!/usr/bin/env python3
from __future__ import annotations
import os
import glob
import sys
import json
import hashlib
import json
import os
import sys
IGNORED_PREFIXES = []
def find_fingerprints_and_check_dirs(base_dir):
all_fingerprints = set(glob.glob(os.path.join(base_dir, '**', 'test*', '**', '*.fingerprint'), recursive=True))
all_fingerprints = set(
glob.glob(
os.path.join(base_dir, "**", "test*", "**", "*.fingerprint"), recursive=True
)
)
all_fingerprints = {os.path.relpath(fp) for fp in all_fingerprints
if not any(fp.startswith(prefix) for prefix in IGNORED_PREFIXES)}
all_fingerprints = {
os.path.relpath(fp)
for fp in all_fingerprints
if not any(fp.startswith(prefix) for prefix in IGNORED_PREFIXES)
}
if not all_fingerprints:
show("No .fingerprint files or cache directories found.")
exit(1)
missing_content = []
orphan_fingerprints = []
empty_content = []
valid_paths = set()
fingerprint_contents = []
for fingerprint in all_fingerprints:
path = fingerprint.replace('.fingerprint', '')
path = fingerprint.replace(".fingerprint", "")
if not os.path.exists(path):
missing_content.append(path)
# paired content path is entirely missing — the .fingerprint is likely
# leftover from a moved/deleted source (testdata trees are git-ignored,
# so they persist locally across rename refactors)
orphan_fingerprints.append(fingerprint)
continue
if not os.path.isdir(path):
@@ -39,13 +49,13 @@ def find_fingerprints_and_check_dirs(base_dir):
if os.listdir(path):
valid_paths.add(path)
else:
missing_content.append(path)
empty_content.append(path)
with open(fingerprint, 'r') as f:
with open(fingerprint, "r") as f:
content = f.read().strip()
fingerprint_contents.append((fingerprint, content))
return sorted(valid_paths), missing_content, fingerprint_contents
return sorted(valid_paths), empty_content, orphan_fingerprints, fingerprint_contents
def parse_fingerprint_contents(fingerprint_content):
@@ -59,7 +69,9 @@ def parse_fingerprint_contents(fingerprint_content):
def calculate_sha256(fingerprint_contents):
sorted_fingerprint_contents = sorted(fingerprint_contents, key=lambda x: x[0])
concatenated_contents = ''.join(content for _, content in sorted_fingerprint_contents)
concatenated_contents = "".join(
content for _, content in sorted_fingerprint_contents
)
sha256_hash = hashlib.sha256(concatenated_contents.encode()).hexdigest()
@@ -68,7 +80,7 @@ def calculate_sha256(fingerprint_contents):
def calculate_file_sha256(file_path):
sha256_hash = hashlib.sha256()
with open(file_path, 'rb') as f:
with open(file_path, "rb") as f:
for byte_block in iter(lambda: f.read(4096), b""):
sha256_hash.update(byte_block)
return sha256_hash.hexdigest()
@@ -79,17 +91,28 @@ def show(*s: str):
def main(file_path: str | None):
base_dir = '.'
valid_paths, missing_content, fingerprint_contents = find_fingerprints_and_check_dirs(base_dir)
base_dir = "."
valid_paths, empty_content, orphan_fingerprints, fingerprint_contents = (
find_fingerprints_and_check_dirs(base_dir)
)
if missing_content:
show("The following paths are missing or have no content, but have corresponding .fingerprint files:")
for path in sorted(missing_content):
if empty_content:
show(
"The following paths exist but are empty, and have corresponding .fingerprint files:"
)
for path in sorted(empty_content):
show(f"- {path}")
# when adding new cache directories there is a time where it is not possible to have this directory without
# running the tests first... but this step is a prerequisite for running the tests. We should not block on this.
# show("Please ensure these paths exist and have content if they are directories.")
# exit(1)
if orphan_fingerprints:
show(
"The following .fingerprint files reference paths that no longer exist "
"(likely leftover from a moved/deleted cataloger — safe to delete, "
"or run `task prune-orphan-fingerprints`):"
)
for fp in sorted(orphan_fingerprints):
show(f"- {fp}")
sha256_hash = calculate_sha256(fingerprint_contents)
@@ -101,30 +124,24 @@ def main(file_path: str | None):
file_digest = calculate_file_sha256(fingerprint_file)
# Parse the fingerprint file to get the digest/path tuples
with open(fingerprint_file, 'r') as f:
with open(fingerprint_file, "r") as f:
fingerprint_content = f.read().strip()
input_map = parse_fingerprint_contents(fingerprint_content)
paths_with_digests.append({
"path": path,
"digest": file_digest,
"input": input_map
})
paths_with_digests.append(
{"path": path, "digest": file_digest, "input": input_map}
)
except Exception as e:
show(f"Error processing {fingerprint_file}: {e}")
raise e
output = {
"digest": sha256_hash,
"paths": paths_with_digests
}
output = {"digest": sha256_hash, "paths": paths_with_digests}
content = json.dumps(output, indent=2, sort_keys=True)
if file_path:
with open(file_path, 'w') as f:
with open(file_path, "w") as f:
f.write(content)
print(content)
-30
View File
@@ -1,30 +0,0 @@
#!/usr/bin/env bash
set -eu
ORIGINAL_STATE_DIR=$(mktemp -d "TEMP-original-state-XXXXXXXXX")
TIDY_STATE_DIR=$(mktemp -d "TEMP-tidy-state-XXXXXXXXX")
trap "cp -p ${ORIGINAL_STATE_DIR}/* ./ && git update-index -q --refresh && rm -fR ${ORIGINAL_STATE_DIR} ${TIDY_STATE_DIR}" EXIT
# capturing original state of files...
cp go.mod go.sum "${ORIGINAL_STATE_DIR}"
# capturing state of go.mod and go.sum after running go mod tidy...
go mod tidy
cp go.mod go.sum "${TIDY_STATE_DIR}"
set +e
# detect difference between the git HEAD state and the go mod tidy state
DIFF_MOD=$(diff -u "${ORIGINAL_STATE_DIR}/go.mod" "${TIDY_STATE_DIR}/go.mod")
DIFF_SUM=$(diff -u "${ORIGINAL_STATE_DIR}/go.sum" "${TIDY_STATE_DIR}/go.sum")
if [[ -n "${DIFF_MOD}" || -n "${DIFF_SUM}" ]]; then
echo "go.mod diff:"
echo "${DIFF_MOD}"
echo "go.sum diff:"
echo "${DIFF_SUM}"
echo ""
printf "FAILED! go.mod and/or go.sum are NOT tidy; please run 'go mod tidy'.\n\n"
exit 1
fi
+102
View File
@@ -0,0 +1,102 @@
#!/usr/bin/env python3
"""
Remove orphan *.fingerprint files left behind by moved/deleted catalogers.
A fingerprint is considered orphaned when:
1. its paired content path (the fingerprint path with `.fingerprint` stripped)
does not exist, AND
2. the nearest ancestor `testdata/` directory has no `Makefile` claiming
responsibility for generating that path.
The second condition is the safety check: if there is a Makefile, the
fingerprint is "live" and might just be waiting for fixtures to be built —
leave it alone. Without a Makefile, nothing in-repo will ever regenerate
the content, so the fingerprint is dead weight that triggers spurious
"missing path" warnings.
Empty parent directories are also pruned after removing the fingerprint.
Use --dry-run to preview without deleting.
"""
from __future__ import annotations
import argparse
import glob
import os
import sys
def find_ancestor_testdata(path: str) -> str | None:
d = os.path.dirname(path)
while d and d not in (".", os.sep):
if os.path.basename(d) == "testdata":
return d
d = os.path.dirname(d)
return None
def is_orphan(fingerprint: str) -> bool:
paired = fingerprint[: -len(".fingerprint")]
if os.path.exists(paired):
return False
testdata_dir = find_ancestor_testdata(fingerprint)
if testdata_dir and os.path.isfile(os.path.join(testdata_dir, "Makefile")):
# a Makefile exists that may regenerate this — not safe to prune
return False
return True
def prune_empty_parents(start: str, stop_at: str = ".") -> list[str]:
removed = []
d = os.path.dirname(start)
stop_at = os.path.abspath(stop_at)
while d and os.path.abspath(d) != stop_at:
try:
if not os.listdir(d):
os.rmdir(d)
removed.append(d)
d = os.path.dirname(d)
else:
break
except OSError:
break
return removed
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--dry-run",
action="store_true",
help="Show what would be removed without deleting anything",
)
args = parser.parse_args()
all_fingerprints = glob.glob("**/test*/**/*.fingerprint", recursive=True)
orphans = sorted(fp for fp in all_fingerprints if is_orphan(fp))
if not orphans:
print("no orphan fingerprints found")
return 0
verb = "would remove" if args.dry_run else "removing"
print(f"{verb} {len(orphans)} orphan fingerprint(s):")
for fp in orphans:
print(f"- {fp}")
if args.dry_run:
continue
try:
os.remove(fp)
except OSError as e:
print(f" ! failed to remove: {e}", file=sys.stderr)
continue
for d in prune_empty_parents(fp):
print(f" (also removed empty dir {d})")
return 0
if __name__ == "__main__":
sys.exit(main())
-57
View File
@@ -1,57 +0,0 @@
#!/usr/bin/env bash
set -eu
bold=$(tput bold)
normal=$(tput sgr0)
GH_CLI=.tool/gh
if ! [ -x "$(command -v $GH_CLI)" ]; then
echo "The GitHub CLI could not be found. run: make bootstrap"
exit 1
fi
$GH_CLI auth status
# set the default repo in cases where multiple remotes are defined
$GH_CLI repo set-default anchore/syft
export GITHUB_TOKEN="${GITHUB_TOKEN-"$($GH_CLI auth token)"}"
# we need all of the git state to determine the next version. Since tagging is done by
# the release pipeline it is possible to not have all of the tags from previous releases.
git fetch --tags
# populates the CHANGELOG.md and VERSION files
echo "${bold}Generating changelog...${normal}"
make changelog 2> /dev/null
NEXT_VERSION=$(cat VERSION)
if [[ "$NEXT_VERSION" == "" || "${NEXT_VERSION}" == "(Unreleased)" ]]; then
echo "Could not determine the next version to release. Exiting..."
exit 1
fi
while true; do
read -p "${bold}Do you want to trigger a release for version '${NEXT_VERSION}'?${normal} [y/n] " yn
case $yn in
[Yy]* ) echo; break;;
[Nn]* ) echo; echo "Cancelling release..."; exit;;
* ) echo "Please answer yes or no.";;
esac
done
echo "${bold}Kicking off release for ${NEXT_VERSION}${normal}..."
echo
$GH_CLI workflow run release.yaml -f version=${NEXT_VERSION}
echo
echo "${bold}Waiting for release to start...${normal}"
sleep 10
set +e
echo "${bold}Head to the release workflow to monitor the release:${normal} $($GH_CLI run list --workflow=release.yaml --limit=1 --json url --jq '.[].url')"
id=$($GH_CLI run list --workflow=release.yaml --limit=1 --json databaseId --jq '.[].databaseId')
$GH_CLI run watch $id --exit-status || (echo ; echo "${bold}Logs of failed step:${normal}" && GH_PAGER="" $GH_CLI run view $id --log-failed)
+4 -64
View File
@@ -1,6 +1,3 @@
# CodeQL scans for security vulnerabilities and coding errors across all
# languages in this repo. Results appear in the "Security" tab under
# "Code scanning alerts" and are enforced by branch protection rules.
name: "CodeQL"
on:
@@ -8,74 +5,17 @@ on:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
# Weekly scheduled scan catches newly disclosed vulnerabilities in
# existing code, not just changes introduced by PRs.
schedule:
- cron: '38 11 * * 3'
permissions: {}
jobs:
analyze:
name: Analyze (${{ matrix.language }})
runs-on: ubuntu-latest
name: Analyze
uses: anchore/workflows/.github/workflows/codeql.yaml@b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5 # v0.7.0
permissions:
# Required to upload SARIF results to the "Security" tab.
security-events: write
# Required to fetch internal or private CodeQL packs.
packages: read
# Only required for workflows in private repositories.
actions: read
contents: read
strategy:
fail-fast: false
matrix:
include:
# GitHub Actions workflow linting — no build needed.
- language: actions
build-mode: none
# Go uses "manual" build mode so we control exactly what gets
# compiled. The default "autobuild" finds the Makefile and runs
# the full CI pipeline (lint, test, snapshot release, etc.),
# which is far more work than CodeQL needs. All it requires is
# compiled Go source so it can build a type-resolved code graph
# for analysis.
- language: go
build-mode: manual
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
# Pin the Go toolchain to whatever go.mod declares so CodeQL
# analyzes with the same version the project actually uses.
# Only runs for the Go matrix entry.
- name: Setup Go
if: matrix.language == 'go'
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
with:
go-version-file: go.mod
- name: Initialize CodeQL
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
# Minimal build for Go: compile all packages so CodeQL gets a full
# type-resolved code graph for analysis.
- name: Build (Go)
if: matrix.build-mode == 'manual'
shell: bash
run: go build ./...
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
with:
# The category tag lets GitHub associate SARIF results with the
# correct language when branch protection checks for required
# code scanning results.
category: "/language:${{matrix.language}}"
@@ -1,10 +0,0 @@
name: Dependabot Automation
on:
pull_request:
permissions:
pull-requests: write
jobs:
run:
uses: anchore/workflows/.github/workflows/dependabot-automation.yaml@main
@@ -1,64 +0,0 @@
# Note: this workflow has been disabled manually in the UI and will be replaced in short order
name: "Detect schema changes"
on:
# IMPORTANT! This workflow is triggered by the `pull_request_target` event
# which means that forked PRs will run with access secrets from the repo
# it's forked from (the "target" repo).
#
# For this reason we only NEVER checkout the code from the pull request
# (e.g. "ref: ${{ github.event.pull_request.head.sha }}") to prevent
# accidentally running potentially untrusted code.
#
# By default the checkout will be:
# - GITHUB_SHA: Last commit on the PR base branch
# - GITHUB_REF: PR base branch
#
# ...unlike a typical PR where:
# - GITHUB_SHA: Last merge commit on the GITHUB_REF branch
# - GITHUB_REF: PR merge branch refs/pull/:prNumber/merge
pull_request_target:
env:
# note: this is used within hashFiles() so must be within the GITHUB_WORKSPACE path (or will silently fail)
CI_COMMENT_FILE: .tmp/labeler-comment.txt
# needs to be any string to uniquely identify the comment on a PR across multiple runs
COMMENT_HEADER: "label-commentary"
jobs:
label:
name: "Label changes"
runs-on: ubuntu-22.04
permissions:
contents: read
pull-requests: write
issues: write
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
persist-credentials: false
repository: anchore/syft # IMPORTANT! An additional protection that this is checking out code from the expected repository
ref: main # IMPORTANT! It is CRITICAL that this only ever considers the code from main and NEVER EVER from a fork.
- run: python .github/scripts/labeler.py
env:
# note: this token has write access to the repo
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITHUB_PR_NUMBER: ${{ github.event.number }}
- name: Delete existing comment
if: ${{ hashFiles( env.CI_COMMENT_FILE ) == '' }}
uses: marocchino/sticky-pull-request-comment@d4d6b0936434b21bc8345ad45a440c5f7d2c40ff #v3.0.3
with:
header: ${{ env.COMMENT_HEADER }}
hide: true
hide_classify: "OUTDATED"
- name: Add comment
if: ${{ hashFiles( env.CI_COMMENT_FILE ) != '' }}
uses: marocchino/sticky-pull-request-comment@d4d6b0936434b21bc8345ad45a440c5f7d2c40ff #v3.0.3
with:
header: ${{ env.COMMENT_HEADER }}
path: ${{ env.CI_COMMENT_FILE }}
@@ -1,18 +0,0 @@
name: Add to OSS board
permissions:
contents: read
on:
issues:
types:
- opened
- reopened
- transferred
- labeled
jobs:
run:
uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main"
secrets:
token: ${{ secrets.OSS_PROJECT_GH_TOKEN }}
+37 -145
View File
@@ -1,7 +1,11 @@
name: "Release"
permissions:
contents: read
permissions: {}
# there should never be two releases in progress at the same time
concurrency:
group: release
cancel-in-progress: false
on:
workflow_dispatch:
@@ -19,116 +23,30 @@ on:
- "install-script-only"
jobs:
quality-gate:
environment: release
version-available:
if: ${{ github.event.inputs.phase == 'all' }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
persist-credentials: false
permissions:
contents: read # required for fetching tags
uses: anchore/workflows/.github/workflows/check-version-available.yaml@b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5 # v0.7.0
with:
version: ${{ github.event.inputs.version }}
- 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:
# --certificate-identity-regexp "https://github.com/anchore/.github/workflows/release.yaml@refs/heads/main"
# if we are not on the main branch, the signature will not be verifiable since the suffix requires the main branch
# at the time of when the OIDC token was issued on the Github Actions runner.
run: echo "This can only be run on the main branch otherwise releases produced will not be verifiable with cosign" && exit 1
- name: Check if tag already exists
# note: this will fail if the tag already exists
run: |
[[ "$VERSION" == v* ]] || (echo "version '$VERSION' does not have a 'v' prefix" && exit 1)
git tag "$VERSION"
env:
VERSION: ${{ github.event.inputs.version }}
- name: Check static analysis results
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: static-analysis
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
checkName: "Static analysis"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check unit test results
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: unit
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
checkName: "Unit tests"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check integration test results
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: integration
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
checkName: "Integration tests"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check acceptance test results (linux)
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: acceptance-linux
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
checkName: "Acceptance tests (Linux)"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check acceptance test results (mac)
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: acceptance-mac
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
checkName: "Acceptance tests (Mac)"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Check cli test results (linux)
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
id: cli-linux
with:
token: ${{ secrets.GITHUB_TOKEN }}
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
checkName: "CLI tests (Linux)"
ref: ${{ github.event.pull_request.head.sha || github.sha }}
- name: Quality gate
if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' || steps.integration.outputs.conclusion != 'success' || steps.cli-linux.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success'
env:
STATIC_ANALYSIS_STATUS: ${{ steps.static-analysis.conclusion }}
UNIT_TEST_STATUS: ${{ steps.unit.outputs.conclusion }}
INTEGRATION_TEST_STATUS: ${{ steps.integration.outputs.conclusion }}
ACCEPTANCE_LINUX_STATUS: ${{ steps.acceptance-linux.outputs.conclusion }}
ACCEPTANCE_MAC_STATUS: ${{ steps.acceptance-mac.outputs.conclusion }}
CLI_LINUX_STATUS: ${{ steps.cli-linux.outputs.conclusion }}
run: |
echo "Static Analysis Status: $STATIC_ANALYSIS_STATUS"
echo "Unit Test Status: $UNIT_TEST_STATUS"
echo "Integration Test Status: $INTEGRATION_TEST_STATUS"
echo "Acceptance Test (Linux) Status: $ACCEPTANCE_LINUX_STATUS"
echo "Acceptance Test (Mac) Status: $ACCEPTANCE_MAC_STATUS"
echo "CLI Test (Linux) Status: $CLI_LINUX_STATUS"
false
check-gate:
if: ${{ github.event.inputs.phase == 'all' }}
permissions:
contents: read
checks: read # required for getting the status of specific check names
uses: anchore/workflows/.github/workflows/check-gate.yaml@b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5 # v0.7.0
with:
# these are checks that should be run on pull-request and merges to main.
# we do NOT want to kick off a release if these have not been verified on main.
# Please see the validations.yaml workflow for the names that should be used here.
checks: '["Acceptance tests (Linux)", "Acceptance tests (Mac)", "Build snapshot artifacts", "CLI tests (Linux)", "Integration tests", "Static analysis", "Unit tests"]'
release:
needs: [ quality-gate ]
needs: [check-gate, version-available]
if: ${{ github.event.inputs.phase == 'all' }}
environment: release
# runs-on.com: compute instances for parallel builds
# spot disabled: reliability for build workflows (used for releases too)
# goreleaser uses parallelism of 12, so we need more CPUs
@@ -136,10 +54,9 @@ jobs:
# tmpfs: faster io-intensive workflows
runs-on: runs-on=${{ github.run_id }}/cpu=16+32/ram=32+128/family=c5+c6+c7+c8/spot=false/extras=s3-cache+tmpfs
permissions:
contents: write
packages: write
# required for goreleaser signs section with cosign
id-token: write
contents: write # required for creating the GitHub release and pushing the version tag
packages: write # required for publishing release artifacts to GitHub packages
id-token: write # required for keyless signing (cosign/sigstore OIDC)
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
@@ -150,31 +67,24 @@ jobs:
uses: ./.github/actions/bootstrap
- name: Login to Docker Hub
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 #v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee #v4.2.0
with:
username: ${{ secrets.ANCHOREOSSWRITE_DH_USERNAME }}
password: ${{ secrets.ANCHOREOSSWRITE_DH_PAT }}
- name: Login to GitHub Container Registry
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 #v4.1.0
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee #v4.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Tag release
run: |
git config --global user.name "anchoreci"
git config --global user.email "anchoreci@users.noreply.github.com"
git tag -a "$VERSION" -m "Release $VERSION"
git push origin --tags
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ github.event.inputs.version }}
- name: Build & publish release artifacts
run: make ci-release
env:
# used for pushing tags
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
RELEASE_VERSION: ${{ github.event.inputs.version }}
# for mac signing and notarization...
QUILL_SIGN_P12: ${{ secrets.ANCHORE_APPLE_DEVELOPER_ID_CERT_CHAIN }}
QUILL_SIGN_PASSWORD: ${{ secrets.ANCHORE_APPLE_DEVELOPER_ID_CERT_PASS }}
@@ -192,30 +102,12 @@ jobs:
file: go.mod
artifact-name: sbom.spdx.json
- name: Notify Slack of new release
uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 #v3.0.1
continue-on-error: true
with:
webhook: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
webhook-type: incoming-webhook
payload: |
text: "A new Syft release has been published: https://github.com/anchore/syft/releases/tag/${{ github.event.inputs.version }}"
blocks:
- type: section
text:
type: mrkdwn
text: |
*A new Syft release has been published* :rocket:
• Release: <https://github.com/anchore/syft/releases/tag/${{ github.event.inputs.version }}|${{ github.event.inputs.version }}>
• Repo: `${{ github.repository }}`
• Workflow: `${{ github.workflow }}`
• Event: `${{ github.event_name }}`
if: ${{ success() }}
release-install-script:
needs: [ release ]
needs: [release]
if: ${{ always() && (needs.release.result == 'success' || github.event.inputs.phase == 'install-script-only') }}
uses: "anchore/workflows/.github/workflows/release-install-script.yaml@main"
permissions:
contents: read # required for the reusable workflow to check out the repo and publish the install script
uses: anchore/workflows/.github/workflows/release-install-script.yaml@b3e328b5ae31ba96297e2ed9a6124e5e6352a4c5 # v0.7.0
with:
tag: ${{ github.event.inputs.version }}
secrets:
@@ -1,15 +0,0 @@
name: "Manage Awaiting Response Label"
on:
issue_comment:
types: [created]
jobs:
run:
permissions:
contents: read
issues: write
pull-requests: write
uses: "anchore/workflows/.github/workflows/remove-awaiting-response-label.yaml@main"
secrets:
token: ${{ secrets.OSS_PROJECT_GH_TOKEN }}
@@ -10,8 +10,7 @@ on:
- '.github/workflows/**'
- '.github/actions/**'
permissions:
contents: read
permissions: {}
jobs:
zizmor:
@@ -26,7 +25,7 @@ jobs:
persist-credentials: false
- name: "Run zizmor"
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
with:
# there is a pass/fail gate as a repo ruleset (if there is no ruleset configured then the action will pass by default)
advanced-security: true
+22 -16
View File
@@ -13,8 +13,7 @@ on:
branches:
- main
permissions:
contents: read
permissions: {}
jobs:
Static-Analysis:
@@ -22,9 +21,12 @@ jobs:
name: "Static analysis"
# runs-on.com: memory & general purpose instances for testing
# spot enabled: ok to interrupt non-production workloads
# s3-cache: faster actions cache
# tmpfs: faster io-intensive workflows
runs-on: &test-runner "runs-on=${{ github.run_id }}/cpu=4+8/ram=32+128/family=r5+r6+r7+r8+m4+m5+m6+m7+m8/spot=price-capacity-optimized/extras=s3-cache+tmpfs"
# note: s3-cache intentionally omitted -- PR runs are untrusted and must not write to the
# shared cache backend that the trusted release workflow reads from (cache poisoning).
runs-on: &test-runner "runs-on=${{ github.run_id }}/cpu=4+8/ram=32+128/family=r5+r6+r7+r8+m4+m5+m6+m7+m8/spot=price-capacity-optimized/extras=tmpfs"
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
@@ -45,6 +47,8 @@ jobs:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
name: "Unit tests"
runs-on: *test-runner
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
@@ -65,6 +69,8 @@ jobs:
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
name: "Integration tests"
runs-on: *test-runner
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
@@ -86,13 +92,13 @@ jobs:
# runs-on.com: compute instances for parallel builds
# spot disabled: reliability for build workflows (used for releases too)
# goreleaser uses parallelism of 12, so we need more CPUs
# s3-cache: faster actions cache
# tmpfs: faster io-intensive workflows
runs-on: "runs-on=${{ github.run_id }}/cpu=16+32/ram=32+128/family=c5+c6+c7+c8/spot=false/extras=s3-cache+tmpfs"
# note: s3-cache intentionally omitted -- PR runs are untrusted and must not write to the
# shared cache backend that the trusted release workflow reads from (cache poisoning).
runs-on: "runs-on=${{ github.run_id }}/cpu=16+32/ram=32+128/family=c5+c6+c7+c8/spot=false/extras=tmpfs"
permissions:
contents: read
steps:
# required for magic-cache from runs-on to function with artifact upload/download (see https://runs-on.com/caching/magic-cache/#actionsupload-artifact-compatibility)
- uses: runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0 # v2.1.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
persist-credentials: false
@@ -120,10 +126,9 @@ jobs:
name: "Acceptance tests (Linux)"
needs: [Build-Snapshot-Artifacts]
runs-on: *test-runner
permissions:
contents: read
steps:
# required for magic-cache from runs-on to function with artifact upload/download (see https://runs-on.com/caching/magic-cache/#actionsupload-artifact-compatibility)
- uses: runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0 # v2.1.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
persist-credentials: false
@@ -162,9 +167,11 @@ jobs:
needs: [Build-Snapshot-Artifacts]
# note: macos runners aren't supported yet for runs-on managed runners.
runs-on: macos-latest
permissions:
contents: read
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
@@ -197,10 +204,9 @@ jobs:
name: "CLI tests (Linux)"
needs: [Build-Snapshot-Artifacts]
runs-on: *test-runner
permissions:
contents: read
steps:
# required for magic-cache from runs-on to function with artifact upload/download (see https://runs-on.com/caching/magic-cache/#actionsupload-artifact-compatibility)
- uses: runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0 # v2.1.0
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
with:
persist-credentials: false
+4 -2
View File
@@ -3,6 +3,8 @@ go.work
go.work.sum
.tool-versions
.python-version
.mise.toml
.env
# app configuration
/.syft.yaml
@@ -20,6 +22,8 @@ bin/
/generate
/specs
mise.toml
.make/.make
.conductor
# changelog generation
CHANGELOG.md
@@ -76,5 +80,3 @@ cosign.pub
__pycache__/
*.py[cod]
*$py.class
+14
View File
@@ -0,0 +1,14 @@
module github.com/anchore/syft/.make
go 1.25.8
require (
github.com/anchore/go-make v0.4.0
github.com/goccy/go-yaml v1.19.2
)
require (
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
golang.org/x/mod v0.35.0 // indirect
golang.org/x/sys v0.44.0 // indirect
)
+10
View File
@@ -0,0 +1,10 @@
github.com/anchore/go-make v0.4.0 h1:poFE4PXcHwvix2E8AhdM7YHZ15bMhZb+W02i3+qrP8M=
github.com/anchore/go-make v0.4.0/go.mod h1:Nc/tkwQHW1d1Vi8+0rtS/vSrH6pxieaUQXLdrctn+8g=
github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
+209
View File
@@ -0,0 +1,209 @@
package main
import (
"fmt"
"os"
"path/filepath"
"runtime"
"github.com/goccy/go-yaml"
. "github.com/anchore/go-make"
"github.com/anchore/go-make/file"
"github.com/anchore/go-make/git"
"github.com/anchore/go-make/lang"
"github.com/anchore/go-make/run"
"github.com/anchore/go-make/tasks/golint"
"github.com/anchore/go-make/tasks/goreleaser"
"github.com/anchore/go-make/tasks/gotest"
)
// taskfileDescriptions maps Taskfile.yaml task names to their `desc:` field.
// Loaded at package init so wrap() can use Taskfile.yaml as the single source
// of truth for wrapped-task descriptions.
var taskfileDescriptions = mustReadTaskfileDescriptions()
func main() {
Makefile(
// shared anchore tasks
golint.Tasks(),
goreleaser.Tasks(),
// unit tests: exclude packages under any test/ directory (matches the syft
// Taskfile's prior `grep -v` against test paths). Coverage threshold of 62%
// preserves the prior coverage gate that used to live in scripts/coverage.py.
gotest.Tasks(
gotest.Name("unit"),
gotest.ExcludeGlob("**/test/**"),
gotest.CoverageThreshold(62),
),
// integration tests: native go-make Task. The race-detector smoke against a
// real image stays bundled here (RunsOn integration) so `make integration`
// behaves like the Taskfile version did.
gotest.Tasks(
gotest.Name("integration"),
gotest.IncludeGlob("./cmd/syft/internal/test/integration/..."),
gotest.Verbose(),
gotest.NoCoverage(),
),
Task{
Name: "integration:race-smoke",
Description: "exercise the CLI with the race detector",
RunsOn: lang.List("integration"),
Run: func() {
Run("go run -race cmd/syft/main.go anchore/test_images:grype-quality-dotnet-69f15d2")
},
},
// cli tests: native go-make Task. Requires SYFT_BINARY_LOCATION pointing at
// an *absolute* path to the snapshot binary. Intentionally does NOT depend
// on snapshot: in CI we download a pre-built snapshot artifact and re-running
// goreleaser here would both burn ~10m and clobber the downloaded binary.
// Locally, the failure message tells you to run `make snapshot` first.
Task{
Name: "cli",
Description: "Run CLI tests",
RunsOn: lang.List("test"),
Run: func() {
bin := snapshotBinPath()
if !file.Exists(bin) {
panic(fmt.Sprintf("snapshot binary not found at %s; run `make snapshot` first", bin))
}
Log("testing binary: %s", bin)
Run(
"go test -count=1 -timeout=15m -v ./test/cli",
run.Env("SYFT_BINARY_LOCATION", bin),
)
},
},
// default validation pipeline (replaces Taskfile `default`/`pr-validations`/`validations`).
Task{
Name: "default",
Description: "Run all validation tasks",
Dependencies: Deps("static-analysis", "test", "install-test"),
},
// --- everything below is implemented in Taskfile.yaml and surfaced here
// via wrap(). Descriptions come from Taskfile.yaml (single source of truth).
// static analysis extras
wrap("check-json-schema-drift").RunOn("static-analysis"),
wrap("check-capability-drift"),
wrap("check-binary-fixture-size").RunOn("static-analysis"),
// test extras
wrap("validate-cyclonedx-schema").RunOn("test"),
wrap("test-utils").RunOn("test"),
wrap("check-docker-cache").RunOn("test"),
wrap("snapshot-smoke-test"),
// update commands
wrap("update-format-golden-files"),
// fixture cache plumbing (heavy ORAS logic, lives in Taskfile).
// refresh-fixtures hooks into "unit" so `make unit` triggers the
// stale-cache detection + download just like `task unit` did on main
// (its `deps: [tmpdir, fixtures]` is what kept the fixture cache fresh).
wrap("fingerprints"),
wrap("refresh-fixtures").RunOn("unit"),
wrap("fixtures"),
wrap("build-fixtures"),
wrap("download-test-fixture-cache"),
wrap("upload-test-fixture-cache"),
wrap("show-test-image-cache"),
// install-script tests (delegates to test/install/Makefile)
wrap("install-test"),
wrap("install-test-cache-save"),
wrap("install-test-cache-load"),
wrap("install-test-ci-mac"),
// compare tests
wrap("generate-compare-file"),
wrap("compare-mac"),
wrap("compare-linux"),
wrap("compare-test-deb-package-install"),
wrap("compare-test-rpm-package-install"),
// code/data generation (umbrella + per-target; each lives in Taskfile)
wrap("generate"),
wrap("generate-json-schema"),
wrap("generate-license-list"),
wrap("generate-cpe-dictionary-index"),
wrap("generate-capabilities"),
// cleanup (each hooks into go-make's built-in `clean` label)
wrap("clean-snapshot").RunOn("clean"),
wrap("clean-docker-cache").RunOn("clean"),
wrap("clean-oras-cache").RunOn("clean"),
wrap("clean-cache").RunOn("clean"),
wrap("clean-test-observations").RunOn("clean"),
)
}
// wrap creates a go-make Task that delegates execution to `task <name>`. The
// task's description is pulled from Taskfile.yaml's `desc:` field — descriptions
// for wrapped tasks must always live in Taskfile.yaml, never here.
func wrap(name string) Task {
desc, ok := taskfileDescriptions[name]
if !ok || desc == "" {
// loud-fail at startup so missing descs can't sneak through review.
panic(fmt.Sprintf("Taskfile.yaml task %q is missing a `desc:` field; please add one", name))
}
return Task{
Name: name,
Description: desc,
Run: func() { Run("task " + name) },
}
}
// mustReadTaskfileDescriptions parses Taskfile.yaml at the repo root and returns
// a map of task name -> desc. Runs at package init time so wrap() can use it.
func mustReadTaskfileDescriptions() map[string]string {
root := git.Root()
if root == "" {
return nil
}
path := filepath.Join(root, "Taskfile.yaml")
data, err := os.ReadFile(path) //nolint:gosec // G304: path resolved from git.Root()
if err != nil {
return nil
}
var tf struct {
Tasks map[string]struct {
Desc string `yaml:"desc"`
Aliases []string `yaml:"aliases"`
} `yaml:"tasks"`
}
lang.Throw(yaml.Unmarshal(data, &tf))
out := make(map[string]string, len(tf.Tasks))
for name, t := range tf.Tasks {
out[name] = t.Desc
// aliases inherit the canonical task's description so wrap() can find them.
for _, alias := range t.Aliases {
out[alias] = t.Desc
}
}
return out
}
// snapshotBinPath replicates the SNAPSHOT_BIN computation from the prior Taskfile:
// <repoRoot>/snapshot/<os>-build_<os>_<arch>/syft, where arch maps amd64->amd64_v1
// and arm64->arm64_v8.0 to match goreleaser's per-target output directory naming.
// Returns an absolute path: the cli tests' getSyftBinaryLocation contract requires
// SYFT_BINARY_LOCATION to be absolute because subtests run with cmd.Dir = t.TempDir().
func snapshotBinPath() string {
osName := runtime.GOOS
var arch string
switch runtime.GOARCH {
case "amd64":
arch = "amd64_v1"
case "arm64":
arch = "arm64_v8.0"
default:
arch = runtime.GOARCH
}
return filepath.Join(RootDir(), "snapshot", osName+"-build_"+osName+"_"+arch, "syft")
}
+13 -41
View File
@@ -1,46 +1,18 @@
OWNER = anchore
PROJECT = syft
# `test` and `snapshot` have matching directory names in this repo, so make would
# refuse to run them without an explicit .PHONY (Nothing to be done for ...).
.PHONY: test snapshot
test:
@go run -C .make . test
TOOL_DIR = .tool
BINNY = $(TOOL_DIR)/binny
TASK = $(TOOL_DIR)/task
snapshot:
@go run -C .make . snapshot
.DEFAULT_GOAL := make-default
.PHONY: *
.DEFAULT_GOAL: make-default
## Bootstrapping targets #################################
make-default:
@go run -C .make .
# note: we need to assume that binny and task have not already been installed
$(BINNY):
@mkdir -p $(TOOL_DIR)
@curl -sSfL https://get.anchore.io/binny | sh -s -- -b $(TOOL_DIR)
# note: we need to assume that binny and task have not already been installed
.PHONY: task
$(TASK) task: $(BINNY)
@$(BINNY) install task -q
.PHONY: ci-bootstrap-go
ci-bootstrap-go:
go mod download
# this is a bootstrapping catch-all, where if the target doesn't exist, we'll ensure the tools are installed and then try again
.DEFAULT:
%:
@make --silent $(TASK)
@$(TASK) $@
## Shim targets #################################
.PHONY: make-default
make-default: $(TASK)
@# run the default task in the taskfile
@$(TASK)
# for those of us that can't seem to kick the habit of typing `make ...` lets wrap the superior `task` tool
TASKS := $(shell bash -c "test -f $(TASK) && NO_COLOR=1 $(TASK) -l | grep '^\* ' | cut -d' ' -f2 | tr -d ':' | tr '\n' ' '" ) $(shell bash -c "test -f $(TASK) && NO_COLOR=1 $(TASK) -l | grep 'aliases:' | cut -d ':' -f 3 | tr '\n' ' ' | tr -d ','")
.PHONY: $(TASKS)
$(TASKS): $(TASK)
@$(TASK) $@
help: $(TASK)
@$(TASK) -l
@go run -C .make . $@
+47 -262
View File
@@ -1,6 +1,11 @@
version: "3"
# NOTE: most generic tasks (static-analysis, format, lint, unit, snapshot, release,
# changelog, ci-release, etc.) are now provided natively by anchore/go-make and
# defined in .make/main.go. This file holds the syft-specific tasks that go-make
# wraps via `wrap("<name>")` calls — keep descriptions (`desc:`) populated so they
# show up in `make help`.
includes:
generate:cpe-index: ./task.d/generate/cpe-index.yaml
@@ -25,11 +30,7 @@ vars:
YQ: "{{ .TOOL_DIR }}/yq"
TASK: "{{ .TOOL_DIR }}/task"
# used for changelog generation
CHANGELOG: CHANGELOG.md
NEXT_VERSION: VERSION
# used for snapshot builds
# used for snapshot bin discovery in compare/install tasks
OS:
sh: uname -s | tr '[:upper:]' '[:lower:]'
ARCH:
@@ -42,11 +43,6 @@ vars:
# e.g. when installing snapshot debs from a local path, ./ forces the deb to be installed in the current working directory instead of referencing a package name
SNAPSHOT_DIR: ./snapshot
SNAPSHOT_BIN: "{{ .PROJECT_ROOT }}/{{ .SNAPSHOT_DIR }}/{{ .OS }}-build_{{ .OS }}_{{ .ARCH }}/{{ .PROJECT }}"
SNAPSHOT_CMD: "{{ .TOOL_DIR }}/goreleaser release --config {{ .TMP_DIR }}/goreleaser.yaml --clean --snapshot --skip=publish --skip=sign"
BUILD_CMD: "{{ .TOOL_DIR }}/goreleaser build --config {{ .TMP_DIR }}/goreleaser.yaml --clean --snapshot --single-target"
RELEASE_CMD: "{{ .TOOL_DIR }}/goreleaser release --clean --release-notes {{ .CHANGELOG }}"
VERSION:
sh: git describe --dirty --always --tags
# used for install and acceptance testing
COMPARE_DIR: ./test/compare
@@ -57,43 +53,10 @@ env:
tasks:
## High-level tasks #################################
default:
desc: Run all validation tasks
aliases:
- pr-validations
- validations
cmds:
- task: static-analysis
- task: test
- task: install-test
static-analysis:
desc: Run all static analysis tasks
cmds:
- task: check-go-mod-tidy
- task: check-licenses
- task: lint
- task: check-json-schema-drift
- task: check-binary-fixture-size
test:
desc: Run all levels of test
cmds:
- task: unit
- task: integration
- task: validate-cyclonedx-schema
- task: test-utils
- task: snapshot
- task: cli
- task: check-docker-cache
## Bootstrap tasks #################################
## Bootstrap (internal helpers used by other Taskfile tasks) ###############
binny:
internal: true
# desc: Get the binny tool
generates:
- "{{ .TOOL_DIR }}/binny"
status:
@@ -102,10 +65,8 @@ tasks:
silent: true
tools:
desc: Install all tools needed for CI and local development
internal: true
deps: [binny]
aliases:
- bootstrap
generates:
- ".binny.yaml"
- "{{ .TOOL_DIR }}/*"
@@ -114,79 +75,14 @@ tasks:
cmd: "{{ .TOOL_DIR }}/binny install -v"
silent: true
update-tools:
desc: Update pinned versions of all tools to their latest available versions
deps: [binny]
generates:
- ".binny.yaml"
- "{{ .TOOL_DIR }}/*"
cmd: "{{ .TOOL_DIR }}/binny update -v"
silent: true
list-tools:
desc: List all tools needed for CI and local development
deps: [binny]
cmd: "{{ .TOOL_DIR }}/binny list"
silent: true
list-tool-updates:
desc: List all tools that are not up to date relative to the binny config
deps: [binny]
cmd: "{{ .TOOL_DIR }}/binny list --updates"
silent: true
tmpdir:
internal: true
silent: true
generates:
- "{{ .TMP_DIR }}"
cmd: "mkdir -p {{ .TMP_DIR }}"
## Static analysis tasks #################################
format:
desc: Auto-format all source code
deps: [tools]
cmds:
- gofmt -w -s .
- "{{ .TOOL_DIR }}/gosimports -local github.com/anchore -w ."
- go mod tidy
lint-fix:
desc: Auto-format all source code + run golangci lint fixers
deps: [tools]
cmds:
- task: format
- "{{ .TOOL_DIR }}/golangci-lint run --tests=false --fix"
lint:
desc: Run gofmt + golangci lint checks
vars:
BAD_FMT_FILES:
sh: gofmt -l -s .
BAD_FILE_NAMES:
sh: "find . | grep -e ':' || true"
deps: [tools]
cmds:
# ensure there are no go fmt differences
- cmd: 'test -z "{{ .BAD_FMT_FILES }}" || (echo "files with gofmt issues: [{{ .BAD_FMT_FILES }}]"; exit 1)'
silent: true
# ensure there are no files with ":" in it (a known back case in the go ecosystem)
- cmd: 'test -z "{{ .BAD_FILE_NAMES }}" || (echo "files with bad names: [{{ .BAD_FILE_NAMES }}]"; exit 1)'
silent: true
# run linting
- "{{ .TOOL_DIR }}/golangci-lint run --tests=false"
check-licenses:
# desc: Ensure transitive dependencies are compliant with the current license policy
deps: [tools]
cmd: "{{ .TOOL_DIR }}/bouncer check ./..."
check-go-mod-tidy:
# desc: Ensure go.mod and go.sum are up to date
cmds:
- cmd: .github/scripts/go-mod-tidy-check.sh && echo "go.mod and go.sum are tidy!"
silent: true
## Static analysis extras #################################################
check-json-schema-drift:
desc: Ensure there is no drift between the JSON schema and the code
@@ -203,8 +99,8 @@ tasks:
cmds:
- .github/scripts/check_binary_fixture_size.sh syft/pkg/cataloger/binary/testdata/classifiers/snippets
## Test extras ############################################################
## Testing tasks #################################
update-format-golden-files:
desc: "Update golden (i.e. snapshot) files used by unit tests"
cmds:
@@ -214,59 +110,32 @@ tasks:
- go test ./syft/format/cyclonedxjson -update-cyclonedx-json
- go test ./syft/format/syftjson -update-json
unit:
desc: Run unit tests
deps:
- tmpdir
- fixtures
vars:
TEST_PKGS:
sh: "go list ./... | grep -v {{ .OWNER }}/{{ .PROJECT }}/test | grep -v {{ .OWNER }}/{{ .PROJECT }}/cmd/syft/internal/test | tr '\n' ' '"
# unit test coverage threshold (in % coverage)
COVERAGE_THRESHOLD: 62
cmds:
- task: clean-test-observations
- "go test -coverprofile {{ .TMP_DIR }}/unit-coverage-details.txt {{ .TEST_PKGS }}"
- cmd: ".github/scripts/coverage.py {{ .COVERAGE_THRESHOLD }} {{ .TMP_DIR }}/unit-coverage-details.txt"
silent: true
integration:
desc: Run integration tests
cmds:
- "go test -v ./cmd/syft/internal/test/integration"
# exercise most of the CLI with the data race detector
# we use a larger image to ensure we're using multiple catalogers at a time
- "go run -race cmd/syft/main.go anchore/test_images:grype-quality-dotnet-69f15d2"
validate-cyclonedx-schema:
desc: Validate that Syft produces valid CycloneDX documents
cmds:
- "cd schema/cyclonedx && make"
cli:
desc: Run CLI tests
deps: [tools]
cmds:
- cmd: "echo 'testing binary: {{ .SNAPSHOT_BIN }}'"
silent: true
- cmd: "test -f {{ .SNAPSHOT_BIN }} || (find {{ .SNAPSHOT_DIR }} && echo '\nno snapshot found for {{ .SNAPSHOT_BIN }}' && false)"
silent: true
- "go test -count=1 -timeout=15m -v ./test/cli"
env:
SYFT_BINARY_LOCATION: "{{ .SNAPSHOT_BIN }}"
test-utils:
desc: Run tests for pipeline utils
cmds:
- cmd: .github/scripts/labeler_test.py
snapshot-smoke-test:
desc: Run a smoke test on the snapshot builds + docker images
cmds:
- cmd: "echo 'testing snapshot binary: {{ .SNAPSHOT_BIN }}'"
silent: true
- cmd: "test -f {{ .SNAPSHOT_BIN }} || (find {{ .SNAPSHOT_DIR }} && echo '\nno snapshot found for {{ .SNAPSHOT_BIN }}' && false)"
silent: true
- "{{ .SNAPSHOT_BIN }} version"
- "{{ .SNAPSHOT_BIN }} scan alpine:latest"
- docker run --rm anchore/syft:latest version
- docker run --rm anchore/syft:latest scan alpine:latest
## Test-fixture-related targets #################################
## Test-fixture-related targets ###########################################
fingerprints:
desc: Generate fingerprints for all non-docker test fixture
desc: Generate fingerprints for all non-docker test fixtures
silent: true
# this will look for `testdata/Makefile` and invoke the `fingerprint` target to calculate all cache input fingerprint files
generates:
@@ -281,16 +150,10 @@ tasks:
echo -e "${YELLOW}creating fingerprint files for non-docker fixtures...${RESET}"
for dir in $(find . -type d -name 'testdata'); do
if [ -f "$dir/Makefile" ]; then
# for debugging...
#echo -e "${YELLOW}• calculating fingerprints in $dir... ${RESET}"
(make -C "$dir" fingerprint)
fi
done
# for debugging...
# echo -e "generated all fixture fingerprints"
- .github/scripts/fingerprint_docker_fixtures.py
- |
# if DOWNLOAD_TEST_FIXTURE_CACHE is set to 'false', then we don't need to calculate the fingerprint for the cache
@@ -432,6 +295,7 @@ tasks:
eval $oras_command
show-test-image-cache:
desc: Print the on-disk + docker daemon state of the stereoscope fixture image cache
silent: true
cmds:
- "echo 'Docker daemon cache:'"
@@ -452,28 +316,36 @@ tasks:
exit 1
fi
## install.sh testing targets #################################
## install.sh testing targets #############################################
install-test:
desc: Run install.sh test suite (delegates to test/install/Makefile)
cmds:
- "cd test/install && make"
install-test-cache-save:
desc: Save the install.sh test image cache (delegates to test/install/Makefile)
cmds:
- "cd test/install && make save"
install-test-cache-load:
desc: Load the install.sh test image cache (delegates to test/install/Makefile)
cmds:
- "cd test/install && make load"
install-test-ci-mac:
desc: Run install.sh CI test suite on macOS (delegates to test/install/Makefile)
cmds:
- "cd test/install && make ci-test-mac"
## Compare-test targets ###################################################
generate-compare-file:
desc: Generate the acceptance comparison reference JSON for the current compare image
cmd: "go run ./cmd/syft {{ .COMPARE_TEST_IMAGE }} -o json > {{ .COMPARE_DIR }}/testdata/acceptance-{{ .COMPARE_TEST_IMAGE }}.json"
compare-mac:
desc: Run macOS install + acceptance comparison against the snapshot build
deps: [tmpdir]
cmd: |
{{ .COMPARE_DIR }}/mac.sh \
@@ -483,11 +355,13 @@ tasks:
{{ .TMP_DIR }}
compare-linux:
desc: Run Linux install + acceptance comparison (deb + rpm) against the snapshot build
cmds:
- task: compare-test-deb-package-install
- task: compare-test-rpm-package-install
compare-test-deb-package-install:
desc: Run Linux .deb install + acceptance comparison against the snapshot build
deps: [tmpdir]
cmd: |
{{ .COMPARE_DIR }}/deb.sh \
@@ -497,6 +371,7 @@ tasks:
{{ .TMP_DIR }}
compare-test-rpm-package-install:
desc: Run Linux .rpm install + acceptance comparison against the snapshot build
deps: [tmpdir]
cmd: |
{{ .COMPARE_DIR }}/rpm.sh \
@@ -506,7 +381,7 @@ tasks:
{{ .TMP_DIR }}
## Code and data generation targets #################################
## Code and data generation targets ######################################
generate:
desc: Add data generation tasks
@@ -556,104 +431,7 @@ tasks:
- "SYFT_ENABLE_COMPLETENESS_TESTS=true go test -p 1 ./internal/capabilities/... -count=1"
## Build-related targets #################################
build:
desc: Build the project
deps: [tools, tmpdir]
generates:
- "{{ .PROJECT }}"
cmds:
- silent: true
cmd: |
echo "dist: {{ .SNAPSHOT_DIR }}" > {{ .TMP_DIR }}/goreleaser.yaml
cat .goreleaser.yaml >> {{ .TMP_DIR }}/goreleaser.yaml
- "{{ .BUILD_CMD }}"
snapshot:
desc: Create a snapshot release
aliases:
- build
deps: [tools, tmpdir]
sources:
- cmd/**/*.go
- syft/**/*.go
- internal/**/*.go
method: checksum
generates:
- "{{ .SNAPSHOT_BIN }}"
cmds:
- silent: true
cmd: |
echo "dist: {{ .SNAPSHOT_DIR }}" > {{ .TMP_DIR }}/goreleaser.yaml
cat .goreleaser.yaml >> {{ .TMP_DIR }}/goreleaser.yaml
- "{{ .SNAPSHOT_CMD }}"
snapshot-smoke-test:
desc: Run a smoke test on the snapshot builds + docker images
cmds:
- cmd: "echo 'testing snapshot binary: {{ .SNAPSHOT_BIN }}'"
silent: true
- cmd: "test -f {{ .SNAPSHOT_BIN }} || (find {{ .SNAPSHOT_DIR }} && echo '\nno snapshot found for {{ .SNAPSHOT_BIN }}' && false)"
silent: true
- "{{ .SNAPSHOT_BIN }} version"
- "{{ .SNAPSHOT_BIN }} scan alpine:latest"
- docker run --rm anchore/syft:latest version
- docker run --rm anchore/syft:latest scan alpine:latest
changelog:
desc: Generate a changelog
deps: [tools]
generates:
- "{{ .CHANGELOG }}"
- "{{ .NEXT_VERSION }}"
cmds:
- "{{ .TOOL_DIR }}/chronicle -vv -n --version-file {{ .NEXT_VERSION }} > {{ .CHANGELOG }}"
- "{{ .TOOL_DIR }}/glow -w 0 {{ .CHANGELOG }}"
## Release targets #################################
release:
desc: Create a release
interactive: true
deps: [tools]
cmds:
- cmd: .github/scripts/trigger-release.sh
silent: true
## CI-only targets #################################
ci-check:
# desc: "[CI only] Are you in CI?"
cmds:
- cmd: .github/scripts/ci-check.sh
silent: true
ci-release:
# desc: "[CI only] Create a release"
deps: [tools]
cmds:
- task: ci-check
- "{{ .TOOL_DIR }}/chronicle -vvv > CHANGELOG.md"
- cmd: "cat CHANGELOG.md"
silent: true
- "{{ .RELEASE_CMD }}"
## Cleanup targets #################################
clean:
desc: Remove all cache files and old builds
cmds:
- task: clean-snapshot
- task: clean-cache
- task: clean-test-observations
- task: clean-docker-cache
- task: clean-oras-cache
## Cleanup targets ########################################################
clean-snapshot:
desc: Remove any snapshot builds
@@ -675,6 +453,7 @@ tasks:
desc: Remove all image docker tar cache, images from the docker daemon, and ephemeral test fixtures
cmds:
- task: clean-docker-cache
- task: prune-orphan-fingerprints
- |
BOLD='\033[1m'
YELLOW='\033[0;33m'
@@ -690,6 +469,12 @@ tasks:
echo -e "${BOLD}Deleted all ephemeral test fixtures${RESET}"
- rm -f {{ .LAST_CACHE_PULL_FILE }} {{ .CACHE_PATHS_FILE }}
prune-orphan-fingerprints:
desc: Remove *.fingerprint files left behind by moved/deleted catalogers
silent: true
cmds:
- .github/scripts/prune_orphan_fingerprints.py
clean-test-observations:
desc: Remove all test observations (i.e. testdata/test-observations.json)
cmds:
+27 -28
View File
@@ -1,6 +1,6 @@
module github.com/anchore/syft
go 1.25.8
go 1.26.3
require (
github.com/BurntSushi/toml v1.6.0
@@ -22,7 +22,7 @@ require (
github.com/anchore/go-sync v0.1.0
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.2.0
github.com/anchore/stereoscope v0.1.23
github.com/anchore/stereoscope v0.2.0
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
@@ -41,8 +41,8 @@ require (
github.com/facebookincubator/nvdtools v0.1.5
github.com/github/go-spdx/v2 v2.4.0
github.com/gkampitakis/go-snaps v0.5.21
github.com/go-git/go-billy/v5 v5.8.0
github.com/go-git/go-git/v5 v5.18.0
github.com/go-git/go-billy/v5 v5.9.0
github.com/go-git/go-git/v5 v5.19.1
github.com/go-test/deep v1.1.1
github.com/go-viper/mapstructure/v2 v2.5.0
github.com/goccy/go-yaml v1.19.2
@@ -62,6 +62,7 @@ require (
github.com/jedib0t/go-pretty/v6 v6.7.8
github.com/jinzhu/copier v0.4.0
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
github.com/klauspost/compress v1.18.5
github.com/magiconair/properties v1.8.10
github.com/mholt/archives v0.1.5
github.com/moby/sys/mountinfo v0.7.2
@@ -92,7 +93,7 @@ require (
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
go.uber.org/goleak v1.3.0
go.yaml.in/yaml/v3 v3.0.4
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
golang.org/x/mod v0.35.0
golang.org/x/net v0.53.0
golang.org/x/time v0.15.0
@@ -110,15 +111,14 @@ require (
cloud.google.com/go/iam v1.5.3 // indirect
cloud.google.com/go/monitoring v1.24.3 // indirect
cloud.google.com/go/storage v1.61.3 // indirect
cyphar.com/go-pathrs v0.2.1 // indirect
dario.cat/mergo v1.0.2 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Microsoft/hcsshim v0.14.0-rc.1 // indirect
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 // indirect
github.com/Microsoft/hcsshim v0.15.0-rc.1 // indirect
github.com/ProtonMail/go-crypto v1.4.0 // indirect
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/agext/levenshtein v1.2.1 // indirect
@@ -164,22 +164,22 @@ require (
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
github.com/cloudflare/circl v1.6.3 // indirect
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
github.com/containerd/cgroups/v3 v3.1.2 // indirect
github.com/containerd/containerd/api v1.10.0 // indirect
github.com/containerd/containerd/v2 v2.2.2 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/cgroups/v3 v3.1.3 // indirect
github.com/containerd/containerd/api v1.11.1 // indirect
github.com/containerd/containerd/v2 v2.3.1 // indirect
github.com/containerd/continuity v0.5.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/fifo v1.1.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v1.0.0-rc.4 // indirect
github.com/containerd/plugin v1.0.0 // indirect
github.com/containerd/plugin v1.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
github.com/containerd/ttrpc v1.2.7 // indirect
github.com/containerd/ttrpc v1.2.8 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v29.4.0+incompatible // indirect
github.com/docker/cli v29.4.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.9.5 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
@@ -215,7 +215,7 @@ require (
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.5 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
@@ -253,11 +253,10 @@ require (
github.com/olekukonko/ll v0.1.6 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runtime-spec v1.3.0 // indirect
github.com/opencontainers/selinux v1.13.1 // indirect
github.com/pborman/indent v1.2.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pelletier/go-toml/v2 v2.3.1 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pjbgf/sha1cd v0.6.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pkg/profile v1.7.0 // indirect
github.com/pkg/xattr v0.4.9 // indirect
@@ -297,8 +296,8 @@ require (
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
@@ -312,13 +311,13 @@ require (
golang.org/x/term v0.42.0 // indirect
golang.org/x/text v0.36.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
gonum.org/v1/gonum v0.16.0 // indirect
gonum.org/v1/gonum v0.17.0 // indirect
google.golang.org/api v0.271.0 // indirect
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
google.golang.org/grpc v1.79.3 // indirect
google.golang.org/protobuf v1.36.11 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
google.golang.org/grpc v1.80.0 // indirect
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
modernc.org/libc v1.70.0 // indirect
modernc.org/mathutil v1.7.1 // indirect
+54 -54
View File
@@ -67,8 +67,6 @@ cloud.google.com/go/storage v1.61.3 h1:VS//ZfBuPGDvakfD9xyPW1RGF1Vy3BWUoVZXgW1KM
cloud.google.com/go/storage v1.61.3/go.mod h1:JtqK8BBB7TWv0HVGHubtUdzYYrakOQIsMLffZ2Z/HWk=
cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U=
cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s=
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.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
@@ -84,8 +82,8 @@ github.com/CycloneDX/cyclonedx-go v0.10.0/go.mod h1:vUvbCXQsEm48OI6oOlanxstwNByX
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=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ=
@@ -99,10 +97,10 @@ github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lpr
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ=
github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c=
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 h1:0kQAzHq8vLs7Pptv+7TxjdETLf/nIqJpIB4oC6Ba4vY=
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
github.com/Microsoft/hcsshim v0.15.0-rc.1 h1:FbbwtQmiD+BVHynGkx5S65JkLyhkEiiTP8nrpmg2SZw=
github.com/Microsoft/hcsshim v0.15.0-rc.1/go.mod h1:HWvvUPIy9HF6LotILj1G4VyS065rcLQ6tqj6tMUdOfI=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
@@ -148,8 +146,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T/EAErk=
github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE=
github.com/anchore/stereoscope v0.1.23 h1:q9i3CtbicTuSlcCnA+5pfoT9WDCEoSqvXDfHMH1hyWo=
github.com/anchore/stereoscope v0.1.23/go.mod h1:JLnun49fkLkuv3ebU0ROvFl/0JiRmNmUtCzc6y4ollo=
github.com/anchore/stereoscope v0.2.0 h1:8haOu2ugLymmxvyfrZR7OTBFiRFRBh5LlNUtRrSRoxI=
github.com/anchore/stereoscope v0.2.0/go.mod h1:PYx3fD4lvBVsYoQ/fBdauhZ5hmkRrJgw1B73svKx7/U=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
@@ -294,14 +292,14 @@ 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/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
github.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4=
github.com/containerd/cgroups/v3 v3.1.2/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o=
github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM=
github.com/containerd/containerd/v2 v2.2.2 h1:mjVQdtfryzT7lOqs5EYUFZm8ioPVjOpkSoG1GJPxEMY=
github.com/containerd/containerd/v2 v2.2.2/go.mod h1:5Jhevmv6/2J+Iu/A2xXAdUIdI5Ah/hfyO7okJ4AFIdY=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ=
github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
github.com/containerd/containerd/api v1.11.1 h1:h8nfoDW9+fNsC/9TwiAHj8B1GzXKtR4eFtkhi/X5RLU=
github.com/containerd/containerd/api v1.11.1/go.mod h1:CaQFRu+N1MtbgL6JDOJLUB1hCKESU1lD6MuTJhgtdlw=
github.com/containerd/containerd/v2 v2.3.1 h1:4dVXBdlvotRBlaP2TmNbY/EGc06KJrMDDUqQdxX/HOk=
github.com/containerd/containerd/v2 v2.3.1/go.mod h1:xVoxGPWZBwwph8DF2IbDhriLKdHfjdpO0b3wFP9wQ1I=
github.com/containerd/continuity v0.5.0 h1:7a85HZpCSs+1Zps0Ee3DPSuAWY+0SJM1JNM51nlEVDg=
github.com/containerd/continuity v0.5.0/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
@@ -312,12 +310,12 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v1.0.0-rc.4 h1:M42JrUT4zfZTqtkUwkr0GzmUWbfyO5VO0Q5b3op97T4=
github.com/containerd/platforms v1.0.0-rc.4/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A=
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
github.com/containerd/plugin v1.1.0 h1:O+7lczNJVMy8rz0YNx3xGB8tTf5qY4i5abF041Ew19U=
github.com/containerd/plugin v1.1.0/go.mod h1:qBTum+A8lJ6lO44A19Eo7y1OlcLj4OWFH1DA/vnHmcc=
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
github.com/containerd/ttrpc v1.2.8 h1:xbVu6D4qF2jihdh9rDVOKqUMiFBQk6YctTdo1zk087Y=
github.com/containerd/ttrpc v1.2.8/go.mod h1:wyZW2K79t4Hfcxl+GUvkZqRBzJlqFFvgEeeWXa42tyE=
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
@@ -325,8 +323,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.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
github.com/cyphar/filepath-securejoin v0.6.1/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=
@@ -343,8 +341,8 @@ 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 v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM=
github.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/cli v29.4.3+incompatible h1:u+UliYm2J/rYrIh2FqHQg32neRG8GjbvNuwQRTzGspU=
github.com/docker/cli v29.4.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
@@ -385,6 +383,8 @@ github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
github.com/erofs/go-erofs v0.3.0 h1:o/W5ABAA3sHYl97WL93dacKEfeDpJhdFf3c2snAti7I=
github.com/erofs/go-erofs v0.3.0/go.mod h1:XkSeN9MHszGd4+3gcEjadJLYHCQpWzJ7/8yznzMuzJs=
github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk=
github.com/facebookincubator/nvdtools v0.1.5 h1:jbmDT1nd6+k+rlvKhnkgMokrCAzHoASWE5LtHbX2qFQ=
github.com/facebookincubator/nvdtools v0.1.5/go.mod h1:Kh55SAWnjckS96TBSrXI99KrEKH4iB0OJby3N8GRJO4=
@@ -420,12 +420,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
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.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM=
github.com/go-git/go-git/v5 v5.18.0/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo=
github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00=
github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ=
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=
@@ -649,6 +649,8 @@ github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -776,8 +778,6 @@ 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.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=
github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE=
github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg=
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=
@@ -786,12 +786,12 @@ github.com/pborman/indent v1.2.1/go.mod h1:FitS+t35kIYtB5xWTZAPhnmrxcciEEOdbyrrp
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -822,8 +822,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
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=
@@ -998,10 +998,10 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0 h1:ZrPRak/kS4xI3AVXy8F7pipuDXmDsrO8Lg+yQjBLjw0=
@@ -1048,8 +1048,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1319,8 +1319,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
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.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
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=
@@ -1427,10 +1427,10 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0=
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1458,8 +1458,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
@@ -1474,8 +1474,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+5 -1
View File
@@ -490,11 +490,15 @@ func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry
}
for _, o := range binaryClassifierOverrides[classifier.Class] {
cpeStrings := make([]string, len(o.CPEs))
for i, c := range o.CPEs {
cpeStrings[i] = c.Attributes.BindToFmtString()
}
packages = append(packages, capabilities.DetectorPackageInfo{
Class: o.Class,
Name: o.Package,
PURL: stripPURLVersion(o.PURL),
CPEs: o.CPEs,
CPEs: cpeStrings,
Type: "BinaryPkg",
})
}
+52 -3
View File
@@ -10,7 +10,7 @@ type binaryClassifierOverride struct {
Class string
Package string
PURL string
CPEs []string
CPEs []cpe.CPE
}
var binaryClassifierOverrides = map[string][]binaryClassifierOverride{
@@ -96,12 +96,61 @@ var binaryClassifierOverrides = map[string][]binaryClassifierOverride{
CPEs: singleCPE("cpe:2.3:a:oracle:jdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
"openssl-binary": {
{
Class: "openssl-binary-aws-lc",
Package: "aws-lc",
PURL: mustPURL("pkg:generic/aws-lc@version"),
CPEs: singleCPE("cpe:2.3:a:amazon:aws_libcrypto:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "openssl-binary",
Package: "openssl",
PURL: mustPURL("pkg:generic/openssl@version"),
CPEs: singleCPE("cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
"mysqld-binary": {
{
Class: "mysqld-mysql-cluster-legacy-binary",
Package: "mysql-cluster",
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
{
Class: "mysqld-mysql-cluster-binary",
Package: "mysql-cluster",
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
{
Class: "mysqld-mysql-server-binary",
Package: "mysql-server",
PURL: mustPURL("pkg:generic/mysql-server@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
},
}
func mustPURL(purl string) string {
return purl
}
func singleCPE(cpeString string, _ ...any) []string {
return []string{cpeString}
func singleCPE(cpeString string, source ...cpe.Source) []cpe.CPE {
src := cpe.GeneratedSource
if len(source) > 0 {
src = source[0]
}
return []cpe.CPE{
cpe.Must(cpeString, src),
}
}
+9 -5
View File
@@ -15,14 +15,18 @@ fixtures: build verify
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
fingerprint: $(FINGERPRINT_FILE)
tools-check:
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
# for selfrando...
# docker buildx build --platform linux/amd64 -t $(TOOL_IMAGE) .
tools:
@(docker image inspect $(TOOL_IMAGE) > /dev/null 2>&1 && make tools-check) || (docker build -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
@if docker image inspect $(TOOL_IMAGE) > /dev/null 2>&1 \
&& test -f Dockerfile.sha256 \
&& sha256sum --quiet -c Dockerfile.sha256 2>/dev/null; then \
: ; \
else \
docker build -t $(TOOL_IMAGE) . \
&& sha256sum Dockerfile > Dockerfile.sha256; \
fi
build: tools
mkdir -p $(BIN)
@@ -46,4 +50,4 @@ $(FINGERPRINT_FILE):
clean:
rm -rf $(BIN) Dockerfile.sha256 $(VERIFY_FILE) $(FINGERPRINT_FILE)
.PHONY: tools tools-check build verify debug clean
.PHONY: tools build verify debug clean
@@ -15,11 +15,15 @@ fixtures: build
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
fingerprint: $(FINGERPRINT_FILE)
tools-check:
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
tools:
@(docker image inspect $(TOOL_IMAGE) > /dev/null 2>&1 && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
@if docker image inspect $(TOOL_IMAGE) > /dev/null 2>&1 \
&& test -f Dockerfile.sha256 \
&& sha256sum --quiet -c Dockerfile.sha256 2>/dev/null; then \
: ; \
else \
docker build --platform linux/amd64 -t $(TOOL_IMAGE) . \
&& sha256sum Dockerfile > Dockerfile.sha256; \
fi
build: tools
@mkdir -p $(BIN)
@@ -38,4 +42,4 @@ $(FINGERPRINT_FILE):
clean:
rm -rf $(BIN) Dockerfile.sha256 $(VERIFY_FILE) $(FINGERPRINT_FILE)
.PHONY: tools tools-check build debug clean
.PHONY: tools build debug clean
+102
View File
@@ -66,6 +66,16 @@ catalogers:
cpes:
- cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/julia'
packages:
- class: julia-binary
name: julia
purl: pkg:generic/julia
cpes:
- cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/helm'
@@ -242,6 +252,77 @@ catalogers:
- cpe:2.3:a:percona:percona_server:*:*:*:*:*:*:*:*
- cpe:2.3:a:percona:xtradb_cluster:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/mysqld'
packages:
- class: mysqld-mysql-cluster-legacy-binary
name: mysql-server
purl: pkg:generic/mysql-server
cpes:
- cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*
- cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/mysqld'
packages:
- class: mysqld-binary
name: ""
purl: pkg:/
cpes: []
type: BinaryPkg
- class: mysqld-mysql-cluster-legacy-binary
name: mysql-cluster
purl: pkg:generic/mysql-cluster
cpes:
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
type: BinaryPkg
- class: mysqld-mysql-cluster-binary
name: mysql-cluster
purl: pkg:generic/mysql-cluster
cpes:
- cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*
- cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
type: BinaryPkg
- class: mysqld-mysql-server-binary
name: mysql-server
purl: pkg:generic/mysql-server
cpes:
- cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*
- cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/ndbd'
packages:
- class: ndbd-binary
name: mysql-cluster
purl: pkg:generic/mysql-cluster
cpes:
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/ndbmtd'
packages:
- class: ndbmtd-binary
name: mysql-cluster
purl: pkg:generic/mysql-cluster
cpes:
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/ndb_mgmd'
packages:
- class: ndb_mgmd-binary
name: mysql-cluster
purl: pkg:generic/mysql-cluster
cpes:
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/xtrabackup'
@@ -427,6 +508,17 @@ catalogers:
criteria:
- '**/openssl'
packages:
- class: openssl-binary
name: ""
purl: pkg:/
cpes: []
type: BinaryPkg
- class: openssl-binary-aws-lc
name: aws-lc
purl: pkg:generic/aws-lc
cpes:
- cpe:2.3:a:amazon:aws_libcrypto:*:*:*:*:*:*:*:*
type: BinaryPkg
- class: openssl-binary
name: openssl
purl: pkg:generic/openssl
@@ -684,6 +776,16 @@ catalogers:
cpes:
- cpe:2.3:a:mongodb:mongodb:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/nginx-ingress-controller'
packages:
- class: ingress-nginx-binary
name: nginx-ingress-controller
purl: pkg:generic/nginx-ingress-controller
cpes:
- cpe:2.3:a:kubernetes:ingress-nginx:*:*:*:*:*:*:*:*
type: BinaryPkg
- method: glob
criteria:
- '**/java'
@@ -36,8 +36,9 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
// or testdata/classifiers/bin directory . Snippets are searched for first, and if not found, then existing binaries are
// used. If no binary or snippet is found the test will fail. If '-must-use-original-binaries' is used the only
// full binaries are tested (no snippets), and if no binary is found the test will be skipped.
logicalFixture string
expected pkg.Package
logicalFixture string
expected pkg.Package
expectedPackages []pkg.Package
}{
{
logicalFixture: "arangodb/3.11.8/linux-amd64",
@@ -206,6 +207,82 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("mariadb-binary"),
},
},
{
logicalFixture: "mysqld/9.7.0/linux-amd64",
expected: pkg.Package{
Name: "mysql-server",
Version: "9.7.0",
Type: "binary",
PURL: "pkg:generic/mysql-server@9.7.0",
Locations: locations("mysqld"),
Metadata: metadata("mysqld-mysql-server-binary"),
},
},
{
logicalFixture: "mysql-cluster/9.7.0/linux-amd64",
expected: pkg.Package{
Name: "mysql-cluster",
Version: "9.7.0",
Type: "binary",
PURL: "pkg:generic/mysql-cluster@9.7.0",
Locations: locations("mysqld"),
Metadata: metadata("mysqld-mysql-cluster-binary"),
},
},
{
logicalFixture: "mysql-cluster/7.6.17/linux-amd64",
expectedPackages: []pkg.Package{
{
Name: "mysql-server",
Version: "5.7.33",
Type: "binary",
PURL: "pkg:generic/mysql-server@5.7.33",
Locations: locations("mysqld"),
Metadata: metadata("mysqld-mysql-cluster-legacy-binary"),
},
{
Name: "mysql-cluster",
Version: "7.6.17",
Type: "binary",
PURL: "pkg:generic/mysql-cluster@7.6.17",
Locations: locations("mysqld"),
Metadata: metadata("mysqld-mysql-cluster-legacy-binary"),
},
},
},
{
logicalFixture: "ndbd/9.7.0/linux-amd64",
expected: pkg.Package{
Name: "mysql-cluster",
Version: "9.7.0",
Type: "binary",
PURL: "pkg:generic/mysql-cluster@9.7.0",
Locations: locations("ndbd"),
Metadata: metadata("ndbd-binary"),
},
},
{
logicalFixture: "ndbmtd/9.7.0/linux-amd64",
expected: pkg.Package{
Name: "mysql-cluster",
Version: "9.7.0",
Type: "binary",
PURL: "pkg:generic/mysql-cluster@9.7.0",
Locations: locations("ndbmtd"),
Metadata: metadata("ndbmtd-binary"),
},
},
{
logicalFixture: "ndb_mgmd/9.7.0/linux-amd64",
expected: pkg.Package{
Name: "mysql-cluster",
Version: "9.7.0",
Type: "binary",
PURL: "pkg:generic/mysql-cluster@9.7.0",
Locations: locations("ndb_mgmd"),
Metadata: metadata("ndb_mgmd-binary"),
},
},
{
logicalFixture: "mongodb/8.0.17/linux-amd64",
expected: pkg.Package{
@@ -432,6 +509,39 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("haproxy-binary"),
},
},
{
logicalFixture: "helm/4.1.4/linux-s390x",
expected: pkg.Package{
Name: "helm",
Version: "4.1.4",
Type: "binary",
PURL: "pkg:golang/helm.sh/helm@4.1.4",
Locations: locations("helm"),
Metadata: metadata("helm"),
},
},
{
logicalFixture: "helm/3.15.2/linux-amd64",
expected: pkg.Package{
Name: "helm",
Version: "3.15.2",
Type: "binary",
PURL: "pkg:golang/helm.sh/helm@3.15.2",
Locations: locations("helm"),
Metadata: metadata("helm"),
},
},
{
logicalFixture: "helm/3.12.0/linux-s390x",
expected: pkg.Package{
Name: "helm",
Version: "3.12.0",
Type: "binary",
PURL: "pkg:golang/helm.sh/helm@3.12.0",
Locations: locations("helm"),
Metadata: metadata("helm"),
},
},
{
logicalFixture: "helm/3.11.1/linux-amd64",
expected: pkg.Package{
@@ -443,6 +553,39 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("helm"),
},
},
{
logicalFixture: "helm/3.0.0-alpha.1/linux-amd64",
expected: pkg.Package{
Name: "helm",
Version: "3.0.0-alpha.1",
Type: "binary",
PURL: "pkg:golang/helm.sh/helm@3.0.0-alpha.1",
Locations: locations("helm"),
Metadata: metadata("helm"),
},
},
{
logicalFixture: "helm/2.17.0-rc.1/linux-amd64",
expected: pkg.Package{
Name: "helm",
Version: "2.17.0-rc.1",
Type: "binary",
PURL: "pkg:golang/helm.sh/helm@2.17.0-rc.1",
Locations: locations("helm"),
Metadata: metadata("helm"),
},
},
{
logicalFixture: "helm/2.0.0-beta.2/linux-amd64",
expected: pkg.Package{
Name: "helm",
Version: "2.0.0-beta.2",
Type: "binary",
PURL: "pkg:golang/helm.sh/helm@2.0.0-beta.2",
Locations: locations("helm"),
Metadata: metadata("helm"),
},
},
{
logicalFixture: "helm/3.10.3/linux-amd64",
expected: pkg.Package{
@@ -1331,6 +1474,50 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("dart-binary"),
},
},
{
logicalFixture: "deno/1.10.3/linux-amd64",
expected: pkg.Package{
Name: "deno",
Version: "1.10.3",
Type: "binary",
PURL: "pkg:generic/deno@1.10.3",
Locations: locations("deno"),
Metadata: metadata("deno-binary"),
},
},
{
logicalFixture: "deno/1.16.4/linux-amd64",
expected: pkg.Package{
Name: "deno",
Version: "1.16.4",
Type: "binary",
PURL: "pkg:generic/deno@1.16.4",
Locations: locations("deno"),
Metadata: metadata("deno-binary"),
},
},
{
logicalFixture: "deno/1.28.3/linux-amd64",
expected: pkg.Package{
Name: "deno",
Version: "1.28.3",
Type: "binary",
PURL: "pkg:generic/deno@1.28.3",
Locations: locations("deno"),
Metadata: metadata("deno-binary"),
},
},
{
logicalFixture: "deno/1.29.4/linux-amd64",
expected: pkg.Package{
Name: "deno",
Version: "1.29.4",
Type: "binary",
PURL: "pkg:generic/deno@1.29.4",
Locations: locations("deno"),
Metadata: metadata("deno-binary"),
},
},
{
logicalFixture: "deno/1.41.0/linux-amd64",
expected: pkg.Package{
@@ -1485,6 +1672,17 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("openssl-binary"),
},
},
{
logicalFixture: "aws-lc/1.69.0/linux-amd64",
expected: pkg.Package{
Name: "aws-lc",
Version: "1.69.0",
Type: "binary",
PURL: "pkg:generic/aws-lc@1.69.0",
Locations: locations("openssl"),
Metadata: metadata("openssl-binary-aws-lc"),
},
},
{
logicalFixture: "openldap/2.6.10/linux-amd64",
expected: pkg.Package{
@@ -1754,6 +1952,25 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
},
},
},
{
// release-candidate elixir image — pre-fix the matchers stripped the
// "-rc.1" suffix from the elixir-library result and missed the
// elixir-binary entirely (#4819).
logicalFixture: "elixir/1.12.0-rc.1/linux-amd64",
expected: pkg.Package{
Name: "elixir",
Version: "1.12.0-rc.1",
Type: "binary",
PURL: "pkg:generic/elixir@1.12.0-rc.1",
Locations: locations("elixir", "lib/elixir/ebin/elixir.app"),
Metadata: pkg.BinarySignature{
Matches: []pkg.ClassifierMatch{
match("elixir-binary", "elixir"),
match("elixir-library", "lib/elixir/ebin/elixir.app"),
},
},
},
},
{
logicalFixture: "istio_pilot-discovery/1.29.0-alpha.0/linux-amd64",
expected: pkg.Package{
@@ -2227,6 +2444,205 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("envoy-binary"),
},
},
{
logicalFixture: "nginx-ingress-controller/1.15.1/linux-amd64",
expected: pkg.Package{
Name: "nginx-ingress-controller",
Version: "1.15.1",
Type: "binary",
PURL: "pkg:generic/nginx-ingress-controller@1.15.1",
Locations: locations("nginx-ingress-controller"),
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "nginx-ingress-controller/1.11.8/linux-amd64",
expected: pkg.Package{
Name: "nginx-ingress-controller",
Version: "1.11.8",
Type: "binary",
PURL: "pkg:generic/nginx-ingress-controller@1.11.8",
Locations: locations("nginx-ingress-controller"),
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "nginx-ingress-controller/1.9.6/linux-amd64",
expected: pkg.Package{
Name: "nginx-ingress-controller",
Version: "1.9.6",
Type: "binary",
PURL: "pkg:generic/nginx-ingress-controller@1.9.6",
Locations: locations("nginx-ingress-controller"),
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "nginx-ingress-controller/1.7.1/linux-amd64",
expected: pkg.Package{
Name: "nginx-ingress-controller",
Version: "1.7.1",
Type: "binary",
PURL: "pkg:generic/nginx-ingress-controller@1.7.1",
Locations: locations("nginx-ingress-controller"),
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "nginx-ingress-controller/1.12.0-beta.0/linux-amd64",
expected: pkg.Package{
Name: "nginx-ingress-controller",
Version: "1.12.0-beta.0",
Type: "binary",
PURL: "pkg:generic/nginx-ingress-controller@1.12.0-beta.0",
Locations: locations("nginx-ingress-controller"),
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "nginx-ingress-controller/1.2.0-beta.1/linux-amd64",
expected: pkg.Package{
Name: "nginx-ingress-controller",
Version: "1.2.0-beta.1",
Type: "binary",
PURL: "pkg:generic/nginx-ingress-controller@1.2.0-beta.1",
Locations: locations("nginx-ingress-controller"),
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "nginx-ingress-controller/1.0.0-alpha.2/linux-amd64",
expected: pkg.Package{
Name: "nginx-ingress-controller",
Version: "1.0.0-alpha.2",
Type: "binary",
PURL: "pkg:generic/nginx-ingress-controller@1.0.0-alpha.2",
Locations: locations("nginx-ingress-controller"),
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "nginx-ingress-controller/0.34.0/linux-amd64",
expected: pkg.Package{
Name: "nginx-ingress-controller",
Version: "0.34.0",
Type: "binary",
PURL: "pkg:generic/nginx-ingress-controller@0.34.0",
Locations: locations("nginx-ingress-controller"),
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "nginx-ingress-controller/0.33.0/linux-amd64",
expected: pkg.Package{
Name: "nginx-ingress-controller",
Version: "0.33.0",
Type: "binary",
PURL: "pkg:generic/nginx-ingress-controller@0.33.0",
Locations: locations("nginx-ingress-controller"),
Metadata: metadata("ingress-nginx-binary"),
},
},
{
logicalFixture: "julia/1.13.0-alpha2/linux-amd64",
expected: pkg.Package{
Name: "julia",
Version: "1.13.0-alpha2",
Type: "binary",
PURL: "pkg:generic/julia@1.13.0-alpha2",
Locations: locations("libjulia-internal.so.1.13.0"),
Metadata: metadata("julia-binary"),
},
},
{
logicalFixture: "julia/1.12.6/linux-amd64",
expected: pkg.Package{
Name: "julia",
Version: "1.12.6",
Type: "binary",
PURL: "pkg:generic/julia@1.12.6",
Locations: locations("libjulia-internal.so.1.12.6"),
Metadata: metadata("julia-binary"),
},
},
{
logicalFixture: "julia/1.11.9/linux-amd64",
expected: pkg.Package{
Name: "julia",
Version: "1.11.9",
Type: "binary",
PURL: "pkg:generic/julia@1.11.9",
Locations: locations("libjulia-internal.so.1.11.9"),
Metadata: metadata("julia-binary"),
},
},
{
logicalFixture: "julia/1.10.11/linux-amd64",
expected: pkg.Package{
Name: "julia",
Version: "1.10.11",
Type: "binary",
PURL: "pkg:generic/julia@1.10.11",
Locations: locations("libjulia-internal.so.1.10.11"),
Metadata: metadata("julia-binary"),
},
},
{
logicalFixture: "julia/1.9.0-alpha1/linux-amd64",
expected: pkg.Package{
Name: "julia",
Version: "1.9.0-alpha1",
Type: "binary",
PURL: "pkg:generic/julia@1.9.0-alpha1",
Locations: locations("libjulia-internal.so.1.9"),
Metadata: metadata("julia-binary"),
},
},
{
logicalFixture: "julia/1.8.5/linux-amd64",
expected: pkg.Package{
Name: "julia",
Version: "1.8.5",
Type: "binary",
PURL: "pkg:generic/julia@1.8.5",
Locations: locations("libjulia-internal.so.1.8"),
Metadata: metadata("julia-binary"),
},
},
{
// note: dynamic (non-snippet) test case
logicalFixture: "julia/1.5.4/linux-amd64",
expected: pkg.Package{
Name: "julia",
Version: "1.5.4",
Type: "binary",
PURL: "pkg:generic/julia@1.5.4",
Locations: locations("julia", "libjulia.so.1.5"),
Metadata: pkg.BinarySignature{
Matches: []pkg.ClassifierMatch{
match("julia-binary", "julia"),
match("julia-binary", "libjulia.so.1.5"),
},
},
},
},
{
// note: dynamic (non-snippet) test case
logicalFixture: "julia/1.3.1/linux-amd64",
expected: pkg.Package{
Name: "julia",
Version: "1.3.1",
Type: "binary",
PURL: "pkg:generic/julia@1.3.1",
Locations: locations("julia", "libjulia.so.1.3"),
Metadata: pkg.BinarySignature{
Matches: []pkg.ClassifierMatch{
match("julia-binary", "julia"),
match("julia-binary", "libjulia.so.1.3"),
},
},
},
},
}
for _, test := range tests {
@@ -2248,9 +2664,16 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
packages, _, err := c.Catalog(context.Background(), resolver)
require.NoError(t, err)
require.Len(t, packages, 1, "mismatched package count")
expected := test.expectedPackages
if len(expected) == 0 {
expected = []pkg.Package{test.expected}
}
assertPackagesAreEqual(t, test.expected, packages[0])
require.Len(t, packages, len(expected), "mismatched package count")
for i := range expected {
assertPackagesAreEqual(t, expected[i], packages[i])
}
})
}
}
+222 -25
View File
@@ -88,8 +88,43 @@ func DefaultClassifiers() []binutils.Classifier {
{
Class: "julia-binary",
FileGlob: "**/libjulia-internal.so",
EvidenceMatcher: m.FileContentsVersionMatcher(
`(?m)__init__\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00verify`),
EvidenceMatcher: binutils.MatchAny(
// �1.13.0-beta3[NUL]branch
// [NUL]GIT_VERSION_INFO[NUL]jl_image_unpack[NUL]1.13.0-alpha2[NUL]branch
// [NUL]GIT_VERSION_INFO[NUL]__init__[NUL]1.12.6[NUL]branch[NUL]commit
// [NUL]GIT_VERSION_INFO[NUL]__init__[NUL]verify_methods[NUL]1.11.9[NUL]branch[NUL]commit
// [NUL][NUL]__init__[NUL]1.10.11[NUL]verify_methods[NUL]
m.FileContentsVersionMatcher(`\x00__init__\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00(branch|verify_methods)`),
m.FileContentsVersionMatcher(`(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00branch\x00`),
// [NUL]verify_methods[NUL]Task cannot be serialized[NUL]1.9.0-alpha1[NUL]BigInt[NUL]
m.FileContentsVersionMatcher(`\x00verify_methods\x00.{0,30}(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00BigInt`),
// unknown option `%s`[NUL]1.8.5[NUL]julia version %s
m.FileContentsVersionMatcher(`\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00julia version`),
),
Package: "julia",
PURL: mustPURL("pkg:generic/julia@version"),
CPEs: singleCPE("cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "julia-binary",
FileGlob: "**/julia",
EvidenceMatcher: binutils.SharedLibraryLookup(
// libjulia.so.1
// libjulia.so.0.6
// libjulia.so
`^libjulia\.so(\.[0-9])?(\.[0-9])?$`,
binutils.MatchAny(
// unknown option `%s`[NUL]1.5.4[NUL]julia version %s
m.FileContentsVersionMatcher(`\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00julia version`),
// [NUL]#kw#[NUL]1.3.1[NUL]BigInt
// [NUL]#kw#[NUL]0.7.0-beta2[NUL]_require_dependencies
m.FileContentsVersionMatcher(`\x00#kw#\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00(BigInt|_require_dependencies)`),
// [NUL]ObjectIdDict[NUL]0.6.4[NUL]jl_sysimg_cpu_target
m.FileContentsVersionMatcher(`\x00ObjectIdDict\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00jl_sysimg_cpu_target`),
// [NUL]require[NUL]0.4.6[NUL]core2
m.FileContentsVersionMatcher(`\x00require\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00core2`),
),
),
Package: "julia",
PURL: mustPURL("pkg:generic/julia@version"),
CPEs: singleCPE("cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
@@ -97,8 +132,17 @@ func DefaultClassifiers() []binutils.Classifier {
{
Class: "helm",
FileGlob: "**/helm",
EvidenceMatcher: m.FileContentsVersionMatcher(
`(?m)\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`),
EvidenceMatcher: binutils.MatchAny(
// [NUL]v1.21.2[NUL].......[NUL][NUL]v4.1.4[NUL][NUL][NUL]
// [NUL]v2.0.0-beta.2[NUL][NUL][NUL]
m.FileContentsVersionMatcher(`\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]|-beta\.[0-9]|-rc\.[0-9])?)\x00{2,}`),
// [NUK]'[DLE]v3.12.0[NUL][NUL]...go1.20.3[NUL][NUL]
m.FileContentsVersionMatcher(`v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]|-beta\.[0-9]|-rc\.[0-9])?)\x00+.{1,500}go[0-9]+\.[0-9]+\.[0-9]+\x00+`),
// [NUL]v3.11.1[NUL]�[NUL]
m.FileContentsVersionMatcher(`\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]|-beta\.[0-9]|-rc\.[0-9])?)\x00`),
// [NUL]@�@v3.15.2[NUL][NUL]
m.FileContentsVersionMatcher(`@v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]|-beta\.[0-9]|-rc\.[0-9])?)\x00`),
),
Package: "helm",
PURL: mustPURL("pkg:golang/helm.sh/helm@version"),
CPEs: singleCPE("cpe:2.3:a:helm:helm:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
@@ -106,13 +150,19 @@ func DefaultClassifiers() []binutils.Classifier {
{
Class: "redis-binary",
FileGlob: "**/redis-server",
EvidenceMatcher: binutils.MatchAny(
// matches most recent versions of redis (~v7), e.g. "7.0.14buildkitsandbox-1702957741000000000"
m.FileContentsVersionMatcher(`[^\d](?P<version>\d+.\d+\.\d+)buildkitsandbox-\d+`),
// matches against older versions of redis (~v3 - v6), e.g. "4.0.11841ce7054bd9-1542359302000000000"
m.FileContentsVersionMatcher(`[^\d](?P<version>[0-9]+\.[0-9]+\.[0-9]+)\w{12}-\d+`),
// matches against older versions of redis (~v2), e.g. "Server started, Redis version 2.8.23"
m.FileContentsVersionMatcher(`Redis version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
EvidenceMatcher: binutils.MatchAll(
// Negative Matchers to exclude valkey-server
binutils.MatchNone(
binutils.MatchPath("**/valkey-server"),
),
binutils.MatchAny(
// matches most recent versions of redis (~v7), e.g. "7.0.14buildkitsandbox-1702957741000000000"
m.FileContentsVersionMatcher(`[^\d](?P<version>\d+.\d+\.\d+)buildkitsandbox-\d+`),
// matches against older versions of redis (~v3 - v6), e.g. "4.0.11841ce7054bd9-1542359302000000000"
m.FileContentsVersionMatcher(`[^\d](?P<version>[0-9]+\.[0-9]+\.[0-9]+)\w{12}-\d+`),
// matches against older versions of redis (~v2), e.g. "Server started, Redis version 2.8.23"
m.FileContentsVersionMatcher(`Redis version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
),
),
Package: "redis",
PURL: mustPURL("pkg:generic/redis@version"),
@@ -288,6 +338,96 @@ func DefaultClassifiers() []binutils.Classifier {
cpe.Must("cpe:2.3:a:percona:xtradb_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
{
// Legacy MySQL Cluster contains both MySQL Server and MySQL Cluster versions (Example: 5.7.33-ndb-7.5.21)
// This classifier identifies the MySQL Server version of the mysqld binary (5.7.33 in the example above).
Class: "mysqld-mysql-cluster-legacy-binary",
FileGlob: "**/mysqld",
EvidenceMatcher: m.FileContentsVersionMatcher(
`cluster-gpl\x00(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?)\-ndb\-[0-9]+(\.[0-9]+)?(\.[0-9]+)?`),
Package: "mysql-server",
PURL: mustPURL("pkg:generic/mysql-server@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
{
Class: "mysqld-binary",
FileGlob: "**/mysqld",
EvidenceMatcher: binutils.BranchingEvidenceMatcher([]binutils.Classifier{
{
// Legacy MySQL Cluster contains both MySQL Server and MySQL Cluster versions (Example: 5.7.33-ndb-7.5.21)
// This classifier identifies the MySQL Cluster version of the mysqld binary (7.5.21 in the example above).
Class: "mysqld-mysql-cluster-legacy-binary",
EvidenceMatcher: m.FileContentsVersionMatcher(
`cluster-gpl\x00[0-9]+(\.[0-9]+)?(\.[0-9]+)?\-ndb\-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?)`),
Package: "mysql-cluster",
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
{
// mysqld from MySQL Cluster after versioning was aligned with MySQL Server
Class: "mysqld-mysql-cluster-binary",
EvidenceMatcher: m.FileContentsVersionMatcher(
`/mysql-cluster-gpl-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
Package: "mysql-cluster",
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
{
// mysqld from MySQL Server
Class: "mysqld-mysql-server-binary",
EvidenceMatcher: m.FileContentsVersionMatcher(
`/mysql-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
Package: "mysql-server",
PURL: mustPURL("pkg:generic/mysql-server@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
}...),
},
{
Class: "ndbd-binary",
FileGlob: "**/ndbd",
EvidenceMatcher: m.FileContentsVersionMatcher(
`/mysql-cluster-gpl-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
Package: "mysql-cluster",
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
{
Class: "ndbmtd-binary",
FileGlob: "**/ndbmtd",
EvidenceMatcher: m.FileContentsVersionMatcher(
`/mysql-cluster-gpl-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
Package: "mysql-cluster",
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
{
Class: "ndb_mgmd-binary",
FileGlob: "**/ndb_mgmd",
EvidenceMatcher: m.FileContentsVersionMatcher(
`/mysql-cluster-gpl-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
Package: "mysql-cluster",
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
},
{
Class: "xtrabackup-binary",
FileGlob: "**/xtrabackup",
@@ -427,10 +567,26 @@ func DefaultClassifiers() []binutils.Classifier {
{
Class: "deno-binary",
FileGlob: "**/deno",
EvidenceMatcher: m.FileContentsVersionMatcher(
// Deno/2.6.3
// Deno/1.41.0
`Deno/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
EvidenceMatcher: binutils.MatchAny(
m.FileContentsVersionMatcher(
// Deno/2.6.3
// Deno/1.41.0
`Deno/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
),
m.FileContentsVersionMatcher(
// deno::tools::standalonedeno-65db94feba9d4d51a09b74629f566dbc90484fbarelease/v1.29.4windows
// cli/tools/standalone.rsdeno-74064c9d8c222b33b2a552ea0af1054f57002a96release/v1.28.3windows
`deno-[0-9a-z]{40}release/v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
),
m.FileContentsVersionMatcher(
// cli/tools/standalone.rsdeno-ab286750a8c87215a9651efb11fcc620f29140051.16.4release/vdlwindows
`deno-[0-9a-z]{40}(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
),
m.FileContentsVersionMatcher(
// 1.10.31567c1013cc8ff12cf039137792da66a1d0015b5DENO_UNSTABLE_COVERAGE_DIRNo current directorycli/main
`(?P<version>[0-9]+\.[0-9]+\.[0-9]+)[0-9a-z]{40}DENO`,
),
),
Package: "deno",
PURL: mustPURL("pkg:generic/deno@version"),
CPEs: singleCPE("cpe:2.3:a:deno:deno:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
@@ -558,14 +714,29 @@ func DefaultClassifiers() []binutils.Classifier {
{
Class: "openssl-binary",
FileGlob: "**/openssl",
EvidenceMatcher: m.FileContentsVersionMatcher(
// [NUL]OpenSSL 3.1.4'
// [NUL]OpenSSL 1.1.1w'
`\x00OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+([a-z]+|-alpha[0-9]|-beta[0-9]|-rc[0-9])?)`,
),
Package: "openssl",
PURL: mustPURL("pkg:generic/openssl@version"),
CPEs: singleCPE("cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
EvidenceMatcher: binutils.BranchingEvidenceMatcher([]binutils.Classifier{
{
Class: "openssl-binary-aws-lc",
EvidenceMatcher: m.FileContentsVersionMatcher(
// [NUL]OpenSSL 1.1.1 (compatible; AWS-LC 1.69.0)[NUL]
`AWS-LC (?P<version>[0-9]+\.[0-9]+\.[0-9]+)\)\x00`,
),
Package: "aws-lc",
PURL: mustPURL("pkg:generic/aws-lc@version"),
CPEs: singleCPE("cpe:2.3:a:amazon:aws_libcrypto:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "openssl-binary",
EvidenceMatcher: m.FileContentsVersionMatcher(
// [NUL]OpenSSL 3.1.4'
// [NUL]OpenSSL 1.1.1w'
`\x00OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+([a-z]+|-alpha[0-9]|-beta[0-9]|-rc[0-9])?)`,
),
Package: "openssl",
PURL: mustPURL("pkg:generic/openssl@version"),
CPEs: singleCPE("cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
}...),
},
{
Class: "openldap-search-binary",
@@ -762,7 +933,9 @@ func DefaultClassifiers() []binutils.Classifier {
Class: "elixir-binary",
FileGlob: "**/elixir",
EvidenceMatcher: m.FileContentsVersionMatcher(
`(?m)ELIXIR_VERSION=(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
// Capture optional pre-release suffix (-rc.1, -alpha.0, -beta.2,
// etc.) so release-candidate elixir images (#4819) match.
`(?m)ELIXIR_VERSION=(?P<version>[0-9]+\.[0-9]+\.[0-9]+(?:-[a-z0-9]+(?:\.[0-9]+)?)?)`),
Package: "elixir",
PURL: mustPURL("pkg:generic/elixir@version"),
CPEs: []cpe.CPE{
@@ -773,7 +946,8 @@ func DefaultClassifiers() []binutils.Classifier {
Class: "elixir-library",
FileGlob: "**/elixir/ebin/elixir.app",
EvidenceMatcher: m.FileContentsVersionMatcher(
`(?m)\{vsn,"(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9]+)?)"\}`),
// Same pre-release extension as elixir-binary above.
`(?m)\{vsn,"(?P<version>[0-9]+\.[0-9]+\.[0-9]+(?:-[a-z0-9]+(?:\.[0-9]+)?)?)"\}`),
Package: "elixir",
PURL: mustPURL("pkg:generic/elixir@version"),
CPEs: singleCPE("cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
@@ -905,6 +1079,29 @@ func DefaultClassifiers() []binutils.Classifier {
PURL: mustPURL("pkg:generic/mongodb@version"),
CPEs: singleCPE("cpe:2.3:a:mongodb:mongodb:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
{
Class: "ingress-nginx-binary",
FileGlob: "**/nginx-ingress-controller",
EvidenceMatcher: binutils.MatchAny(
// [NUL][NUL]v1.15.1[NUL][NUL]@e[ETX][NUL][NUL][NUL][NUL]go1.26.1[NUL][NUL][NUL]
// �v1.15.1[NUL][NUL]�z[ETX][NUL][NUL][NUL][NUL]go1.24.4[NUL][NUL][NUL]
m.FileContentsVersionMatcher(`v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00+.{0,50}go[0-9]+\.[0-9]+(\-(alpha|beta)\.[0-9])?\.[0-9]+\x00+`),
// �Lv1.9.6[NUL][NUL]$a�c[SOH][NUL][NUL][NUL]
// [NUL][NUL]v0.34.0[NUL]......�$a�...[NUL]
m.FileContentsVersionMatcher(`v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\-(alpha|beta)\.[0-9])?)\x00+.{0,800}\$a.{0,10}\x00+`),
// [NUL][NUL]v1.7.1[NUL][NUL][NUL]...S=v<y5...
// [NUL]0.33.0[NUL][NUL]...[NUL][NUL]...S=v<y5
m.FileContentsVersionMatcher(`\x00+v?(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\-(alpha|beta)\.[0-9])?)\x00+.{0,100}S=v<y5`),
// [NUL][NUL]go1.22.8[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]v1.12.0-beta.0[NUL][NUL]
m.FileContentsVersionMatcher(`\x00+go[0-9]+\.[0-9]+\.[0-9]+\x00+v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\-(alpha|beta)\.[0-9])?)\x00+`),
// [NUL][NUL]v1.2.0-beta.1[NUL][NUL]
// [NUL][NUL]v1.0.0-alpha.2[NUL][NUL]
m.FileContentsVersionMatcher(`\x00+v(?P<version>[0-9]+\.[0-9]+\.[0-9]+\-(alpha|beta)\.[0-9])\x00+`),
),
Package: "nginx-ingress-controller",
PURL: mustPURL("pkg:generic/nginx-ingress-controller@version"),
CPEs: singleCPE("cpe:2.3:a:kubernetes:ingress-nginx:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
},
}
return append(classifiers, defaultJavaClassifiers()...)
@@ -275,9 +275,15 @@ func copyBinaryFromContainer(containerName, containerPath, destinationPath, dige
return err
}
// ensure permissions are 600 for destination
if err := os.Chmod(destinationPath, 0600); err != nil {
return fmt.Errorf("unable to set permissions on file %q: %w", destinationPath, err)
// ensure permissions are 600 for destination (if it is not a symlink)
info, err := os.Lstat(destinationPath)
if err != nil {
return fmt.Errorf("unable to stat file %q: %w", destinationPath, err)
}
if info.Mode()&os.ModeSymlink == 0 {
if err := os.Chmod(destinationPath, 0600); err != nil {
return fmt.Errorf("unable to set permissions on file %q: %w", destinationPath, err)
}
}
// capture digest file
@@ -0,0 +1,9 @@
name: deno
offset: 31454975
length: 120
snippetSha256: 30ab01cb89ba17c770cca7d60e12f2c2119fd8db2b389636053bad1146ac83df
fileSha256: 68ec2c702e9c21e47d5557c603080932ffa24627b463b4a5a4e2ed4ff00f7d5d
### byte snippet to follow ###
s already installed
1.10.31567c1013cc8ff12cf039137792da66a1d0015b5DENO_UNSTABLE_COVERAGE_DIRNo current directorycli/main
@@ -0,0 +1,9 @@
name: deno
offset: 4219127
length: 120
snippetSha256: 97e369d4eed74c5b1ff7d578c9de100c71136dfcc54f19455b2938671d1a9640
fileSha256: f3af5cf3838c0cd01de1acaaa716de804ad08c716927af1848aa9664b96a737c
### byte snippet to follow ###
g ctrl+d or close()
Error: cli/tools/standalone.rsdeno-ab286750a8c87215a9651efb11fcc620f29140051.16.4release/vdlwindowsh
@@ -0,0 +1,9 @@
name: deno
offset: 5079501
length: 110
snippetSha256: aa6181b1204d821493756007090ad81677a26f20de0efe9c53bdf401b54849fd
fileSha256: 3373cbed016860095b01f693a58181c4cf1ac9d6ab7bd5dbca5f684788402919
### byte snippet to follow ###
n to exit
cli/tools/standalone.rsdeno-74064c9d8c222b33b2a552ea0af1054f57002a96release/v1.28.3windowshttps://dl
@@ -0,0 +1,9 @@
name: deno
offset: 5116362
length: 220
snippetSha256: 786a6c5d8be76e3c6e628cd2400c6ed5a7a7cfc75b135e8e7f1375f55dc28787
fileSha256: d9c8a385c3704e220b1722c642f22e32f8f267013742f9b44d68c67bb5f9232d
### byte snippet to follow ###
ument. For example:
deno run --allow-read=. main.js./$deno$eval.console.log(cli/tools/standalone.rsCompiledeno::tools::standalonedeno-65db94feba9d4d51a09b74629f566dbc90484fbarelease/v1.29.4windowshttps://dl.deno.land
@@ -0,0 +1,20 @@
#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: 2021 The Elixir Team
# SPDX-FileCopyrightText: 2012 Plataformatec
set -e
ELIXIR_VERSION=1.12.0-rc.1
if [ $# -eq 0 ] || { [ $# -eq 1 ] && { [ "$1" = "--help" ] || [ "$1" = "-h" ]; }; }; then
cat <<USAGE >&2
Usage: $(basename "$0") [options] [.exs file] [data]
## General options
-e "COMMAND" Evaluates the given command (*)
-h, --help Prints this message (standalone)
-r "FILE" Requires the given files/patterns (*)
-S SCRIPT Finds and executes the given script in \$PATH
@@ -0,0 +1,19 @@
{application,elixir,
[{description,"elixir"},
{vsn,"1.12.0-rc.1"},
{modules,
['Elixir.Access','Elixir.Agent.Server','Elixir.Agent',
'Elixir.Application','Elixir.ArgumentError',
elixir_overridable,elixir_parser,elixir_quote,elixir_rewrite,
elixir_sup,elixir_tokenizer,elixir_utils,iex]},
{registered,[elixir_sup,elixir_config,elixir_code_server]},
{applications,[kernel,stdlib,compiler]},
{mod,{elixir,[]}},
{env,
[{ansi_syntax_colors,
[{atom,cyan},
{binary,default_color},
{operator,default_color}]},
{check_endianness,true},
{dbg_callback,{'Elixir.Macro',dbg,[]}},
{time_zone_database,'Elixir.Calendar.UTCOnlyTimeZoneDatabase'}]}]}.
@@ -0,0 +1 @@
libjulia-internal.so.1.10.11
@@ -0,0 +1 @@
libjulia-internal.so.1.11.9
@@ -0,0 +1 @@
libjulia-internal.so.1.12.6
@@ -0,0 +1 @@
libjulia-internal.so.1.13.0
@@ -0,0 +1 @@
libjulia-internal.so.1.13.0
@@ -0,0 +1 @@
libjulia-internal.so.1.8
@@ -0,0 +1 @@
libjulia-internal.so.1.9
+286 -16
View File
@@ -123,6 +123,38 @@ from-images:
paths:
- /usr/lib/dart/bin/dart
- name: deno
version: 1.10.3
images:
- ref: denoland/deno:1.10.3@sha256:9687db39d68333fce31f371734a1b982092507606508289a5c7a24cfc5fe6ee2
platform: linux/amd64
paths:
- /usr/bin/deno
- name: deno
version: 1.16.4
images:
- ref: denoland/deno:1.16.4@sha256:027868eb6f079ef290957bcda05280a6b08ff86baf549bc6eff5c17467a44d41
platform: linux/amd64
paths:
- /usr/bin/deno
- name: deno
version: 1.28.3
images:
- ref: denoland/deno:1.28.3@sha256:8636e6ac55fbd4687c111eb4b798b1772d43874c53647ca4a2bad6d1962643f0
platform: linux/amd64
paths:
- /usr/bin/deno
- name: deno
version: 1.29.4
images:
- ref: denoland/deno:1.29.4@sha256:f5b5a4678b18884724b277a4eb5490a978eab2da5a47461766df9fb59ebb08b6
platform: linux/amd64
paths:
- /usr/bin/deno
- name: deno
version: 1.41.0
images:
@@ -312,20 +344,52 @@ from-images:
paths:
- /usr/bin/mysql
# TODO: add pattern for mysqld
# - version: 5.6.51
# images:
# - ref: mysql:5.6.51@sha256:897086d07d1efa876224b147397ea8d3147e61dd84dce963aace1d5e9dc2802d
# platform: linux/amd64
# paths:
# - /usr/sbin/mysqld
#
# - version: 8.0.34
# images:
# - ref: mysql:8.0.34@sha256:8b8835a2c32cd7357a5d2ea4b49ad870ff519c8c1d4add362803feddf4a0a973
# platform: linux/amd64
# paths:
# - /usr/sbin/mysqld
- version: 9.7.0
images:
- ref: mysql:9.7.0@sha256:6e5e46e6aece0bc8edb5abecc6fd726653f36447860f7f4dbf3481c91b477f69
platform: linux/amd64
paths:
- /usr/sbin/mysqld
- name: mysql-cluster
version: 9.7.0
images:
- ref: container-registry.oracle.com/mysql/community-cluster:9.7.0
platform: linux/amd64
paths:
- /usr/sbin/mysqld
- name: mysql-cluster
version: 7.6.17
images:
- ref: container-registry.oracle.com/mysql/community-cluster:7.6.17
platform: linux/amd64
paths:
- /usr/sbin/mysqld
- name: ndbd
version: 9.7.0
images:
- ref: container-registry.oracle.com/mysql/community-cluster:9.7.0
platform: linux/amd64
paths:
- /usr/sbin/ndbd
- name: ndbmtd
version: 9.7.0
images:
- ref: container-registry.oracle.com/mysql/community-cluster:9.7.0
platform: linux/amd64
paths:
- /usr/sbin/ndbmtd
- name: ndb_mgmd
version: 9.7.0
images:
- ref: container-registry.oracle.com/mysql/community-cluster:9.7.0
platform: linux/amd64
paths:
- /usr/sbin/ndb_mgmd
- name: mongodb
version: 8.0.17
@@ -625,12 +689,14 @@ from-images:
paths:
- /usr/local/bin/redis-server
- version: 9.0.0
- name: valkey-server
version: 9.0.0
images:
- ref: valkey/valkey:9.0.0@sha256:42ea97850708540d4e05f6241cfbd241c1ba502e64d9a42efb2c2e277a8ca9d6
platform: linux/amd64
paths:
- /usr/local/bin/valkey-server
- /usr/local/bin/redis-server
- version: 2.9.0
images:
@@ -802,6 +868,27 @@ from-images:
- /usr/local/lib/libruby.so.2.6.10
- /usr/local/lib/libruby.so.2.6
- version: 4.1.4
images:
- ref: alpine/helm:4.1.4@sha256:d69c97f71f38128c3ed4b8bcd70b4de65efad9e09494c85d158661e7ffd8b719
platform: linux/s390x
paths:
- /usr/bin/helm
- version: 3.15.2
images:
- ref: alpine/helm:3.15.2@sha256:61185768b41810085cb57a9bc8de9003bea78ac0dd417e5b0b16223a6a0aa812
platform: linux/amd64
paths:
- /usr/bin/helm
- version: 3.12.0
images:
- ref: alpine/helm:3.12.0@sha256:6f6e18df0710af82188ab8b27d3890b6d519c501c0982f3a76f3fdbff39ff3c7
platform: linux/s390x
paths:
- /usr/bin/helm
- version: 3.11.1
images:
- ref: alpine/helm:3.11.1@sha256:8628e3695fb743a8b9de89626f1b7a221280c2152c0e288c2504e59b68233e8b
@@ -816,6 +903,27 @@ from-images:
paths:
- /usr/local/bin/helm
- version: 3.0.0-alpha.1
images:
- ref: alpine/helm:3.0.0-alpha.1@sha256:95bd39add326ac58a333815666aace6d4a28606a79eef96d4e44287b11540b69
platform: linux/amd64
paths:
- /usr/bin/helm
- version: 2.17.0-rc.1
images:
- ref: alpine/helm:2.17.0-rc.1@sha256:3938b9f291794fe88489dae2a87e0ad9d634464c686186d4a3ca33beef84278d
platform: linux/amd64
paths:
- /usr/bin/helm
- version: 2.0.0-beta.2
images:
- ref: alpine/helm:2.0.0-beta.2@sha256:97d97b27a723b28c459d6c92e7b0591c553e88e483a248235fbdaa4dacb2e6d5
platform: linux/amd64
paths:
- /usr/bin/helm
- version: 1.15.2
images:
- ref: hashicorp/consul:1.15.2@sha256:c2169f3bb18dd947ae8eb5f6766896695c71fb439f050a3343e0007d895615b8
@@ -917,11 +1025,19 @@ from-images:
- name: openssl
version: 1.1.1w
images:
- ref: openresty/openresty:1.25.3.1-2-alpine@sha256:b1cb45b1556801b8cb1bc29ea78faf2eaf67926a37b052dd070866e44d7df07a
- ref: openresty/openresty:1.25.3.1-2-alpine@sha256:6c24f25318e5ecf0c981bec584842af329552a538fe48a24ec25387374c652b2
platform: linux/arm64
paths:
- /usr/local/openresty/openssl/bin/openssl
- name: aws-lc
version: 1.69.0
images:
- ref: haproxytech/haproxy-alpine:3.3.8@sha256:ba237771cc7e734b4449829b09d107012c8d7578d77487b8453207e7ca851996
platform: linux/amd64
paths:
- /opt/aws-lc/bin/openssl
- name: openldap
version: 2.6.10
images:
@@ -1379,3 +1495,157 @@ from-images:
platform: linux/amd64
paths:
- /usr/local/bin/envoy
- version: 1.15.1
images:
- ref: registry.k8s.io/ingress-nginx/controller:v1.15.1@sha256:594ceea76b01c592858f803f9ff4d2cb40542cae2060410b2c95f75907d659e1
platform: linux/amd64
paths:
- /nginx-ingress-controller
- version: 1.11.8
images:
- ref: registry.k8s.io/ingress-nginx/controller:v1.11.8@sha256:695d79381ee6af00c7f5c9fd434f50851d7d32838ad5b2c507e416cf2084fc79
platform: linux/amd64
paths:
- /nginx-ingress-controller
- version: 1.9.6
images:
- ref: registry.k8s.io/ingress-nginx/controller:v1.9.6@sha256:1405cc613bd95b2c6edd8b2a152510ae91c7e62aea4698500d23b2145960ab9c
platform: linux/amd64
paths:
- /nginx-ingress-controller
- version: 1.8.4
images:
- ref: registry.k8s.io/ingress-nginx/controller:v1.8.4@sha256:8d8ddf32b83ca3e74bd5f66369fa60d85353e18ff55fa7691b321aa4716f5ba9
platform: linux/amd64
paths:
- /nginx-ingress-controller
- version: 1.7.1
images:
- ref: registry.k8s.io/ingress-nginx/controller:v1.7.1@sha256:7244b95ea47bddcb8267c1e625fb163fc183ef55448855e3ac52a7b260a60407
platform: linux/amd64
paths:
- /nginx-ingress-controller
- version: 1.12.0-beta.0
images:
- ref: registry.k8s.io/ingress-nginx/controller:v1.12.0-beta.0@sha256:9724476b928967173d501040631b23ba07f47073999e80e34b120e8db5f234d5
platform: linux/amd64
paths:
- /nginx-ingress-controller
- version: 1.2.0-beta.1
images:
- ref: k8s.gcr.io/ingress-nginx/controller:v1.2.0-beta.1@sha256:7059739637c30865f74cae403fffa55c2cb6d9779cd15654480dd0e4f850d536
platform: linux/amd64
paths:
- /nginx-ingress-controller
- version: 1.0.0-alpha.2
images:
- ref: k8s.gcr.io/ingress-nginx/controller:v1.0.0-alpha.2@sha256:04a0ad3a1279c2a58898e789eed767eafa138ee1e5b9b23a988c6e8485cf958d
platform: linux/amd64
paths:
- /nginx-ingress-controller
- version: 0.34.0
images:
- ref: k8s.gcr.io/ingress-nginx/controller:v0.34.0@sha256:5e0dcdc3ddb076f9ad988c624b8c459b53235ace26b9b18c192dedd3a038173c
platform: linux/amd64
paths:
- /nginx-ingress-controller
- version: 0.33.0
images:
- ref: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.33.0@sha256:ad8a1e9e44b4deb6ded8d677e9f0014178294e579d0710a48bca1e05838f2473
platform: linux/amd64
paths:
- /nginx-ingress-controller
- name: julia
version: 1.13.0-beta3
images:
- ref: library/julia:1.13.0-beta3@sha256:3b36013b071d9abc9bdedb66a4230a9445bc692aa2c49f85852b94e81c7f0a74
platform: linux/amd64
paths:
- /usr/local/julia/lib/julia/libjulia-internal.so
- /usr/local/julia/lib/julia/libjulia-internal.so.1.13.0
- name: julia
version: 1.13.0-alpha2
images:
- ref: library/julia:1.13.0-alpha2@sha256:f1b1cd24979025371be317353d0d9071e9b549583d2b13783cbafd40b8d4e319
platform: linux/amd64
paths:
- /usr/local/julia/lib/julia/libjulia-internal.so
- /usr/local/julia/lib/julia/libjulia-internal.so.1.13.0
- name: julia
version: 1.12.6
images:
- ref: library/julia:1.12.6@sha256:54d6a3f40bbd76021e6ca8fab2dd2af08cc5388b078223d3040eaf3567b4ccd3
platform: linux/amd64
paths:
- /usr/local/julia/lib/julia/libjulia-internal.so
- /usr/local/julia/lib/julia/libjulia-internal.so.1.12.6
- name: julia
version: 1.11.9
images:
- ref: library/julia:1.11.9@sha256:18e83baa9277c2ac94bef267d585634760b5adf9bc7343480161c7b09034b452
platform: linux/amd64
paths:
- /usr/local/julia/lib/julia/libjulia-internal.so
- /usr/local/julia/lib/julia/libjulia-internal.so.1.11.9
- name: julia
version: 1.10.11
images:
- ref: library/julia:1.10.11@sha256:2f27b44f3ecda0f941c1b475b8967fa933fbf52b5f877f69bfe4df959c56df66
platform: linux/amd64
paths:
- /usr/local/julia/lib/julia/libjulia-internal.so
- /usr/local/julia/lib/julia/libjulia-internal.so.1.10.11
- name: julia
version: 1.9.0-alpha1
images:
- ref: library/julia:1.9.0-alpha1@sha256:fb932603485d7cea435e85c073c04bdf4d0b90232c03013585aa0f65c8216111
platform: linux/amd64
paths:
- /usr/local/julia/lib/julia/libjulia-internal.so
- /usr/local/julia/lib/julia/libjulia-internal.so.1.9
- name: julia
version: 1.8.5
images:
- ref: library/julia:1.8.5@sha256:5edccd0ca0b5f00d78beca0bd8217d65b2c24ef051ad07e2b08ed67224ff905a
platform: linux/amd64
paths:
- /usr/local/julia/lib/julia/libjulia-internal.so
- /usr/local/julia/lib/julia/libjulia-internal.so.1.8
- name: julia
version: 1.5.4
images:
- ref: library/julia:1.5.4@sha256:39c3f721aa5ede195965e7f20575b1ee5949bdd2b42204e227d98114bcc94b83
platform: linux/amd64
paths:
- /usr/local/julia/bin/julia
- /usr/local/julia/lib/libjulia.so.1
- /usr/local/julia/lib/libjulia.so.1.5
- name: julia
version: 1.3.1
images:
- ref: library/julia:1.3.1@sha256:a14f56f9f2ed44559d9f435cc346ede09a7d61afcbac61ba037acd2b39b56171
platform: linux/amd64
paths:
- /usr/local/julia/bin/julia
- /usr/local/julia/lib/libjulia.so.1
- /usr/local/julia/lib/libjulia.so.1.3
+71 -48
View File
@@ -18,71 +18,60 @@ var (
licensePattern = regexp.MustCompile(`^License: (?P<license>\S*)`)
commonLicensePathPattern = regexp.MustCompile(`/usr/share/common-licenses/(?P<license>[0-9A-Za-z_.\-]+)`)
licenseAgreementHeadingPattern = regexp.MustCompile(`(?i)^\s*(?P<license>LICENSE AGREEMENT(?: FOR .+?)?)\s*$`)
formatHeaderPattern = regexp.MustCompile(`^Format:\s*https?://www\.debian\.org/doc/packaging-manuals/copyright-format/`)
)
// heading-detection states. Replaces licenseFirstSentenceAfterHeadingPattern,
// which only matched at the start of the file: a non-empty heading, a line
// of dashes, blank lines, then text up to the first period.
const (
expectHeading = iota
expectDashes
skipBlanks
captureLicense
headingDone // matched or impossible — stop checking
)
func parseLicensesFromCopyright(reader io.Reader) []string {
findings := strset.New()
scanner := bufio.NewScanner(reader)
// State machine replacing licenseFirstSentenceAfterHeadingPattern.
// That regex only matched at the start of the file: a non-empty heading,
// a line of dashes, blank lines, then text up to the first period.
const (
expectHeading = iota
expectDashes
skipBlanks
captureLicense
headingDone // matched or impossible — stop checking
)
// per the DEP-5 spec, machine-readable copyright files MUST have a
// Format field whose value is a URI for the specification. Only files
// with this header should be parsed as machine-readable.
// See: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
formatVerified := false
headingState := expectHeading
var licenseText strings.Builder
for scanner.Scan() {
line := scanner.Text()
// per-line regex checks (applied to every line)
if value := findLicenseClause(licensePattern, line); value != "" {
findings.Add(value)
}
if value := findLicenseClause(commonLicensePathPattern, line); value != "" {
findings.Add(value)
}
if value := findLicenseClause(licenseAgreementHeadingPattern, line); value != "" {
findings.Add(value)
if !formatVerified {
if strings.TrimSpace(line) == "" {
continue
}
if !formatHeaderPattern.MatchString(line) {
return nil
}
formatVerified = true
}
// multi-line heading detection (only at start of file)
switch headingState {
case expectHeading:
if strings.TrimSpace(line) != "" {
headingState = expectDashes
} else {
headingState = headingDone
}
case expectDashes:
trimmed := strings.TrimSpace(line)
if len(trimmed) > 0 && strings.Trim(trimmed, "-") == "" {
headingState = skipBlanks
} else {
headingState = headingDone
}
case skipBlanks:
if strings.TrimSpace(line) != "" {
headingState = captureLicense
licenseText.WriteString(line)
if value := extractUpToFirstPeriod(licenseText.String()); value != "" {
findings.Add(value)
headingState = headingDone
}
}
case captureLicense:
licenseText.WriteString(" ")
licenseText.WriteString(line)
if value := extractUpToFirstPeriod(licenseText.String()); value != "" {
for _, p := range []*regexp.Regexp{licensePattern, commonLicensePathPattern, licenseAgreementHeadingPattern} {
if value := findLicenseClause(p, line); value != "" {
findings.Add(value)
headingState = headingDone
}
}
var found string
headingState, found = advanceHeadingState(headingState, line, &licenseText)
if found != "" {
findings.Add(found)
}
}
if !formatVerified {
return nil
}
results := findings.List()
@@ -91,6 +80,40 @@ func parseLicensesFromCopyright(reader io.Reader) []string {
return results
}
func advanceHeadingState(state int, line string, licenseText *strings.Builder) (int, string) {
switch state {
case expectHeading:
if strings.TrimSpace(line) != "" {
return expectDashes, ""
}
return headingDone, ""
case expectDashes:
trimmed := strings.TrimSpace(line)
if len(trimmed) > 0 && strings.Trim(trimmed, "-") == "" {
return skipBlanks, ""
}
return headingDone, ""
case skipBlanks:
if strings.TrimSpace(line) == "" {
return state, ""
}
licenseText.WriteString(line)
if value := extractUpToFirstPeriod(licenseText.String()); value != "" {
return headingDone, value
}
return captureLicense, ""
case captureLicense:
licenseText.WriteString(" ")
licenseText.WriteString(line)
if value := extractUpToFirstPeriod(licenseText.String()); value != "" {
return headingDone, value
}
return state, ""
case headingDone:
}
return state, ""
}
// extractUpToFirstPeriod returns the license text up to the first period,
// processed through ensureIsSingleLicense, or "" if no period found yet.
func extractUpToFirstPeriod(s string) string {
@@ -2,6 +2,7 @@ package debian
import (
"os"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
@@ -14,13 +15,14 @@ func TestParseLicensesFromCopyright(t *testing.T) {
expected []string
}{
{
fixture: "testdata/copyright/libc6",
// note: there are other licenses in this file that are not matched --we don't do full text license identification yet
expected: []string{"GPL-2", "LGPL-2.1"},
// no Format header; not machine-readable, returns nil
fixture: "testdata/copyright/libc6",
expected: nil,
},
{
// no Format header; not machine-readable, returns nil
fixture: "testdata/copyright/trilicense",
expected: []string{"GPL-2", "LGPL-2.1", "MPL-1.1"},
expected: nil,
},
{
fixture: "testdata/copyright/liblzma5",
@@ -31,21 +33,25 @@ func TestParseLicensesFromCopyright(t *testing.T) {
expected: []string{"GPL-1", "GPL-2", "LGPL-2.1"},
},
{
fixture: "testdata/copyright/python",
// note: this should not capture #, Permission, This, see ... however it's not clear how to fix this (this is probably good enough)
expected: []string{"#", "Apache", "Apache-2", "Apache-2.0", "Expat", "GPL-2", "ISC", "LGPL-2.1+", "PSF-2", "Permission", "Python", "This", "see"},
// no Format header; not machine-readable, returns nil
// previously this captured nonsensical values like "#", "Permission", "This", "see"
fixture: "testdata/copyright/python",
expected: nil,
},
{
// no Format header; not machine-readable, returns nil
fixture: "testdata/copyright/cuda",
expected: []string{"NVIDIA Software License Agreement and CUDA Supplement to Software License Agreement"},
expected: nil,
},
{
// no Format header; not machine-readable, returns nil
fixture: "testdata/copyright/dev-kit",
expected: []string{"LICENSE AGREEMENT FOR NVIDIA SOFTWARE DEVELOPMENT KITS"},
expected: nil,
},
{
// no Format header; not machine-readable, returns nil
fixture: "testdata/copyright/microsoft",
expected: []string{"LICENSE AGREEMENT FOR MICROSOFT PRODUCTS"},
expected: nil,
},
}
@@ -63,3 +69,71 @@ func TestParseLicensesFromCopyright(t *testing.T) {
})
}
}
func TestParseLicensesFromCopyright_FormatHeader(t *testing.T) {
tests := []struct {
name string
content string
machineReadable bool
}{
{
name: "valid http Format header",
content: "Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n",
machineReadable: true,
},
{
name: "valid https Format header",
content: "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n",
machineReadable: true,
},
{
name: "blank lines before Format header",
content: "\n\nFormat: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n",
machineReadable: true,
},
{
name: "no Format header",
content: "This is the Debian prepackaged version of foo.\n",
machineReadable: false,
},
{
name: "Format header is not first non-blank line",
content: "Some-Field: value\nFormat: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\n",
machineReadable: false,
},
{
name: "empty content",
content: "",
machineReadable: false,
},
{
name: "only blank lines",
content: "\n\n\n",
machineReadable: false,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
actual := parseLicensesFromCopyright(strings.NewReader(test.content))
// parseLicensesFromCopyright returns nil for non-machine-readable
// files and a (possibly empty) slice otherwise.
if test.machineReadable {
require.NotNil(t, actual)
} else {
require.Nil(t, actual)
}
})
}
}
func TestParseLicensesFromCopyrightInline(t *testing.T) {
// verify that a file with License: fields but no Format header returns nil
content := `License: GPL-2
License: LGPL-2.1
`
actual := parseLicensesFromCopyright(strings.NewReader(content))
if actual != nil {
t.Errorf("expected nil for non-machine-readable file, got %v", actual)
}
}
+5 -1
View File
@@ -730,7 +730,10 @@ func TestCataloger(t *testing.T) {
if len(p.CPEs) == 0 {
continue
}
assert.Contains(t, p.Name, "Microsoft.NETCore.App")
if runtimeFamilyFromName(p.Name) != netRuntimeFamily {
continue
}
assert.Equal(t, runtimeCPEs(p.Name, p.Version), p.CPEs)
return
}
t.Error("expected at least one runtime package with a CPE")
@@ -1186,6 +1189,7 @@ func TestCataloger(t *testing.T) {
"runtime.linux-x64.Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)", // a compile target reference
},
expectedRels: []string{
"Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] helloworld @ 1.0.0 (/app/helloworld.deps.json)",
"Microsoft.NETCore.DotNetHostPolicy @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json) [dependency-of] Microsoft.NETCore.App @ 2.2.8 (/usr/share/dotnet/shared/Microsoft.NETCore.App/2.2.8/Microsoft.NETCore.App.deps.json)",
"Serilog @ 2.10.0 (/app/helloworld.deps.json) [dependency-of] Serilog.Sinks.Console @ 4.0.1 (/app/helloworld.deps.json)",
"Serilog @ 2.10.0 (/app/helloworld.deps.json) [dependency-of] helloworld @ 1.0.0 (/app/helloworld.deps.json)",
@@ -108,7 +108,7 @@ func (c depsBinaryCataloger) Catalog(_ context.Context, resolver file.Resolver)
Name: "Microsoft.NETCore.App",
Version: version,
Type: pkg.DotnetPkg,
CPEs: runtimeCPEs(version),
CPEs: runtimeCPEs("Microsoft.NETCore.App", version),
Locations: file.NewLocationSet(locs...),
}
pkgs = append(pkgs, rtp)
+63 -39
View File
@@ -22,6 +22,14 @@ var (
versionPunctuationRegex = regexp.MustCompile(`[.,]+`)
)
type runtimeFamily string
const (
unknownRuntimeFamily runtimeFamily = ""
netRuntimeFamily runtimeFamily = "net"
aspNetCoreRuntimeFamily runtimeFamily = "aspnet_core"
)
// newDotnetDepsPackage creates a new Dotnet dependency package from a logicalDepsJSONPackage.
// Note that the new logicalDepsJSONPackage now directly holds library and executable information.
func newDotnetDepsPackage(lp logicalDepsJSONPackage, depsLocation file.Location) *pkg.Package {
@@ -36,7 +44,7 @@ func newDotnetDepsPackage(lp logicalDepsJSONPackage, depsLocation file.Location)
var cpes []cpe.CPE
if isRuntime(name) {
cpes = runtimeCPEs(ver)
cpes = runtimeCPEs(name, ver)
}
p := &pkg.Package{
@@ -56,52 +64,68 @@ func newDotnetDepsPackage(lp logicalDepsJSONPackage, depsLocation file.Location)
}
func isRuntime(name string) bool {
// found in a self-contained net8 app in the deps.json for the application
selfContainedRuntimeDependency := strings.HasPrefix(name, "runtimepack.Microsoft.NETCore.App.Runtime")
// found in net8 apps in the deps.json for the runtime
explicitRuntimeDependency := strings.HasPrefix(name, "Microsoft.NETCore.App.Runtime")
// found in net2 apps in the deps.json for the runtime
producesARuntime := strings.HasPrefix(name, "runtime") && strings.HasSuffix(name, "Microsoft.NETCore.App")
return selfContainedRuntimeDependency || explicitRuntimeDependency || producesARuntime
return runtimeFamilyFromName(name) != unknownRuntimeFamily
}
func runtimeCPEs(ver string) []cpe.CPE {
// .NET Core Versions
// 2016: .NET Core 1.0, cpe:2.3:a:microsoft:dotnet_core:1.0:*:*:*:*:*:*:*
// 2016: .NET Core 1.1, cpe:2.3:a:microsoft:dotnet_core:1.1:*:*:*:*:*:*:*
// 2017: .NET Core 2.0, cpe:2.3:a:microsoft:dotnet_core:2.0:*:*:*:*:*:*:*
// 2018: .NET Core 2.1, cpe:2.3:a:microsoft:dotnet_core:2.1:*:*:*:*:*:*:*
// 2018: .NET Core 2.2, cpe:2.3:a:microsoft:dotnet_core:2.2:*:*:*:*:*:*:*
// 2019: .NET Core 3.0, cpe:2.3:a:microsoft:dotnet_core:3.0:*:*:*:*:*:*:*
// 2019: .NET Core 3.1, cpe:2.3:a:microsoft:dotnet_core:3.1:*:*:*:*:*:*:*
func runtimeFamilyFromName(name string) runtimeFamily {
normalizedName := strings.ToLower(name)
// Unified .NET Versions
// 2020: .NET 5.0, cpe:2.3:a:microsoft:dotnet:5.0:*:*:*:*:*:*:*
// 2021: .NET 6.0, cpe:2.3:a:microsoft:dotnet:6.0:*:*:*:*:*:*:*
// 2022: .NET 7.0, cpe:2.3:a:microsoft:dotnet:7.0:*:*:*:*:*:*:*
// 2023: .NET 8.0, cpe:2.3:a:microsoft:dotnet:8.0:*:*:*:*:*:*:*
// 2024: .NET 9.0, cpe:2.3:a:microsoft:dotnet:9.0:*:*:*:*:*:*:*
// 2025 ...?
// found in self-contained or framework-dependent apps in deps.json entries
if strings.HasPrefix(normalizedName, "runtimepack.microsoft.aspnetcore.app.runtime") ||
strings.HasPrefix(normalizedName, "microsoft.aspnetcore.app.runtime") ||
normalizedName == "microsoft.aspnetcore.app" ||
(strings.HasPrefix(normalizedName, "runtime") && strings.HasSuffix(normalizedName, "microsoft.aspnetcore.app")) {
return aspNetCoreRuntimeFamily
}
fields := strings.Split(ver, ".")
majorVersion, err := strconv.Atoi(fields[0])
if err != nil {
log.WithFields("error", err).Tracef("failed to parse .NET major version from %q", ver)
// found in self-contained, framework-dependent, and synthesized runtime packages
if strings.HasPrefix(normalizedName, "runtimepack.microsoft.netcore.app.runtime") ||
strings.HasPrefix(normalizedName, "microsoft.netcore.app.runtime") ||
normalizedName == "microsoft.netcore.app" ||
(strings.HasPrefix(normalizedName, "runtime") && strings.HasSuffix(normalizedName, "microsoft.netcore.app")) {
return netRuntimeFamily
}
return unknownRuntimeFamily
}
func runtimeCPEs(name, ver string) []cpe.CPE {
family := runtimeFamilyFromName(name)
if family == unknownRuntimeFamily {
return nil
}
var minorVersion int
if len(fields) > 1 {
minorVersion, err = strconv.Atoi(fields[1])
if err != nil {
log.WithFields("error", err).Tracef("failed to parse .NET minor version from %q", ver)
return nil
}
fields := strings.Split(ver, ".")
if len(fields) == 0 {
return nil
}
productName := "dotnet"
if majorVersion < 5 {
productName = "dotnet_core"
normalizedVersionFields := make([]string, 0, len(fields))
majorVersion, err := strconv.Atoi(fields[0])
if err != nil {
log.WithFields("error", err).Tracef("failed to parse .NET runtime major version from %q", ver)
return nil
}
normalizedVersionFields = append(normalizedVersionFields, strconv.Itoa(majorVersion))
for _, field := range fields[1:] {
value, err := strconv.Atoi(field)
if err != nil {
log.WithFields("error", err).Tracef("failed to parse .NET runtime version component %q from %q", field, ver)
return nil
}
normalizedVersionFields = append(normalizedVersionFields, strconv.Itoa(value))
}
if len(normalizedVersionFields) == 1 {
normalizedVersionFields = append(normalizedVersionFields, "0")
}
productName := ".net"
if family == aspNetCoreRuntimeFamily {
productName = "asp.net_core"
} else if majorVersion < 5 {
productName = ".net_core"
}
return []cpe.CPE{
@@ -110,7 +134,7 @@ func runtimeCPEs(ver string) []cpe.CPE {
Part: "a",
Vendor: "microsoft",
Product: productName,
Version: fmt.Sprintf("%d.%d", majorVersion, minorVersion),
Version: strings.Join(normalizedVersionFields, "."),
},
// we didn't find this in the underlying material, but this is the convention in NVD and we are certain this is a runtime package
Source: cpe.DeclaredSource,
+133 -54
View File
@@ -46,6 +46,56 @@ func Test_getDepsJSONFilePrefix(t *testing.T) {
}
}
func Test_newDotnetDepsPackage_AssignsRuntimeCPEs(t *testing.T) {
tests := []struct {
name string
nameVersion string
expected []cpe.CPE
}{
{
name: ".NET runtime package",
nameVersion: "runtimepack.Microsoft.NETCore.App.Runtime.win-x64/10.0.4",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: ".net",
Version: "10.0.4",
},
Source: cpe.DeclaredSource,
},
},
},
{
name: "ASP.NET Core runtime package",
nameVersion: "runtimepack.Microsoft.AspNetCore.App.Runtime.win-x64/10.0.4",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "asp.net_core",
Version: "10.0.4",
},
Source: cpe.DeclaredSource,
},
},
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
actual := newDotnetDepsPackage(
logicalDepsJSONPackage{NameVersion: tc.nameVersion},
file.NewLocation("/app/test.deps.json"),
)
assert.Equal(t, tc.expected, actual.CPEs)
})
}
}
func Test_NewDotnetBinaryPackage(t *testing.T) {
tests := []struct {
name string
@@ -400,19 +450,21 @@ func Test_spaceNormalize(t *testing.T) {
func TestRuntimeCPEs(t *testing.T) {
tests := []struct {
name string
version string
expected []cpe.CPE
name string
packageName string
version string
expected []cpe.CPE
}{
{
name: ".NET Core 1.0",
version: "1.0",
name: ".NET Core 1.0",
packageName: "Microsoft.NETCore.App",
version: "1.0",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet_core",
Product: ".net_core",
Version: "1.0",
},
Source: cpe.DeclaredSource,
@@ -420,14 +472,15 @@ func TestRuntimeCPEs(t *testing.T) {
},
},
{
name: ".NET Core 2.1",
version: "2.1",
name: ".NET Core 2.1",
packageName: "Microsoft.NETCore.App",
version: "2.1",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet_core",
Product: ".net_core",
Version: "2.1",
},
Source: cpe.DeclaredSource,
@@ -435,14 +488,15 @@ func TestRuntimeCPEs(t *testing.T) {
},
},
{
name: ".NET Core 3.1",
version: "3.1",
name: ".NET Core 3.1",
packageName: "Microsoft.NETCore.App",
version: "3.1",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet_core",
Product: ".net_core",
Version: "3.1",
},
Source: cpe.DeclaredSource,
@@ -450,14 +504,15 @@ func TestRuntimeCPEs(t *testing.T) {
},
},
{
name: ".NET Core 4.9 (hypothetical)",
version: "4.9",
name: ".NET Core 4.9 (hypothetical)",
packageName: "Microsoft.NETCore.App",
version: "4.9",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet_core",
Product: ".net_core",
Version: "4.9",
},
Source: cpe.DeclaredSource,
@@ -465,14 +520,15 @@ func TestRuntimeCPEs(t *testing.T) {
},
},
{
name: ".NET 5.0",
version: "5.0",
name: ".NET 5.0",
packageName: "Microsoft.NETCore.App",
version: "5.0",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet",
Product: ".net",
Version: "5.0",
},
Source: cpe.DeclaredSource,
@@ -480,14 +536,15 @@ func TestRuntimeCPEs(t *testing.T) {
},
},
{
name: ".NET 6.0",
version: "6.0",
name: ".NET 6.0",
packageName: "Microsoft.NETCore.App",
version: "6.0",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet",
Product: ".net",
Version: "6.0",
},
Source: cpe.DeclaredSource,
@@ -495,14 +552,15 @@ func TestRuntimeCPEs(t *testing.T) {
},
},
{
name: ".NET 8.0",
version: "8.0",
name: ".NET 8.0",
packageName: "Microsoft.NETCore.App",
version: "8.0",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet",
Product: ".net",
Version: "8.0",
},
Source: cpe.DeclaredSource,
@@ -510,14 +568,15 @@ func TestRuntimeCPEs(t *testing.T) {
},
},
{
name: ".NET 10.0 (future version)",
version: "10.0",
name: ".NET 10.0 (future version)",
packageName: "Microsoft.NETCore.App",
version: "10.0",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet",
Product: ".net",
Version: "10.0",
},
Source: cpe.DeclaredSource,
@@ -525,14 +584,47 @@ func TestRuntimeCPEs(t *testing.T) {
},
},
{
name: "Patch version should not be included",
version: "6.0.21",
name: "Patch version should be preserved",
packageName: "Microsoft.NETCore.App",
version: "6.0.21",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet",
Product: ".net",
Version: "6.0.21",
},
Source: cpe.DeclaredSource,
},
},
},
{
name: "ASP.NET Core runtime",
packageName: "runtimepack.Microsoft.AspNetCore.App.Runtime.win-x64",
version: "9.0.10",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "asp.net_core",
Version: "9.0.10",
},
Source: cpe.DeclaredSource,
},
},
},
{
name: "Assumed minor version",
packageName: "Microsoft.NETCore.App",
version: "6",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: ".net",
Version: "6.0",
},
Source: cpe.DeclaredSource,
@@ -540,39 +632,26 @@ func TestRuntimeCPEs(t *testing.T) {
},
},
{
name: "Assumed minor version",
version: "6",
expected: []cpe.CPE{
{
Attributes: cpe.Attributes{
Part: "a",
Vendor: "microsoft",
Product: "dotnet",
Version: "6.0",
},
Source: cpe.DeclaredSource,
},
},
name: "Invalid version format",
packageName: "Microsoft.NETCore.App",
version: "invalid",
expected: nil,
},
{
name: "Invalid version format",
version: "invalid",
expected: nil,
},
{
name: "Empty version",
version: "",
expected: nil,
name: "Empty version",
packageName: "Microsoft.NETCore.App",
version: "",
expected: nil,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := runtimeCPEs(tc.version)
result := runtimeCPEs(tc.packageName, tc.version)
if !reflect.DeepEqual(result, tc.expected) {
t.Errorf("runtimeCPEs(%q) = %+v; want %+v",
tc.version, result, tc.expected)
t.Errorf("runtimeCPEs(%q, %q) = %+v; want %+v",
tc.packageName, tc.version, result, tc.expected)
}
})
}
@@ -120,6 +120,20 @@ func MatchAll(matchers ...EvidenceMatcher) EvidenceMatcher {
}
}
// MatchNone succeeds only if the matcher returns no results.
func MatchNone(matcher EvidenceMatcher) EvidenceMatcher {
return func(classifier Classifier, context MatcherContext) ([]pkg.Package, error) {
match, err := matcher(classifier, context)
if err != nil {
return nil, err
}
if match != nil {
return nil, nil
}
return []pkg.Package{}, nil
}
}
type ContextualEvidenceMatchers struct {
CatalogerName string
}
@@ -243,3 +243,40 @@ func Test_SupportingEvidenceMatcher(t *testing.T) {
})
}
}
func TestMatchNone(t *testing.T) {
matchingMatcher := MatchPath("**")
notMatchingMatcher := MatchPath("will-not-match")
tests := []struct {
name string
matcher EvidenceMatcher
expected bool // true if MatchNone should succeed (inner failed)
}{
{
name: "inner matches, MatchNone fails",
matcher: MatchNone(matchingMatcher),
expected: false,
},
{
name: "inner fails, MatchNone succeeds",
matcher: MatchNone(notMatchingMatcher),
expected: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
pkgs, err := tt.matcher(Classifier{}, MatcherContext{
Location: file.NewLocation("some/path"),
})
require.NoError(t, err)
if tt.expected {
assert.NotNil(t, pkgs)
assert.Empty(t, pkgs)
} else {
assert.Nil(t, pkgs)
}
})
}
}
@@ -76,6 +76,9 @@
"github.com/go-git/go-git": [
"cpe:2.3:a:go-git_project:go-git:*:*:*:*:*:go:*:*"
],
"github.com/go-jose/go-jose/v4#pkg-constants": [
"cpe:2.3:a:go-jose_project:go-jose:*:*:*:*:*:*:*:*"
],
"github.com/go-resty/resty/v2": [
"cpe:2.3:a:resty_project:resty:*:*:*:*:*:go:*:*"
],
@@ -151,6 +154,9 @@
"github.com/rezmoss/axios4go": [
"cpe:2.3:a:rezmoss:axios4go:*:*:*:*:*:go:*:*"
],
"github.com/russellhaering/goxmldsig": [
"cpe:2.3:a:goxmldsig_project:goxmldsig:*:*:*:*:*:*:*:*"
],
"github.com/sap/cloud-security-client-go": [
"cpe:2.3:a:sap:cloud-security-client-go:*:*:*:*:*:go:*:*"
],
@@ -160,6 +166,9 @@
"github.com/sigstore/gitsign": [
"cpe:2.3:a:sigstore:gitsign:*:*:*:*:*:go:*:*"
],
"github.com/sipeed/picoclaw": [
"cpe:2.3:a:sipeed:picoclaw:*:*:*:*:*:go:*:*"
],
"github.com/sirupsen/logrus": [
"cpe:2.3:a:turbopuffer:logrus:*:*:*:*:*:go:*:*"
],
@@ -169,12 +178,27 @@
"github.com/stacklok/minder": [
"cpe:2.3:a:lfprojects:minder:*:*:*:*:*:go:*:*"
],
"github.com/tomwright/dasel": [
"cpe:2.3:a:tomwright:dasel:*:*:*:*:*:go:*:*"
],
"github.com/tomwright/dasel/v2": [
"cpe:2.3:a:tomwright:dasel:*:*:*:*:*:go:*:*"
],
"github.com/tomwright/dasel/v3": [
"cpe:2.3:a:tomwright:dasel:*:*:*:*:*:go:*:*"
],
"github.com/valyala/fasthttp": [
"cpe:2.3:a:fasthttp_project:fasthttp:*:*:*:*:*:*:*:*"
],
"github.com/whilp/git-urls": [
"cpe:2.3:a:git-urls_project:git-urls:*:*:*:*:*:go:*:*"
],
"github.com/yuin/goldmark": [
"cpe:2.3:a:yuin:goldmark:*:*:*:*:*:go:*:*"
],
"go.opentelemetry.io/otel": [
"cpe:2.3:a:opentelemetry:opentelemetry:*:*:*:*:*:go:*:*"
],
"golang.org/x/crypto": [
"cpe:2.3:a:go:ssh:*:*:*:*:*:go:*:*"
],
@@ -2222,6 +2246,9 @@
"626": [
"cpe:2.3:a:626_project:626:*:*:*:*:*:node.js:*:*"
],
"@aborruso/ckan-mcp-server": [
"cpe:2.3:a:ondata:ckan_mcp_server:*:*:*:*:*:node.js:*:*"
],
"@actions/artifact": [
"cpe:2.3:a:github:actions\\/artifact:*:*:*:*:*:node.js:*:*"
],
@@ -2248,6 +2275,9 @@
"@anthropic-ai/claude-code": [
"cpe:2.3:a:anthropic:claude_code:*:*:*:*:*:node.js:*:*"
],
"@anthropic-ai/sdk": [
"cpe:2.3:a:anthropic:claude_sdk_for_typescript:*:*:*:*:*:*:*:*"
],
"@apollo/gateway": [
"cpe:2.3:a:apollographql:apollo_gateway:*:*:*:*:*:node.js:*:*"
],
@@ -2257,6 +2287,9 @@
"@apollosproject/data-connector-rock": [
"cpe:2.3:a:apollosapp:data-connector-rock:*:*:*:*:*:node.js:*:*"
],
"@appium/support": [
"cpe:2.3:a:appium:appium\\/support:*:*:*:*:*:node.js:*:*"
],
"@astrojs/cloudflare": [
"cpe:2.3:a:astro:\\@astrojs\\/cloudflare:*:*:*:*:*:node.js:*:*"
],
@@ -2284,15 +2317,27 @@
"@backstage/backend-common": [
"cpe:2.3:a:linuxfoundation:backstage_backend-common:*:*:*:*:*:node.js:*:*"
],
"@backstage/integration": [
"cpe:2.3:a:linuxfoundation:backstage\\/integration:*:*:*:*:*:node.js:*:*"
],
"@backstage/plugin-auth-backend": [
"cpe:2.3:a:linuxfoundation:auth_backend:*:*:*:*:*:node.js:*:*"
],
"@backstage/plugin-scaffolder-backend": [
"cpe:2.3:a:linuxfoundation:backstage\\/plugin-scaffolder-backend:*:*:*:*:*:*:*:*"
],
"@backstage/plugin-techdocs": [
"cpe:2.3:a:linuxfoundation:\\@backstage\\/plugin-techdocs:*:*:*:*:*:node.js:*:*"
],
"@backstage/plugin-techdocs-node": [
"cpe:2.3:a:linuxfoundation:backstage_plugin-techdocs-node:*:*:*:*:*:*:*:*"
],
"@backstage/techdocs-common": [
"cpe:2.3:a:linuxfoundation:\\@backstage\\/techdocs-common:*:*:*:*:*:node.js:*:*"
],
"@better-auth/oauth-provider": [
"cpe:2.3:a:better-auth:better-auth\\/oauth-provider:*:*:*:*:*:node.js:*:*"
],
"@braintree/sanitize-url": [
"cpe:2.3:a:paypal:braintree\\/sanitize-url:*:*:*:*:*:node.js:*:*"
],
@@ -2332,6 +2377,9 @@
"@coding-solo/godot-mcp": [
"cpe:2.3:a:coding-solo:godot_mcp:*:*:*:*:*:*:*:*"
],
"@contentauth/c2pa-web": [
"cpe:2.3:a:adobe:c2pa-web:*:*:*:*:*:node.js:*:*"
],
"@cookiex/deep": [
"cpe:2.3:a:cookiex-deep_project:cookiex-deep:*:*:*:*:*:node.js:*:*"
],
@@ -2350,6 +2398,9 @@
"@evershop/evershop": [
"cpe:2.3:a:evershop:evershop:*:*:*:*:*:node.js:*:*"
],
"@fastify/middie": [
"cpe:2.3:a:fastify:fastify\\/middie:*:*:*:*:*:node.js:*:*"
],
"@fastly/js-compute": [
"cpe:2.3:a:fastly:js-compute:*:*:*:*:*:node.js:*:*"
],
@@ -2365,6 +2416,12 @@
"@flourish/sdk": [
"cpe:2.3:a:canva:flourish_sdk:*:*:*:*:*:node.js:*:*"
],
"@frontmcp/adapters": [
"cpe:2.3:a:agentfront:\\@frontmcp\\/adapters:*:*:*:*:*:node.js:*:*"
],
"@frontmcp/sdk": [
"cpe:2.3:a:agentfront:\\@frontmcp\\/sdk:*:*:*:*:*:node.js:*:*"
],
"@github/paste-markdown": [
"cpe:2.3:a:paste-markdown_project:paste-markdown:*:*:*:*:*:node.js:*:*"
],
@@ -2389,6 +2446,9 @@
"@joeattardi/emoji-button": [
"cpe:2.3:a:emoji_button_project:emoji_button:*:*:*:*:*:node.js:*:*"
],
"@keystone-6/core": [
"cpe:2.3:a:keystonejs:keystone:*:*:*:*:*:node.js:*:*"
],
"@keystonejs/keystone": [
"cpe:2.3:a:keystonejs:keystone:*:*:*:*:*:node.js:*:*"
],
@@ -2407,6 +2467,9 @@
"@misskey-dev/summaly": [
"cpe:2.3:a:misskey:summaly:*:*:*:*:*:node.js:*:*"
],
"@mobilenext/mobile-mcp": [
"cpe:2.3:a:mobilenexthq:mobile_mcp:*:*:*:*:*:node.js:*:*"
],
"@nestjs/core": [
"cpe:2.3:a:nestjs:nest:*:*:*:*:*:node.js:*:*"
],
@@ -2431,6 +2494,12 @@
"@octokit/webhooks": [
"cpe:2.3:a:octokit:webhooks:*:*:*:*:*:node.js:*:*"
],
"@openclaw/voice-call": [
"cpe:2.3:a:openclaw:openclaw\\/voice-call:*:*:*:*:*:node.js:*:*"
],
"@opennextjs/cloudflare": [
"cpe:2.3:a:opennextjs:opennext_for_cloudflare:*:*:*:*:*:node.js:*:*"
],
"@openzeppelin/contracts": [
"cpe:2.3:a:openzeppelin:contracts:*:*:*:*:*:node.js:*:*"
],
@@ -2456,6 +2525,9 @@
"@rkesters/gnuplot": [
"cpe:2.3:a:gnuplot_project:gnuplot:*:*:*:*:*:node.js:*:*"
],
"@saltcorn/server": [
"cpe:2.3:a:saltcorn:saltcorn:*:*:*:*:*:*:*:*"
],
"@sap/xssec": [
"cpe:2.3:a:sap:\\@sap\\/xssec:*:*:*:*:*:node.js:*:*"
],
@@ -2749,6 +2821,9 @@
"box2d-native": [
"cpe:2.3:a:box2d-native_project:box2d-native:*:*:*:*:*:node.js:*:*"
],
"brace-expansion": [
"cpe:2.3:a:juliangruber:brace-expansion:*:*:*:*:*:node.js:*:*"
],
"braces": [
"cpe:2.3:a:braces_project:braces:*:*:*:*:*:node.js:*:*",
"cpe:2.3:a:jonschlinkert:braces:*:*:*:*:*:node.js:*:*"
@@ -2859,6 +2934,9 @@
"class-transformer": [
"cpe:2.3:a:class-transformer_project:class-transformer:*:*:*:*:*:node.js:*:*"
],
"clawdbot": [
"cpe:2.3:a:openclaw:openclaw:*:*:*:*:*:node.js:*:*"
],
"cli": [
"cpe:2.3:a:cli_project:cli:*:*:*:*:*:node.js:*:*"
],
@@ -3100,6 +3178,9 @@
"defaults-deep": [
"cpe:2.3:a:defaults-deep_project:defaults-deep:*:*:*:*:*:node.js:*:*"
],
"defu": [
"cpe:2.3:a:unjs:defu:*:*:*:*:*:node.js:*:*"
],
"defuddle": [
"cpe:2.3:a:kepano:defuddle:*:*:*:*:*:node.js:*:*"
],
@@ -3182,6 +3263,9 @@
"dotty": [
"cpe:2.3:a:dotty_project:dotty:*:*:*:*:*:*:*:*"
],
"drizzle-orm": [
"cpe:2.3:a:drizzle:drizzle:*:*:*:*:*:node.js:*:*"
],
"droppy": [
"cpe:2.3:a:droppy_project:droppy:*:*:*:*:*:node.js:*:*"
],
@@ -3216,7 +3300,8 @@
"cpe:2.3:a:adonisjs:edge:*:*:*:*:*:node.js:*:*"
],
"effect": [
"cpe:2.3:a:effect_project:effect:*:*:*:*:*:node.js:*:*"
"cpe:2.3:a:effect_project:effect:*:*:*:*:*:node.js:*:*",
"cpe:2.3:a:effectful:effect:*:*:*:*:*:node.js:*:*"
],
"egg-scripts": [
"cpe:2.3:a:eggjs:egg-scripts:*:*:*:*:*:node.js:*:*"
@@ -3354,6 +3439,9 @@
"fast-string-search": [
"cpe:2.3:a:fast_string_search_project:fast_string_search:*:*:*:*:*:node.js:*:*"
],
"fast-uri": [
"cpe:2.3:a:openjsf:fast-uri:*:*:*:*:*:node.js:*:*"
],
"fast-xml-parser": [
"cpe:2.3:a:naturalintelligence:fast-xml-parser:*:*:*:*:*:*:*:*"
],
@@ -3818,6 +3906,9 @@
"iniparserjs": [
"cpe:2.3:a:iniparserjs_project:iniparserjs:*:*:*:*:*:node.js:*:*"
],
"inngest": [
"cpe:2.3:a:inngest:inngest:*:*:*:*:*:node.js:*:*"
],
"install-g-test": [
"cpe:2.3:a:install-g-test_project:install-g-test:*:*:*:*:*:node.js:*:*"
],
@@ -4069,6 +4160,9 @@
"launchpad": [
"cpe:2.3:a:bitovi:launchpad:*:*:*:*:*:node.js:*:*"
],
"leaflet": [
"cpe:2.3:a:leafletjs:leaflet:*:*:*:*:*:node.js:*:*"
],
"less-openui5": [
"cpe:2.3:a:less-openui5_project:less-openui5:*:*:*:*:*:node.js:*:*"
],
@@ -4147,6 +4241,18 @@
"lodash": [
"cpe:2.3:a:lodash:lodash:*:*:*:*:*:node.js:*:*"
],
"lodash-amd": [
"cpe:2.3:a:lodash:lodash-amd:*:*:*:*:*:node.js:*:*"
],
"lodash-es": [
"cpe:2.3:a:lodash:lodash-es:*:*:*:*:*:node.js:*:*"
],
"lodash.template": [
"cpe:2.3:a:lodash:lodash.template:*:*:*:*:*:node.js:*:*"
],
"lodash.unset": [
"cpe:2.3:a:lodash:lodash.unset:*:*:*:*:*:node.js:*:*"
],
"log4js": [
"cpe:2.3:a:log4js_project:log4js:*:*:*:*:*:node.js:*:*"
],
@@ -4235,6 +4341,9 @@
"materialize-css": [
"cpe:2.3:a:materializecss:materialize:*:*:*:*:*:*:node.js:*"
],
"mathjs": [
"cpe:2.3:a:mathjs:mathjs:*:*:*:*:*:node.js:*:*"
],
"matrix-appservice-bridge": [
"cpe:2.3:a:matrix:matrix-appservice-bridge:*:*:*:*:*:node.js:*:*"
],
@@ -4377,6 +4486,9 @@
"mpath": [
"cpe:2.3:a:mpath_project:mpath:*:*:*:*:*:node.js:*:*"
],
"mppx": [
"cpe:2.3:a:wevm:mppx:*:*:*:*:*:node.js:*:*"
],
"mqtt-packet": [
"cpe:2.3:a:mqtt-packet_project:mqtt-packet:*:*:*:*:*:node.js:*:*"
],
@@ -4443,6 +4555,9 @@
"nanoid": [
"cpe:2.3:a:nanoid_project:nanoid:*:*:*:*:*:node.js:*:*"
],
"nanotar": [
"cpe:2.3:a:unjs:nanotar:*:*:*:*:*:node.js:*:*"
],
"napi-postinstall": [
"cpe:2.3:a:un-ts:napi-postinstall:*:*:*:*:*:node.js:*:*"
],
@@ -4739,6 +4854,9 @@
"open-graph": [
"cpe:2.3:a:open-graph_project:open-graph:*:*:*:*:*:node.js:*:*"
],
"openclaw": [
"cpe:2.3:a:openclaw:openclaw:*:*:*:*:*:node.js:*:*"
],
"opencv": [
"cpe:2.3:a:node-opencv_project:node-opencv:*:*:*:*:*:node.js:*:*"
],
@@ -4802,6 +4920,9 @@
"parsejson": [
"cpe:2.3:a:parsejson_project:parsejson:*:*:*:*:*:node.js:*:*"
],
"parsoid": [
"cpe:2.3:a:wikimedia:parsoid:*:*:*:*:*:node.js:*:*"
],
"passport": [
"cpe:2.3:a:passport_project:passport:*:*:*:*:*:node.js:*:*"
],
@@ -4817,6 +4938,9 @@
"path-parse": [
"cpe:2.3:a:path-parse_project:path-parse:*:*:*:*:*:node.js:*:*"
],
"path-to-regexp": [
"cpe:2.3:a:pillarjs:path-to-regexp:*:*:*:*:*:node.js:*:*"
],
"pathval": [
"cpe:2.3:a:chaijis:pathval:*:*:*:*:*:node.js:*:*"
],
@@ -5109,6 +5233,9 @@
"robot-js": [
"cpe:2.3:a:getrobot:robot-js:*:*:*:*:*:node.js:*:*"
],
"rollup": [
"cpe:2.3:a:rollupjs:rollup:*:*:*:*:*:node.js:*:*"
],
"rollup-plugin-dev-server": [
"cpe:2.3:a:rollup-plugin-dev-server_project:rollup-plugin-dev-server:*:*:*:*:*:node.js:*:*"
],
@@ -5130,6 +5257,9 @@
"rtcmulticonnection-client": [
"cpe:2.3:a:rtcmulticonnection-client_project:rtcmulticonnection-client:*:*:*:*:*:node.js:*:*"
],
"rwsdk": [
"cpe:2.3:a:redwoodjs:redwoodsdk:*:*:*:*:*:*:*:*"
],
"s3-uploader": [
"cpe:2.3:a:s3-uploader_project:s3-uploader:*:*:*:*:*:node.js:*:*"
],
@@ -5228,7 +5358,8 @@
"cpe:2.3:a:serc.js_project:serc.js:*:*:*:*:*:node.js:*:*"
],
"serialize-javascript": [
"cpe:2.3:a:verizon:serialize-javascript:*:*:*:*:*:node.js:*:*"
"cpe:2.3:a:verizon:serialize-javascript:*:*:*:*:*:node.js:*:*",
"cpe:2.3:a:yahoo:serialize_javascript:*:*:*:*:*:node.js:*:*"
],
"serialize-to-js": [
"cpe:2.3:a:serialize-to-js_project:serialize-to-js:*:*:*:*:*:node.js:*:*"
@@ -5381,6 +5512,9 @@
"sockjs": [
"cpe:2.3:a:sockjs_project:sockjs:*:*:*:*:*:node.js:*:*"
],
"spin.js": [
"cpe:2.3:a:spin.js:spin.js:*:*:*:*:*:node.js:*:*"
],
"split-html-to-chars": [
"cpe:2.3:a:split-html-to-chars_project:split-html-to-chars:*:*:*:*:*:node.js:*:*"
],
@@ -5493,6 +5627,9 @@
"svelte": [
"cpe:2.3:a:svelte:svelte:*:*:*:*:*:node.js:*:*"
],
"svgo": [
"cpe:2.3:a:svgo:svgo:*:*:*:*:*:node.js:*:*"
],
"swagger-ui-dist": [
"cpe:2.3:a:smartbear:swagger-ui-dist:*:*:*:*:*:node.js:*:*"
],
@@ -5702,6 +5839,9 @@
"utils-extend": [
"cpe:2.3:a:utils-extend_project:utils-extend:*:*:*:*:*:node.js:*:*"
],
"uuid": [
"cpe:2.3:a:uuidjs:uuid:*:*:*:*:*:node.js:*:*"
],
"uv-tj-demo": [
"cpe:2.3:a:uv-tj-demo_project:uv-tj-demo:*:*:*:*:*:node.js:*:*"
],
@@ -5981,6 +6121,9 @@
"Flask-Caching": [
"cpe:2.3:a:flask-caching_project:flask-caching:*:*:*:*:*:flask:*:*"
],
"Flask-HTTPAuth": [
"cpe:2.3:a:miguelgrinberg:flask-httpauth:*:*:*:*:*:python:*:*"
],
"Flask-Security-Too": [
"cpe:2.3:a:flask-security-too_project:flask-security-too:*:*:*:*:*:*:*:*"
],
@@ -6017,9 +6160,15 @@
"XML2Dict": [
"cpe:2.3:a:xml2dict_project:xml2dict:*:*:*:*:*:python:*:*"
],
"agixt": [
"cpe:2.3:a:agixt:agixt:*:*:*:*:*:*:*:*"
],
"aiohttp-session": [
"cpe:2.3:a:aio-libs:aiohttp_session:*:*:*:*:*:*:*:*"
],
"ajenti.plugin.core": [
"cpe:2.3:a:ajenti:ajenti_plugin_core:*:*:*:*:*:*:*:*"
],
"aniso8601": [
"cpe:2.3:a:aniso8601_project:aniso8601:*:*:*:*:*:*:*:*"
],
@@ -6041,6 +6190,9 @@
"b2sdk": [
"cpe:2.3:a:backblaze:b2-sdk-python:*:*:*:*:*:*:*:*"
],
"bedrock-agentcore-starter-toolkit": [
"cpe:2.3:a:amazon:bedrock_agentcore_starter_toolkit:*:*:*:*:*:python:*:*"
],
"black": [
"cpe:2.3:a:python:black:*:*:*:*:*:python:*:*"
],
@@ -6068,6 +6220,9 @@
"certifi": [
"cpe:2.3:a:certifi:certifi:*:*:*:*:*:python:*:*"
],
"claude-agent-sdk": [
"cpe:2.3:a:anthropic:claude_agent_sdk:*:*:*:*:*:python:*:*"
],
"cleo": [
"cpe:2.3:a:python-poetry:cleo:*:*:*:*:*:*:*:*"
],
@@ -6093,6 +6248,9 @@
"cpe:2.3:a:cryptography.io:cryptography:*:*:*:*:*:*:*:*",
"cpe:2.3:a:cryptography.io:cryptography:*:*:*:*:*:python:*:*"
],
"curl-cffi": [
"cpe:2.3:a:lexiforest:curl_cffi:*:*:*:*:*:python:*:*"
],
"cvxopt": [
"cpe:2.3:a:cvxopt_project:cvxopt:*:*:*:*:*:python:*:*"
],
@@ -6126,6 +6284,9 @@
"django-user-sessions": [
"cpe:2.3:a:django-user-sessions_project:django-user-sessions:*:*:*:*:*:*:*:*"
],
"docling-core": [
"cpe:2.3:a:docling:docling-core:*:*:*:*:*:python:*:*"
],
"drf-jwt": [
"cpe:2.3:a:styria:django-rest-framework-json_web_tokens:*:*:*:*:*:*:*:*"
],
@@ -6144,6 +6305,9 @@
"ecdsa": [
"cpe:2.3:a:tlsfuzzer:ecdsa:*:*:*:*:*:python:*:*"
],
"emmett": [
"cpe:2.3:a:emmett:emmett:*:*:*:*:*:*:*:*"
],
"enum34": [
"cpe:2.3:a:python:enum34:*:*:*:*:*:*:*:*"
],
@@ -6156,6 +6320,9 @@
"fastapi-api-key": [
"cpe:2.3:a:athroniaeth:fastapi_api_key:*:*:*:*:*:python:*:*"
],
"fastfeedparser": [
"cpe:2.3:a:kagi:fastfeedparser:*:*:*:*:*:python:*:*"
],
"feedgen": [
"cpe:2.3:a:feedgen_project:feedgen:*:*:*:*:*:python:*:*"
],
@@ -6217,6 +6384,9 @@
"jwcrypto": [
"cpe:2.3:a:latchset:jwcrypto:*:*:*:*:*:*:*:*"
],
"kedro": [
"cpe:2.3:a:linuxfoundation:kedro:*:*:*:*:*:python:*:*"
],
"keep": [
"cpe:2.3:a:keep_project:keep:*:*:*:*:*:*:*:*"
],
@@ -6232,9 +6402,18 @@
"langchain-experimental": [
"cpe:2.3:a:langchain:langchain_experimental:*:*:*:*:*:python:*:*"
],
"langflow-base": [
"cpe:2.3:a:langflow:langflow-base:*:*:*:*:*:python:*:*"
],
"lerobot": [
"cpe:2.3:a:huggingface:lerobot:*:*:*:*:*:python:*:*"
],
"loguru": [
"cpe:2.3:a:loguru_project:loguru:*:*:*:*:*:python:*:*"
],
"lupa": [
"cpe:2.3:a:scoder:lupa:*:*:*:*:*:python:*:*"
],
"lxml": [
"cpe:2.3:a:lxml:lxml:*:*:*:*:*:*:*:*"
],
@@ -6247,6 +6426,12 @@
"mage-ai": [
"cpe:2.3:a:mage:mage-ai:*:*:*:*:*:python:*:*"
],
"magic-wormhole": [
"cpe:2.3:a:magic_wormhole_project:magic_wormhole:*:*:*:*:*:python:*:*"
],
"marimo": [
"cpe:2.3:a:coreweave:marimo:*:*:*:*:*:python:*:*"
],
"marshmallow": [
"cpe:2.3:a:marshmallow_project:marshmallow:*:*:*:*:*:python:*:*"
],
@@ -6274,6 +6459,9 @@
"oncall": [
"cpe:2.3:a:linkedin:oncall:*:*:*:*:*:*:*:*"
],
"onnx": [
"cpe:2.3:a:linuxfoundation:onnx:*:*:*:*:*:*:*:*"
],
"openapi-python-client": [
"cpe:2.3:a:openapi-python-client_project:openapi-python-client:*:*:*:*:*:*:*:*"
],
@@ -6307,9 +6495,15 @@
"pdm": [
"cpe:2.3:a:frostming:pdm:*:*:*:*:*:python:*:*"
],
"picklescan": [
"cpe:2.3:a:mmaitre314:picklescan:*:*:*:*:*:*:*:*"
],
"pillow-heif": [
"cpe:2.3:a:bigcat88:pillow-heif:*:*:*:*:*:python:*:*"
],
"pipecat-ai": [
"cpe:2.3:a:pipecat:pipecat:*:*:*:*:*:python:*:*"
],
"pipreqs": [
"cpe:2.3:a:pipreqs_project:pipreqs:*:*:*:*:*:python:*:*"
],
@@ -6319,6 +6513,9 @@
"poetry": [
"cpe:2.3:a:python-poetry:poetry:*:*:*:*:*:python:*:*"
],
"praisonaiagents": [
"cpe:2.3:a:praison:praisonaiagents:*:*:*:*:*:python:*:*"
],
"pretix": [
"cpe:2.3:a:rami:pretix:*:*:*:*:*:*:*:*"
],
@@ -6367,6 +6564,9 @@
"pydash": [
"cpe:2.3:a:derrickgilland:pydash:*:*:*:*:*:python:*:*"
],
"pydicom": [
"cpe:2.3:a:pydicom:pydicom:*:*:*:*:*:python:*:*"
],
"pyesasky": [
"cpe:2.3:a:esa:pyesasky:*:*:*:*:*:python:*:*"
],
@@ -6391,6 +6591,9 @@
"pypolicyd-spf": [
"cpe:2.3:a:pypolicyd-spf_project:pypolicyd-spf:*:*:*:*:*:*:*:*"
],
"python-dotenv": [
"cpe:2.3:a:saurabh-kumar:python-dotenv:*:*:*:*:*:python:*:*"
],
"python-gnupg": [
"cpe:2.3:a:python:python-gnupg:*:*:*:*:*:*:*:*"
],
@@ -6463,6 +6666,9 @@
"setuptools": [
"cpe:2.3:a:python:setuptools:*:*:*:*:*:*:*:*"
],
"sigstore": [
"cpe:2.3:a:linuxfoundation:sigstore-python:*:*:*:*:*:*:*:*"
],
"simiki": [
"cpe:2.3:a:simiki_project:simiki:*:*:*:*:*:*:*:*"
],
@@ -6490,12 +6696,21 @@
"starlette": [
"cpe:2.3:a:encode:starlette:*:*:*:*:*:python:*:*"
],
"strawberry-graphql": [
"cpe:2.3:a:strawberry:strawberry_graphql:*:*:*:*:*:python:*:*"
],
"tablib": [
"cpe:2.3:a:python:tablib:*:*:*:*:*:*:*:*"
],
"taguette": [
"cpe:2.3:a:taguette:taguette:*:*:*:*:*:*:*:*"
],
"telnyx": [
"cpe:2.3:a:telnyx:telnyx:*:*:*:*:*:python:*:*"
],
"tinytag": [
"cpe:2.3:a:tinytag:tinytag:*:*:*:*:*:python:*:*"
],
"tkvideoplayer": [
"cpe:2.3:a:python:tkvideoplayer:*:*:*:*:*:*:*:*"
],
@@ -6511,6 +6726,9 @@
"unearth": [
"cpe:2.3:a:frostming:unearth:*:*:*:*:*:python:*:*"
],
"unstructured": [
"cpe:2.3:a:unstructured:unstructured:*:*:*:*:*:python:*:*"
],
"untangle": [
"cpe:2.3:a:untangle_project:untangle:*:*:*:*:*:python:*:*"
],
@@ -6585,6 +6803,12 @@
"bson": [
"cpe:2.3:a:bson_project:bson:*:*:*:*:*:ruby:*:*"
],
"bsv-sdk": [
"cpe:2.3:a:sgbett:bsv_ruby_sdk:*:*:*:*:*:ruby:*:*"
],
"bsv-wallet": [
"cpe:2.3:a:sgbett:bsv-wallet:*:*:*:*:*:ruby:*:*"
],
"cap-strap": [
"cpe:2.3:a:cap-strap_project:cap-strap:*:*:*:*:*:ruby:*:*"
],
@@ -6718,11 +6942,15 @@
"httparty": [
"cpe:2.3:a:jnunemaker:httparty:*:*:*:*:*:ruby:*:*"
],
"icalendar": [
"cpe:2.3:a:icalendar_project:icalendar:*:*:*:*:*:ruby:*:*"
],
"iodine": [
"cpe:2.3:a:boazsegev:iodine:*:*:*:*:*:ruby:*:*"
],
"json": [
"cpe:2.3:a:json_project:json:*:*:*:*:*:ruby:*:*"
"cpe:2.3:a:json_project:json:*:*:*:*:*:ruby:*:*",
"cpe:2.3:a:ruby-lang:json:*:*:*:*:*:ruby:*:*"
],
"json-jwt": [
"cpe:2.3:a:json-jwt_project:json-jwt:*:*:*:*:*:ruby:*:*"
@@ -6754,12 +6982,18 @@
"lean-ruport": [
"cpe:2.3:a:lean-ruport_project:lean-ruport:*:*:*:*:*:ruby:*:*"
],
"lodash-rails": [
"cpe:2.3:a:lodash:lodash-rails:*:*:*:*:*:ruby:*:*"
],
"loofah": [
"cpe:2.3:a:loofah_project:loofah:*:*:*:*:*:ruby:*:*"
],
"lynx": [
"cpe:2.3:a:lynx_project:lynx:*:*:*:*:*:ruby:*:*"
],
"mcp": [
"cpe:2.3:a:lfprojects:mcp_ruby_sdk:*:*:*:*:*:*:*:*"
],
"mechanize": [
"cpe:2.3:a:mechanize_project:mechanize:*:*:*:*:*:ruby:*:*"
],
@@ -6896,6 +7130,9 @@
"rbovirt": [
"cpe:2.3:a:amos_benari:rbovirt:*:*:*:*:*:ruby:*:*"
],
"rdiscount": [
"cpe:2.3:a:dafoster:rdiscount:*:*:*:*:*:ruby:*:*"
],
"rdoc": [
"cpe:2.3:a:ruby-lang:rdoc:*:*:*:*:*:ruby:*:*"
],
@@ -7189,6 +7426,9 @@
"containers": [
"cpe:2.3:a:containers_project:containers:*:*:*:*:*:rust:*:*"
],
"coreutils": [
"cpe:2.3:a:uutils:coreutils:*:*:*:*:*:rust:*:*"
],
"cranelift-codegen": [
"cpe:2.3:a:bytecodealliance:cranelift-codegen:*:*:*:*:*:rust:*:*"
],
@@ -7264,6 +7504,9 @@
"heapless": [
"cpe:2.3:a:heapless_project:heapless:*:*:*:*:*:rust:*:*"
],
"httpsig-hyper": [
"cpe:2.3:a:junkurihara:httpsig-hyper:*:*:*:*:*:rust:*:*"
],
"ic-stable-structures": [
"cpe:2.3:a:dfinity:stable_structures:*:*:*:*:*:rust:*:*"
],
@@ -7309,6 +7552,9 @@
"libp2p-deflate": [
"cpe:2.3:a:libp2p:libp2p-deflate:*:*:*:*:*:rust:*:*"
],
"libp2p-gossipsub": [
"cpe:2.3:a:protocol:libp2p-gossipsub:*:*:*:*:*:rust:*:*"
],
"libpulse-binding": [
"cpe:2.3:a:libpulse-binding_project:libpulse-binding:*:*:*:*:*:rust:*:*"
],
@@ -7339,6 +7585,9 @@
"lucet-runtime-internals": [
"cpe:2.3:a:lucet-runtime-internals_project:lucet-runtime-internals:*:*:*:*:*:rust:*:*"
],
"lz4_flex": [
"cpe:2.3:a:pseitz:lz4_flex:*:*:*:*:*:rust:*:*"
],
"magnetic": [
"cpe:2.3:a:magnetic_project:magnetic:*:*:*:*:*:rust:*:*"
],
@@ -7480,6 +7729,9 @@
"simple_asn1": [
"cpe:2.3:a:simple_asn1_project:simple_asn1:*:*:*:*:*:rust:*:*"
],
"skim": [
"cpe:2.3:a:skim-rs:skim:*:*:*:*:*:rust:*:*"
],
"slack_morphism": [
"cpe:2.3:a:slack_morphism_project:slack_morphism:*:*:*:*:*:rust:*:*"
],
@@ -7564,6 +7816,9 @@
"try-mutex": [
"cpe:2.3:a:try-mutex_project:try-mutex:*:*:*:*:*:rust:*:*"
],
"tuftool": [
"cpe:2.3:a:amazon:tuftool:*:*:*:*:*:rust:*:*"
],
"tungstenite": [
"cpe:2.3:a:snapview:tungstenite:*:*:*:*:*:rust:*:*"
],
@@ -7606,6 +7861,18 @@
"zebra-consensus": [
"cpe:2.3:a:zfnd:zebra-consensus:*:*:*:*:*:rust:*:*"
],
"zebra-rpc": [
"cpe:2.3:a:zfnd:zebra-rpc:*:*:*:*:*:rust:*:*"
],
"zebra-script": [
"cpe:2.3:a:zfnd:zebra-script:*:*:*:*:*:rust:*:*"
],
"zebrad": [
"cpe:2.3:a:zfnd:zebrad:*:*:*:*:*:rust:*:*"
],
"zeptoclaw": [
"cpe:2.3:a:zeptoclaw:zeptoclaw:*:*:*:*:*:rust:*:*"
],
"zeroize_derive": [
"cpe:2.3:a:zeroize_derive_project:zeroize_derive:*:*:*:*:*:rust:*:*"
]
@@ -12356,6 +12623,9 @@
"friends": [
"cpe:2.3:a:alex.kirk:friends:*:*:*:*:*:wordpress:*:*"
],
"front-end-editor": [
"cpe:2.3:a:scribu:front-end_editor:*:*:*:*:*:wordpress:*:*"
],
"front-end-only-users": [
"cpe:2.3:a:etoilewebdesign:front_end_users:*:*:*:*:*:wordpress:*:*"
],
@@ -14098,6 +14368,10 @@
"link-to-bible": [
"cpe:2.3:a:bibleserver:link_to_bible:*:*:*:*:*:wordpress:*:*"
],
"link-whisper": [
"cpe:2.3:a:linkwhisper:link_whisper:*:*:*:*:*:wordpress:*:*",
"cpe:2.3:a:linkwhisper:link_whisper:*:*:*:*:free:wordpress:*:*"
],
"linked-variation": [
"cpe:2.3:a:multidots:advanced_linked_variations_for_woocommerce:*:*:*:*:*:wordpress:*:*"
],
@@ -17243,6 +17517,9 @@
"sexy-contact-form": [
"cpe:2.3:a:creative-solutions:creative_contact_form:*:*:*:*:*:wordpress:*:*"
],
"sg-cachepress": [
"cpe:2.3:a:siteground:speed_optimizer:*:*:*:*:*:wordpress:*:*"
],
"sg-security": [
"cpe:2.3:a:siteground:security_optimizer:*:*:*:*:*:wordpress:*:*"
],
@@ -18171,6 +18448,9 @@
"sprout-invoices": [
"cpe:2.3:a:webventures:client_invoicing_by_sprout_invoices:*:*:*:*:*:wordpress:*:*"
],
"sql-chart-builder": [
"cpe:2.3:a:guaven:sql_chart_builder:*:*:*:*:*:wordpress:*:*"
],
"squeeze": [
"cpe:2.3:a:squeeze_project:squeeze:*:*:*:*:*:wordpress:*:*"
],
@@ -21864,6 +22144,9 @@
"wpshopgermany-protectedshops": [
"cpe:2.3:a:maennchen1:wpshopgermany_-_protected_shops:*:*:*:*:*:wordpress:*:*"
],
"wpshopify": [
"cpe:2.3:a:shopwp_project:shopwp:*:*:*:*:*:wordpress:*:*"
],
"wpsimpletools-log-viewer": [
"cpe:2.3:a:wpsimpletools:basic_log_viewer:*:*:*:*:*:wordpress:*:*"
],
+1 -1
View File
@@ -108,7 +108,7 @@ func (a archiveFilename) extension() string {
func (a archiveFilename) pkgType() pkg.Type {
switch strings.ToLower(a.extension()) {
case "jar", "war", "ear", "lpkg", "par", "sar", "nar", "kar", "rar":
case "jar", "war", "ear", "lpkg", "par", "sar", "nar", "kar", "rar", "zap":
return pkg.JavaPkg
case "jpi", "hpi":
return pkg.JenkinsPluginPkg
@@ -48,6 +48,7 @@ var archiveFormatGlobs = []string{
// out of date, and they charge for their IDE. If you find an example
// project that we can build in CI feel free to include it
"**/*.rar", // Java Resource Adapter Archive
"**/*.zap", // ZAP add-ons https://github.com/zaproxy/zaproxy/wiki/ZapAddOns
}
// javaArchiveHashes are all the current hash algorithms used to calculate archive digests
@@ -351,6 +351,85 @@ func TestParseJar(t *testing.T) {
},
},
},
{
// Dupicate the example-java-app-gradle test and the Makefile is adjusted to copy its jar to example-zap-addon-0.1.0.zap
name: "example-zap-addon",
fixture: "testdata/java-builds/packages/example-zap-addon-0.1.0.zap",
wantErr: require.NoError, // no nested jars
expected: map[string]pkg.Package{
"example-zap-addon": {
Name: "example-zap-addon",
Version: "0.1.0",
PURL: "pkg:maven/example-zap-addon/example-zap-addon@0.1.0",
Language: pkg.Java,
Type: pkg.JavaPkg,
Licenses: pkg.NewLicenseSet(
pkg.License{
Value: "Apache-2.0",
SPDXExpression: "Apache-2.0",
Type: license.Concluded,
Locations: file.NewLocationSet(file.NewLocation("testdata/java-builds/packages/example-zap-addon-0.1.0.zap")),
},
),
Metadata: pkg.JavaArchive{
VirtualPath: "testdata/java-builds/packages/example-zap-addon-0.1.0.zap",
Manifest: &pkg.JavaManifest{
Main: []pkg.KeyValue{
{
Key: "Manifest-Version",
Value: "1.0",
},
{
Key: "Main-Class",
Value: "hello.HelloWorld",
},
},
},
// PomProject: &pkg.JavaPomProject{
// Path: "META-INF/maven/io.jenkins.plugins/example-jenkins-plugin/pom.xml",
// Parent: &pkg.JavaPomParent{GroupID: "org.jenkins-ci.plugins", ArtifactID: "plugin", Version: "4.46"},
// GroupID: "io.jenkins.plugins",
// ArtifactID: "example-jenkins-plugin",
// Version: "1.0-SNAPSHOT",
// Name: "Example Jenkins Plugin",
// },
},
},
"joda-time": {
Name: "joda-time",
Version: "2.2",
PURL: "pkg:maven/joda-time/joda-time@2.2",
Language: pkg.Java,
Type: pkg.JavaPkg,
Licenses: pkg.NewLicenseSet(
pkg.NewLicenseFromFieldsWithContext(ctx, "Apache 2", "http://www.apache.org/licenses/LICENSE-2.0.txt", func() *file.Location {
l := file.NewLocation("testdata/java-builds/packages/example-zap-addon-0.1.0.zap")
return &l
}()),
),
Metadata: pkg.JavaArchive{
// ensure that nested packages with different names than that of the parent are appended as
// a suffix on the virtual path with a colon separator between group name and artifact name
VirtualPath: "testdata/java-builds/packages/example-zap-addon-0.1.0.zap:joda-time:joda-time",
PomProperties: &pkg.JavaPomProperties{
Path: "META-INF/maven/joda-time/joda-time/pom.properties",
GroupID: "joda-time",
ArtifactID: "joda-time",
Version: "2.2",
},
PomProject: &pkg.JavaPomProject{
Path: "META-INF/maven/joda-time/joda-time/pom.xml",
GroupID: "joda-time",
ArtifactID: "joda-time",
Version: "2.2",
Name: "Joda time",
Description: "Date and time library to replace JDK date handling",
URL: "http://joda-time.sourceforge.net",
},
},
},
},
},
}
for _, test := range tests {
@@ -32,6 +32,7 @@ func Test_ArchiveCataloger_Globs(t *testing.T) {
"java-archives/example.far",
"java-archives/example.lpkg",
"java-archives/example.rar",
"java-archives/example.zap",
"archives/example.zip",
"archives/example.tar",
"archives/example.tar.gz",
@@ -0,0 +1 @@
example archive
+4 -1
View File
@@ -14,7 +14,7 @@ fixtures: jars archives native-image
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
fingerprint: $(FINGERPRINT_FILE)
jars: $(PKGSDIR)/example-java-app-maven-0.1.0.jar $(PKGSDIR)/example-java-app-gradle-0.1.0.jar $(PKGSDIR)/example-jenkins-plugin.hpi $(PKGSDIR)/spring-boot-0.0.1-SNAPSHOT.jar
jars: $(PKGSDIR)/example-java-app-maven-0.1.0.jar $(PKGSDIR)/example-java-app-gradle-0.1.0.jar $(PKGSDIR)/example-jenkins-plugin.hpi $(PKGSDIR)/spring-boot-0.0.1-SNAPSHOT.jar $(PKGSDIR)/example-zap-addon-0.1.0.zap
archives: $(PKGSDIR)/example-java-app-maven-0.1.0.zip $(PKGSDIR)/example-java-app-maven-0.1.0.tar $(PKGSDIR)/example-java-app-maven-0.1.0.tar.gz $(PKGSDIR)/example-java-app-maven-0.1.0.tgz
@@ -55,6 +55,9 @@ clean-maven:
$(PKGSDIR)/example-java-app-gradle-0.1.0.jar:
./build-example-java-app-gradle.sh $(PKGSDIR)
$(PKGSDIR)/example-zap-addon-0.1.0.zap: $(PKGSDIR)/example-java-app-gradle-0.1.0.jar
cp $(PKGSDIR)/example-java-app-gradle-0.1.0.jar $(PKGSDIR)/example-zap-addon-0.1.0.zap
clean-gradle:
rm -rf example-java-app/.gradle \
example-java-app/build
@@ -6,9 +6,7 @@ import (
"encoding/json"
"fmt"
"io"
"maps"
"regexp"
"slices"
"strings"
"github.com/goccy/go-yaml"
@@ -250,7 +248,9 @@ func parseYarnLockYaml(reader io.ReadCloser) ([]yarnPackage, error) {
return nil, fmt.Errorf("failed to unmarshal yarn v2 lockfile: %w", err)
}
packages := make(map[string]yarnPackage)
var seenPkgs = strset.New()
pkgs := []yarnPackage{}
for key, value := range lockfile {
packageName := findPackageName(key)
if packageName == "" {
@@ -258,10 +258,16 @@ func parseYarnLockYaml(reader io.ReadCloser) ([]yarnPackage, error) {
continue
}
packages[packageName] = yarnPackage{Name: packageName, Version: value.Version, Resolved: value.Resolution, Integrity: value.Checksum, Dependencies: value.Dependencies}
var pkg = yarnPackage{Name: packageName, Version: value.Version, Resolved: value.Resolution, Integrity: value.Checksum, Dependencies: value.Dependencies}
var nameVersion = pkg.Name + "@" + pkg.Version
if !seenPkgs.Has(nameVersion) {
seenPkgs.Add(nameVersion)
pkgs = append(pkgs, pkg)
}
}
return slices.Collect(maps.Values(packages)), nil
return pkgs, nil
}
func (a genericYarnLockAdapter) parseYarnLock(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
@@ -204,6 +204,42 @@ func TestParseYarnBerry(t *testing.T) {
pkgtest.TestFileParser(t, fixture, adapter.parseYarnLock, expectedPkgs, expectedRelationships)
}
func TestParseYarnBerryWithDuplicates(t *testing.T) {
var expectedRelationships []artifact.Relationship
fixture := "testdata/yarn-berry-dups/yarn.lock"
locations := file.NewLocationSet(file.NewLocation(fixture))
expectedPkgs := []pkg.Package{
{
Name: "async",
Version: "3.2.6",
Locations: locations,
PURL: "pkg:npm/async@3.2.6",
Language: pkg.JavaScript,
Type: pkg.NpmPkg,
Metadata: pkg.YarnLockEntry{
Resolved: "async@npm:3.2.6",
Integrity: "10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70",
},
},
{
Name: "async",
Version: "0.9.2",
Locations: locations,
PURL: "pkg:npm/async@0.9.2",
Language: pkg.JavaScript,
Type: pkg.NpmPkg,
Metadata: pkg.YarnLockEntry{
Resolved: "async@npm:0.9.2",
Integrity: "10c0/22ac816db119a9b84ac7182fa969b2cceacfcfa278c3efb0ac6a94d1210a4429e42c8cf6e704039aa7662e4ba62f26cecf039c91d41ceb91355dc9672c9b9ac1",
},
},
}
adapter := newGenericYarnLockAdapter(CatalogerConfig{})
pkgtest.TestFileParser(t, fixture, adapter.parseYarnLock, expectedPkgs, expectedRelationships)
}
func TestParseYarnLock(t *testing.T) {
var expectedRelationships []artifact.Relationship
fixture := "testdata/yarn/yarn.lock"
@@ -0,0 +1,20 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 8
cacheKey: 10c0
"async@npm:^3.2.3":
version: 3.2.6
resolution: "async@npm:3.2.6"
checksum: 10c0/36484bb15ceddf07078688d95e27076379cc2f87b10c03b6dd8a83e89475a3c8df5848859dd06a4c95af1e4c16fc973de0171a77f18ea00be899aca2a4f85e70
languageName: node
linkType: hard
"async@npm:0.9.2":
version: 0.9.2
resolution: "async@npm:0.9.2"
checksum: 10c0/22ac816db119a9b84ac7182fa969b2cceacfcfa278c3efb0ac6a94d1210a4429e42c8cf6e704039aa7662e4ba62f26cecf039c91d41ceb91355dc9672c9b9ac1
languageName: node
linkType: hard
+4 -1
View File
@@ -4,7 +4,7 @@ configs: # AUTO-GENERATED - config structs and their fields
kernel.LinuxKernelCatalogerConfig:
fields:
- key: CatalogModules
description: CatalogModules enables cataloging linux kernel modules (`*.ko` files) in addition to the kernel itself.
description: CatalogModules enables cataloging linux kernel modules (`*.ko` and compressed `*.ko.gz`, `*.ko.xz`, `*.ko.zst` files) in addition to the kernel itself.
app_key: linux-kernel.catalog-modules
catalogers:
- ecosystem: linux # MANUAL
@@ -36,6 +36,9 @@ catalogers:
- '**/zImage'
- '**/zImage-*'
- '**/lib/modules/**/*.ko'
- '**/lib/modules/**/*.ko.gz'
- '**/lib/modules/**/*.ko.xz'
- '**/lib/modules/**/*.ko.zst'
metadata_types: # AUTO-GENERATED
- pkg.LinuxKernel
- pkg.LinuxKernelModule
+4 -1
View File
@@ -17,7 +17,7 @@ import (
var _ pkg.Cataloger = (*linuxKernelCataloger)(nil)
type LinuxKernelCatalogerConfig struct {
// CatalogModules enables cataloging linux kernel modules (`*.ko` files) in addition to the kernel itself.
// CatalogModules enables cataloging linux kernel modules (`*.ko` and compressed `*.ko.gz`, `*.ko.xz`, `*.ko.zst` files) in addition to the kernel itself.
// app-config: linux-kernel.catalog-modules
CatalogModules bool `yaml:"catalog-modules" json:"catalog-modules" mapstructure:"catalog-modules"`
}
@@ -47,6 +47,9 @@ var kernelArchiveGlobs = []string{
var kernelModuleGlobs = []string{
"**/lib/modules/**/*.ko",
"**/lib/modules/**/*.ko.gz",
"**/lib/modules/**/*.ko.xz",
"**/lib/modules/**/*.ko.zst",
}
// NewLinuxKernelCataloger returns a new kernel files cataloger object.
@@ -3,9 +3,16 @@ package kernel
import (
"context"
"debug/elf"
"errors"
"fmt"
"io"
"os"
"strings"
"github.com/mholt/archives"
intfile "github.com/anchore/syft/internal/file"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/internal/unionreader"
@@ -20,7 +27,14 @@ func parseLinuxKernelModuleFile(ctx context.Context, _ file.Resolver, _ *generic
if err != nil {
return nil, nil, fmt.Errorf("unable to get union reader for file: %w", err)
}
metadata, err := parseLinuxKernelModuleMetadata(unionReader)
moduleReader, err := decompressedModuleReader(ctx, reader.RealPath, unionReader)
if err != nil {
return nil, nil, fmt.Errorf("unable to decompress kernel module %q: %w", reader.RealPath, err)
}
defer moduleReader.Close()
metadata, err := parseLinuxKernelModuleMetadata(moduleReader)
if err != nil {
return nil, nil, fmt.Errorf("unable to parse kernel module metadata: %w", err)
}
@@ -39,6 +53,91 @@ func parseLinuxKernelModuleFile(ctx context.Context, _ file.Resolver, _ *generic
}, nil, nil
}
// decompressedModuleReader returns a UnionReader over the decompressed contents of the kernel module
// when the path indicates compression (e.g. .ko.gz, .ko.xz, .ko.zst). For plain .ko files the original
// reader is returned unchanged. ELF parsing requires random access (io.ReaderAt + io.Seeker), so
// compressed streams are spilled to a temp file rather than buffered in memory — kernel modules can be
// tens of MB decompressed and large numbers of them are scanned per cataloger run. The caller owns
// the returned reader and must Close it; the underlying reader (r) is not closed by Close on the
// passthrough path — its lifecycle is the caller's.
func decompressedModuleReader(ctx context.Context, path string, r unionreader.UnionReader) (unionreader.UnionReader, error) {
// fast path: plain .ko files don't need format sniffing
if strings.HasSuffix(path, ".ko") {
return &nopCloseUnionReader{UnionReader: r}, nil
}
format, stream, err := intfile.IdentifyArchive(ctx, path, r)
if err != nil {
if errors.Is(err, archives.NoMatch) {
return passthrough(r)
}
return nil, fmt.Errorf("unable to identify compression format: %w", err)
}
decompressor, ok := format.(archives.Decompressor)
if !ok {
// not a single-stream compressed format (e.g. a tar/zip archive); treat as a plain .ko
return passthrough(r)
}
rc, err := decompressor.OpenReader(stream)
if err != nil {
return nil, fmt.Errorf("unable to open %s decompression stream: %w", format.Extension(), err)
}
defer rc.Close()
td := tmpdir.FromContext(ctx)
if td == nil {
return nil, fmt.Errorf("no temp dir factory in context")
}
tempFile, fileCleanup, err := td.NewFile("syft-kmod-*.ko") //nolint:gocritic // cleanup outlives this function — runs from tempFileUnionReader.Close on the returned reader
if err != nil {
fileCleanup()
return nil, fmt.Errorf("unable to create temp file for decompressed kernel module: %w", err)
}
tfr := &tempFileUnionReader{File: tempFile, cleanup: fileCleanup}
if _, err := io.Copy(tempFile, rc); err != nil {
_ = tfr.Close()
return nil, fmt.Errorf("unable to write decompressed kernel module: %w", err)
}
if _, err := tempFile.Seek(0, io.SeekStart); err != nil {
_ = tfr.Close()
return nil, fmt.Errorf("unable to rewind decompressed kernel module: %w", err)
}
return tfr, nil
}
// passthrough returns the original reader rewound to offset 0. IdentifyArchive consumes bytes to
// sniff magic; we rewind explicitly so callers don't have to reason about the seeker's position.
func passthrough(r unionreader.UnionReader) (unionreader.UnionReader, error) {
if _, err := r.Seek(0, io.SeekStart); err != nil {
return nil, fmt.Errorf("unable to rewind reader after format sniff: %w", err)
}
return &nopCloseUnionReader{UnionReader: r}, nil
}
// nopCloseUnionReader wraps a UnionReader so that Close is a no-op. used on the passthrough path
// where the underlying reader's lifecycle is owned by the caller, not by us.
type nopCloseUnionReader struct {
unionreader.UnionReader
}
func (*nopCloseUnionReader) Close() error { return nil }
// tempFileUnionReader is a UnionReader backed by a temp file; Close closes the file and removes it.
type tempFileUnionReader struct {
*os.File
cleanup func()
}
func (t *tempFileUnionReader) Close() error {
err := t.File.Close()
t.cleanup()
return err
}
func parseLinuxKernelModuleMetadata(r unionreader.UnionReader) (p *pkg.LinuxKernelModule, err error) {
// filename: /lib/modules/5.15.0-1031-aws/kernel/zfs/zzstd.ko
// version: 1.4.5a
@@ -0,0 +1,285 @@
package kernel
import (
"bytes"
"compress/gzip"
"context"
"debug/elf"
"encoding/binary"
"io"
"os"
"testing"
"github.com/klauspost/compress/zstd"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/ulikunitz/xz"
"github.com/anchore/syft/internal/tmpdir"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)
func testContext(t *testing.T) context.Context {
t.Helper()
td := tmpdir.FromPath(t.TempDir())
return tmpdir.WithValue(context.Background(), td)
}
// minimalKOBytes constructs a minimal ELF64 LE relocatable object with a .modinfo
// section containing the given null-terminated key=value entries.
func minimalKOBytes(entries []string) []byte {
// .modinfo section: each entry is key=value\0
var modinfo bytes.Buffer
for _, e := range entries {
modinfo.WriteString(e)
modinfo.WriteByte(0)
}
// section header string table — embeds names of all sections back-to-back, leading null required.
// offsets below index into this blob.
shstrtab := []byte("\x00.modinfo\x00.shstrtab\x00")
const (
modinfoNameOff uint32 = 1
shstrtabNameOff uint32 = 10
)
const (
ehdrSize uint64 = 64
shdrSize uint64 = 64
numSections uint16 = 3 // null + .modinfo + .shstrtab
)
// layout: [ehdr][modinfo][shstrtab][pad to 8][section headers]
var (
modinfoOff = ehdrSize
modinfoSize = uint64(modinfo.Len())
shstrtabOff = modinfoOff + modinfoSize
shstrtabSize = uint64(len(shstrtab))
shdrsOff = alignUp(shstrtabOff+shstrtabSize, 8)
)
header := elf.Header64{
Ident: [16]byte{
0x7f, 'E', 'L', 'F',
byte(elf.ELFCLASS64),
byte(elf.ELFDATA2LSB),
byte(elf.EV_CURRENT),
},
Type: uint16(elf.ET_REL),
Machine: uint16(elf.EM_X86_64),
Version: uint32(elf.EV_CURRENT),
Shoff: shdrsOff,
Ehsize: uint16(ehdrSize),
Shentsize: uint16(shdrSize),
Shnum: numSections,
Shstrndx: numSections - 1, // .shstrtab is last
}
sections := []elf.Section64{
{}, // SHN_UNDEF
{
Name: modinfoNameOff,
Type: uint32(elf.SHT_PROGBITS),
Off: modinfoOff,
Size: modinfoSize,
Addralign: 1,
},
{
Name: shstrtabNameOff,
Type: uint32(elf.SHT_STRTAB),
Off: shstrtabOff,
Size: shstrtabSize,
Addralign: 1,
},
}
var buf bytes.Buffer
_ = binary.Write(&buf, binary.LittleEndian, header)
buf.Write(modinfo.Bytes())
buf.Write(shstrtab)
for uint64(buf.Len()) < shdrsOff {
buf.WriteByte(0)
}
for _, s := range sections {
_ = binary.Write(&buf, binary.LittleEndian, s)
}
return buf.Bytes()
}
func alignUp(v, align uint64) uint64 {
if v%align == 0 {
return v
}
return v + (align - v%align)
}
func gzCompress(data []byte) []byte {
var buf bytes.Buffer
w := gzip.NewWriter(&buf)
_, _ = w.Write(data)
_ = w.Close()
return buf.Bytes()
}
func xzCompress(data []byte) []byte {
var buf bytes.Buffer
w, _ := xz.NewWriter(&buf)
_, _ = w.Write(data)
_ = w.Close()
return buf.Bytes()
}
func zstCompress(data []byte) []byte {
var buf bytes.Buffer
w, _ := zstd.NewWriter(&buf)
_, _ = w.Write(data)
_ = w.Close()
return buf.Bytes()
}
// makeLocationReadCloser wraps a byte slice as a file.LocationReadCloser with the given path.
func makeLocationReadCloser(path string, data []byte) file.LocationReadCloser {
return file.LocationReadCloser{
Location: file.NewVirtualLocation(path, path),
ReadCloser: io.NopCloser(bytes.NewReader(data)),
}
}
func TestParseLinuxKernelModuleFile_Compressed(t *testing.T) {
modinfo := []string{
"name=dummy_mod",
"version=1.2.3",
"vermagic=6.1.0-rc1 SMP mod_unload",
"license=GPL v2",
}
koBytes := minimalKOBytes(modinfo)
tests := []struct {
name string
path string
data []byte
wantName string
wantVer string
wantKV string // expected KernelVersion from vermagic
}{
{
name: "uncompressed .ko",
path: "/lib/modules/6.1.0-rc1/kernel/dummy_mod.ko",
data: koBytes,
wantName: "dummy_mod",
wantVer: "1.2.3",
wantKV: "6.1.0-rc1",
},
{
name: "gzip-compressed .ko.gz",
path: "/lib/modules/6.1.0-rc1/kernel/dummy_mod.ko.gz",
data: gzCompress(koBytes),
wantName: "dummy_mod",
wantVer: "1.2.3",
wantKV: "6.1.0-rc1",
},
{
name: "xz-compressed .ko.xz",
path: "/lib/modules/6.1.0-rc1/kernel/dummy_mod.ko.xz",
data: xzCompress(koBytes),
wantName: "dummy_mod",
wantVer: "1.2.3",
wantKV: "6.1.0-rc1",
},
{
name: "zstd-compressed .ko.zst",
path: "/lib/modules/6.1.0-rc1/kernel/dummy_mod.ko.zst",
data: zstCompress(koBytes),
wantName: "dummy_mod",
wantVer: "1.2.3",
wantKV: "6.1.0-rc1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reader := makeLocationReadCloser(tt.path, tt.data)
pkgs, rels, err := parseLinuxKernelModuleFile(testContext(t), nil, &generic.Environment{}, reader)
require.NoError(t, err)
require.Len(t, pkgs, 1)
assert.Empty(t, rels)
assert.Equal(t, tt.wantName, pkgs[0].Name)
assert.Equal(t, tt.wantVer, pkgs[0].Version)
meta, ok := pkgs[0].Metadata.(pkg.LinuxKernelModule)
require.True(t, ok)
assert.Equal(t, tt.wantKV, meta.KernelVersion)
})
}
}
func TestDecompressedModuleReader(t *testing.T) {
koBytes := minimalKOBytes([]string{"name=test", "vermagic=5.15.0 SMP mod_unload"})
tests := []struct {
name string
path string
data []byte
}{
{"uncompressed", "/lib/modules/5.15.0/kernel/test.ko", koBytes},
{"gz", "/lib/modules/5.15.0/kernel/test.ko.gz", gzCompress(koBytes)},
{"xz", "/lib/modules/5.15.0/kernel/test.ko.xz", xzCompress(koBytes)},
{"zst", "/lib/modules/5.15.0/kernel/test.ko.zst", zstCompress(koBytes)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
br := bytes.NewReader(tt.data)
wrapped := struct {
io.ReadCloser
io.ReaderAt
io.Seeker
}{
ReadCloser: io.NopCloser(br),
ReaderAt: br,
Seeker: br,
}
got, err := decompressedModuleReader(testContext(t), tt.path, wrapped)
require.NoError(t, err)
require.NotNil(t, got)
t.Cleanup(func() { _ = got.Close() })
b, err := io.ReadAll(got)
require.NoError(t, err)
assert.Equal(t, koBytes, b, "decompressed bytes should match original .ko bytes")
})
}
}
func TestDecompressedModuleReader_TempFileRemovedOnClose(t *testing.T) {
koBytes := minimalKOBytes([]string{"name=test", "vermagic=5.15.0 SMP"})
data := gzCompress(koBytes)
br := bytes.NewReader(data)
wrapped := struct {
io.ReadCloser
io.ReaderAt
io.Seeker
}{
ReadCloser: io.NopCloser(br),
ReaderAt: br,
Seeker: br,
}
got, err := decompressedModuleReader(testContext(t), "/test.ko.gz", wrapped)
require.NoError(t, err)
tfr, ok := got.(*tempFileUnionReader)
require.True(t, ok, "expected compressed path to spill to a temp file")
path := tfr.File.Name()
_, err = os.Stat(path)
require.NoError(t, err, "temp file should exist before Close")
require.NoError(t, got.Close())
_, err = os.Stat(path)
assert.True(t, os.IsNotExist(err), "temp file should be removed after Close, got err=%v", err)
}
@@ -73,63 +73,63 @@ func Test_InterpreterCataloger(t *testing.T) {
fixture: "image-apache",
expectedPkgs: []string{
// interpreters
"libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
// extensions
"calendar @ 8.2.30 (/usr/lib/php/20220829/calendar.so)",
"ctype @ 8.2.30 (/usr/lib/php/20220829/ctype.so)",
"exif @ 8.2.30 (/usr/lib/php/20220829/exif.so)",
"ffi @ 8.2.30 (/usr/lib/php/20220829/ffi.so)",
"fileinfo @ 8.2.30 (/usr/lib/php/20220829/fileinfo.so)",
"ftp @ 8.2.30 (/usr/lib/php/20220829/ftp.so)",
"gettext @ 8.2.30 (/usr/lib/php/20220829/gettext.so)",
"iconv @ 8.2.30 (/usr/lib/php/20220829/iconv.so)",
"mysqli @ 8.2.30 (/usr/lib/php/20220829/mysqli.so)",
"opcache @ 8.2.30 (/usr/lib/php/20220829/opcache.so)",
"pdo @ 8.2.30 (/usr/lib/php/20220829/pdo.so)",
"pdo_mysql @ 8.2.30 (/usr/lib/php/20220829/pdo_mysql.so)",
"phar @ 8.2.30 (/usr/lib/php/20220829/phar.so)",
"posix @ 8.2.30 (/usr/lib/php/20220829/posix.so)",
"readline @ 8.2.30 (/usr/lib/php/20220829/readline.so)",
"shmop @ 8.2.30 (/usr/lib/php/20220829/shmop.so)",
"simplexml @ 8.2.30 (/usr/lib/php/20220829/simplexml.so)",
"sockets @ 8.2.30 (/usr/lib/php/20220829/sockets.so)",
"sysvmsg @ 8.2.30 (/usr/lib/php/20220829/sysvmsg.so)",
"sysvsem @ 8.2.30 (/usr/lib/php/20220829/sysvsem.so)",
"sysvshm @ 8.2.30 (/usr/lib/php/20220829/sysvshm.so)",
"tokenizer @ 8.2.30 (/usr/lib/php/20220829/tokenizer.so)",
"xml @ 8.2.30 (/usr/lib/php/20220829/xml.so)",
"xmlreader @ 8.2.30 (/usr/lib/php/20220829/xmlreader.so)",
"xmlwriter @ 8.2.30 (/usr/lib/php/20220829/xmlwriter.so)",
"xsl @ 8.2.30 (/usr/lib/php/20220829/xsl.so)",
"calendar @ 8.2.31 (/usr/lib/php/20220829/calendar.so)",
"ctype @ 8.2.31 (/usr/lib/php/20220829/ctype.so)",
"exif @ 8.2.31 (/usr/lib/php/20220829/exif.so)",
"ffi @ 8.2.31 (/usr/lib/php/20220829/ffi.so)",
"fileinfo @ 8.2.31 (/usr/lib/php/20220829/fileinfo.so)",
"ftp @ 8.2.31 (/usr/lib/php/20220829/ftp.so)",
"gettext @ 8.2.31 (/usr/lib/php/20220829/gettext.so)",
"iconv @ 8.2.31 (/usr/lib/php/20220829/iconv.so)",
"mysqli @ 8.2.31 (/usr/lib/php/20220829/mysqli.so)",
"opcache @ 8.2.31 (/usr/lib/php/20220829/opcache.so)",
"pdo @ 8.2.31 (/usr/lib/php/20220829/pdo.so)",
"pdo_mysql @ 8.2.31 (/usr/lib/php/20220829/pdo_mysql.so)",
"phar @ 8.2.31 (/usr/lib/php/20220829/phar.so)",
"posix @ 8.2.31 (/usr/lib/php/20220829/posix.so)",
"readline @ 8.2.31 (/usr/lib/php/20220829/readline.so)",
"shmop @ 8.2.31 (/usr/lib/php/20220829/shmop.so)",
"simplexml @ 8.2.31 (/usr/lib/php/20220829/simplexml.so)",
"sockets @ 8.2.31 (/usr/lib/php/20220829/sockets.so)",
"sysvmsg @ 8.2.31 (/usr/lib/php/20220829/sysvmsg.so)",
"sysvsem @ 8.2.31 (/usr/lib/php/20220829/sysvsem.so)",
"sysvshm @ 8.2.31 (/usr/lib/php/20220829/sysvshm.so)",
"tokenizer @ 8.2.31 (/usr/lib/php/20220829/tokenizer.so)",
"xml @ 8.2.31 (/usr/lib/php/20220829/xml.so)",
"xmlreader @ 8.2.31 (/usr/lib/php/20220829/xmlreader.so)",
"xmlwriter @ 8.2.31 (/usr/lib/php/20220829/xmlwriter.so)",
"xsl @ 8.2.31 (/usr/lib/php/20220829/xsl.so)",
},
expectedRels: []string{
"calendar @ 8.2.30 (/usr/lib/php/20220829/calendar.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"ctype @ 8.2.30 (/usr/lib/php/20220829/ctype.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"exif @ 8.2.30 (/usr/lib/php/20220829/exif.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"ffi @ 8.2.30 (/usr/lib/php/20220829/ffi.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"fileinfo @ 8.2.30 (/usr/lib/php/20220829/fileinfo.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"ftp @ 8.2.30 (/usr/lib/php/20220829/ftp.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"gettext @ 8.2.30 (/usr/lib/php/20220829/gettext.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"iconv @ 8.2.30 (/usr/lib/php/20220829/iconv.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"mysqli @ 8.2.30 (/usr/lib/php/20220829/mysqli.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"opcache @ 8.2.30 (/usr/lib/php/20220829/opcache.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"pdo @ 8.2.30 (/usr/lib/php/20220829/pdo.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"pdo_mysql @ 8.2.30 (/usr/lib/php/20220829/pdo_mysql.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"phar @ 8.2.30 (/usr/lib/php/20220829/phar.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"posix @ 8.2.30 (/usr/lib/php/20220829/posix.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"readline @ 8.2.30 (/usr/lib/php/20220829/readline.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"shmop @ 8.2.30 (/usr/lib/php/20220829/shmop.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"simplexml @ 8.2.30 (/usr/lib/php/20220829/simplexml.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"sockets @ 8.2.30 (/usr/lib/php/20220829/sockets.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"sysvmsg @ 8.2.30 (/usr/lib/php/20220829/sysvmsg.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"sysvsem @ 8.2.30 (/usr/lib/php/20220829/sysvsem.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"sysvshm @ 8.2.30 (/usr/lib/php/20220829/sysvshm.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"tokenizer @ 8.2.30 (/usr/lib/php/20220829/tokenizer.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"xml @ 8.2.30 (/usr/lib/php/20220829/xml.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"xmlreader @ 8.2.30 (/usr/lib/php/20220829/xmlreader.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"xmlwriter @ 8.2.30 (/usr/lib/php/20220829/xmlwriter.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"xsl @ 8.2.30 (/usr/lib/php/20220829/xsl.so) [dependency-of] libphp @ 8.2.30 (/usr/lib/apache2/modules/libphp8.2.so)",
"calendar @ 8.2.31 (/usr/lib/php/20220829/calendar.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"ctype @ 8.2.31 (/usr/lib/php/20220829/ctype.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"exif @ 8.2.31 (/usr/lib/php/20220829/exif.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"ffi @ 8.2.31 (/usr/lib/php/20220829/ffi.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"fileinfo @ 8.2.31 (/usr/lib/php/20220829/fileinfo.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"ftp @ 8.2.31 (/usr/lib/php/20220829/ftp.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"gettext @ 8.2.31 (/usr/lib/php/20220829/gettext.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"iconv @ 8.2.31 (/usr/lib/php/20220829/iconv.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"mysqli @ 8.2.31 (/usr/lib/php/20220829/mysqli.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"opcache @ 8.2.31 (/usr/lib/php/20220829/opcache.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"pdo @ 8.2.31 (/usr/lib/php/20220829/pdo.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"pdo_mysql @ 8.2.31 (/usr/lib/php/20220829/pdo_mysql.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"phar @ 8.2.31 (/usr/lib/php/20220829/phar.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"posix @ 8.2.31 (/usr/lib/php/20220829/posix.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"readline @ 8.2.31 (/usr/lib/php/20220829/readline.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"shmop @ 8.2.31 (/usr/lib/php/20220829/shmop.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"simplexml @ 8.2.31 (/usr/lib/php/20220829/simplexml.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"sockets @ 8.2.31 (/usr/lib/php/20220829/sockets.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"sysvmsg @ 8.2.31 (/usr/lib/php/20220829/sysvmsg.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"sysvsem @ 8.2.31 (/usr/lib/php/20220829/sysvsem.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"sysvshm @ 8.2.31 (/usr/lib/php/20220829/sysvshm.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"tokenizer @ 8.2.31 (/usr/lib/php/20220829/tokenizer.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"xml @ 8.2.31 (/usr/lib/php/20220829/xml.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"xmlreader @ 8.2.31 (/usr/lib/php/20220829/xmlreader.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"xmlwriter @ 8.2.31 (/usr/lib/php/20220829/xmlwriter.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
"xsl @ 8.2.31 (/usr/lib/php/20220829/xsl.so) [dependency-of] libphp @ 8.2.31 (/usr/lib/apache2/modules/libphp8.2.so)",
},
},
}
+8 -1
View File
@@ -1,6 +1,13 @@
FROM --platform=linux/amd64 httpd:2.4.63-bookworm AS builder
RUN apt update -y && apt install -y libapache2-mod-php php8.2-memcache php8.2-memcache php8.2-xml php8.2-mysqli php8.2-opcache
# pin php8.2 packages to avoid version drift when debian publishes security updates
ARG PHP_VERSION=8.2.31-1~deb12u1
RUN apt update -y && apt install -y \
libapache2-mod-php8.2=${PHP_VERSION} \
php8.2-common=${PHP_VERSION} \
php8.2-xml=${PHP_VERSION} \
php8.2-mysql=${PHP_VERSION} \
php8.2-opcache=${PHP_VERSION}
FROM busybox:latest

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