mirror of
https://github.com/anchore/syft.git
synced 2026-10-24 20:12:22 +02:00
Compare commits
+7
-7
@@ -2,7 +2,7 @@ tools:
|
||||
# we want to use a pinned version of binny to manage the toolchain (so binny manages itself!)
|
||||
- name: binny
|
||||
version:
|
||||
want: v0.9.0
|
||||
want: v0.10.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: anchore/binny
|
||||
@@ -26,7 +26,7 @@ tools:
|
||||
# used for linting
|
||||
- name: golangci-lint
|
||||
version:
|
||||
want: v2.4.0
|
||||
want: v2.5.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: golangci/golangci-lint
|
||||
@@ -42,7 +42,7 @@ tools:
|
||||
# used for signing the checksums file at release
|
||||
- name: cosign
|
||||
version:
|
||||
want: v2.6.0
|
||||
want: v3.0.2
|
||||
method: github-release
|
||||
with:
|
||||
repo: sigstore/cosign
|
||||
@@ -58,7 +58,7 @@ tools:
|
||||
# used to release all artifacts
|
||||
- name: goreleaser
|
||||
version:
|
||||
want: v2.12.0
|
||||
want: v2.12.5
|
||||
method: github-release
|
||||
with:
|
||||
repo: goreleaser/goreleaser
|
||||
@@ -90,7 +90,7 @@ tools:
|
||||
# used for running all local and CI tasks
|
||||
- name: task
|
||||
version:
|
||||
want: v3.44.1
|
||||
want: v3.45.4
|
||||
method: github-release
|
||||
with:
|
||||
repo: go-task/task
|
||||
@@ -98,7 +98,7 @@ tools:
|
||||
# used for triggering a release
|
||||
- name: gh
|
||||
version:
|
||||
want: v2.79.0
|
||||
want: v2.81.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: cli/cli
|
||||
@@ -114,7 +114,7 @@ tools:
|
||||
# used to upload test fixture cache
|
||||
- name: yq
|
||||
version:
|
||||
want: v4.47.2
|
||||
want: v4.48.1
|
||||
method: github-release
|
||||
with:
|
||||
repo: mikefarah/yq
|
||||
|
||||
@@ -9,6 +9,9 @@ permit:
|
||||
- Unlicense
|
||||
|
||||
ignore-packages:
|
||||
# https://github.com/sorairolake/lzip-go/blob/34a2615d2abf740175c6b0a835baa08364e09430/go.sum.license#L3
|
||||
# has `SPDX-License-Identifier: Apache-2.0 OR MIT`, both of which are acceptable
|
||||
- github.com/sorairolake/lzip-go
|
||||
# packageurl-go is released under the MIT license located in the root of the repo at /mit.LICENSE
|
||||
- github.com/anchore/packageurl-go
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ runs:
|
||||
- name: Restore tool cache
|
||||
if: inputs.tools == 'true'
|
||||
id: tool-cache
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tool
|
||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
|
||||
@@ -63,7 +63,7 @@ runs:
|
||||
|
||||
- name: Restore ORAS cache from github actions
|
||||
if: inputs.download-test-fixture-cache == 'true'
|
||||
uses: actions/cache@0400d5f644dc74513175e3cd8d07132dd4860809 # v4.2.4
|
||||
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp/oras-cache
|
||||
key: ${{ inputs.cache-key-prefix }}-oras-cache
|
||||
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@192325c86100d080feab897ff886c34abd4c83a3 #v3.29.5
|
||||
uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 #v3.29.5
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@192325c86100d080feab897ff886c34abd4c83a3 #v3.29.5
|
||||
uses: github/codeql-action/autobuild@f443b600d91635bebf5b0d9ebc620189c0d6fba5 #v3.29.5
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -72,4 +72,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@192325c86100d080feab897ff886c34abd4c83a3 #v3.29.5
|
||||
uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 #v3.29.5
|
||||
|
||||
@@ -161,7 +161,7 @@ jobs:
|
||||
# for updating brew formula in anchore/homebrew-syft
|
||||
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
|
||||
|
||||
- uses: anchore/sbom-action@da167eac915b4e86f08b264dbdbc867b61be6f0c #v0.20.5
|
||||
- uses: anchore/sbom-action@f8bdd1d8ac5e901a77a92f111440fdb1b593736b #v0.20.6
|
||||
continue-on-error: true
|
||||
with:
|
||||
file: go.mod
|
||||
|
||||
@@ -90,7 +90,7 @@ jobs:
|
||||
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
||||
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
||||
- name: Upload snapshot artifacts
|
||||
uses: actions/cache/save@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
||||
with:
|
||||
# we need to preserve the snapshot data itself as well as the task data that confirms if the
|
||||
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -175,7 +175,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -225,7 +225,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@0400d5f644dc74513175e3cd8d07132dd4860809 #v4.2.4
|
||||
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 #v4.3.0
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
|
||||
@@ -16,6 +16,8 @@ bin/
|
||||
/snapshot
|
||||
/.tool
|
||||
/.task
|
||||
/generate
|
||||
/specs
|
||||
|
||||
# changelog generation
|
||||
CHANGELOG.md
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ builds:
|
||||
dir: ./cmd/syft
|
||||
binary: syft
|
||||
goos: [windows]
|
||||
goarch: [amd64]
|
||||
goarch: [amd64, arm64]
|
||||
mod_timestamp: *build-timestamp
|
||||
ldflags: *build-ldflags
|
||||
|
||||
|
||||
+3
-3
@@ -27,7 +27,7 @@ Also double check that the docker context being used is the default context. If
|
||||
|
||||
`docker context use default`
|
||||
|
||||
After cloning the following step can help you get setup:
|
||||
After cloning, the following steps can help you get setup:
|
||||
1. run `make bootstrap` to download go mod dependencies, create the `/.tmp` dir, and download helper utilities.
|
||||
2. run `make` to view the selection of developer commands in the Makefile
|
||||
3. run `make build` to build the release snapshot binaries and packages
|
||||
@@ -120,7 +120,7 @@ sequenceDiagram
|
||||
source-->>+sbom: add source to SBOM struct
|
||||
source-->>+catalog: pass src to generate catalog
|
||||
catalog-->-sbom: add cataloging results onto SBOM
|
||||
sbom-->>encoder: pass SBOM and format desiered to syft encoder
|
||||
sbom-->>encoder: pass SBOM and format desired to syft encoder
|
||||
encoder-->>source: return bytes that are the SBOM of the original input
|
||||
|
||||
Note right of catalog: cataloger configuration is done based on src
|
||||
@@ -186,7 +186,7 @@ Cataloger names should be unique and named with the following rules of thumb in
|
||||
- Use lowercase letters, numbers, and hyphens only
|
||||
- Use hyphens to separate words
|
||||
- Catalogers for language ecosystems should start with the language name (e.g. `python-` for a cataloger that raises up python packages)
|
||||
- Distinct between when the cataloger is searching for evidence of installed packages vs declared packages. For example, there are currently two different gemspec-based catalogers, the `ruby-gemspec-cataloger` and `ruby-installed-gemspec-cataloger`, where the latter requires that the gemspec is found within a `specifications` directory (which means it was installed, not just at the root of a source repo).
|
||||
- Distinguish between when the cataloger is searching for evidence of installed packages vs declared packages. For example, there are currently two different gemspec-based catalogers, the `ruby-gemspec-cataloger` and `ruby-installed-gemspec-cataloger`, where the latter requires that the gemspec is found within a `specifications` directory (which means it was installed, not just at the root of a source repo).
|
||||
|
||||
#### Building a new Cataloger
|
||||
|
||||
|
||||
@@ -133,6 +133,7 @@ Note that flags using the @<version> can be used for earlier versions of each sp
|
||||
- Elixir (mix)
|
||||
- Erlang (rebar3)
|
||||
- Go (go.mod, Go binaries)
|
||||
- GitHub (workflows, actions)
|
||||
- Haskell (cabal, stack)
|
||||
- Java (jar, ear, war, par, sar, nar, native-image)
|
||||
- JavaScript (npm, yarn)
|
||||
|
||||
+1
-1
@@ -29,4 +29,4 @@ To report a security issue, please email
|
||||
with a description of the issue, the steps you took to create the issue,
|
||||
affected versions, and, if known, mitigations for the issue.
|
||||
|
||||
All support will be made on the best effort base, so please indicate the "urgency level" of the vulnerability as Critical, High, Medium or Low.
|
||||
All support will be made on a best effort basis, so please indicate the "urgency level" of the vulnerability as Critical, High, Medium or Low.
|
||||
|
||||
+1
-1
@@ -502,7 +502,7 @@ tasks:
|
||||
generate-json-schema:
|
||||
desc: Generate a new JSON schema
|
||||
cmds:
|
||||
- "cd syft/internal && go generate . && cd jsonschema && go run . && go fmt ../..."
|
||||
- "cd ./internal && go generate . && cd ./jsonschema && go run . && go fmt ../..."
|
||||
|
||||
generate-license-list:
|
||||
desc: Generate an updated license processing code off of the latest available SPDX license list
|
||||
|
||||
@@ -4,7 +4,7 @@ go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.2
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.3
|
||||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/OneOfOne/xxhash v1.2.8
|
||||
@@ -24,14 +24,14 @@ require (
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
|
||||
github.com/anchore/stereoscope v0.1.10
|
||||
github.com/anchore/stereoscope v0.1.11
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1
|
||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1
|
||||
github.com/charmbracelet/bubbles v0.21.0
|
||||
github.com/charmbracelet/bubbletea v1.3.9
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/dave/jennifer v1.7.1
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
@@ -41,19 +41,19 @@ require (
|
||||
github.com/elliotchance/phpserialize v1.4.0
|
||||
github.com/facebookincubator/nvdtools v0.1.5
|
||||
github.com/github/go-spdx/v2 v2.3.3
|
||||
github.com/gkampitakis/go-snaps v0.5.14
|
||||
github.com/gkampitakis/go-snaps v0.5.15
|
||||
github.com/go-git/go-billy/v5 v5.6.2
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/go-git/go-git/v5 v5.16.3
|
||||
github.com/go-test/deep v1.1.1
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0
|
||||
github.com/gohugoio/hashstructure v0.5.0
|
||||
github.com/gohugoio/hashstructure v0.6.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/go-containerregistry v0.20.6
|
||||
github.com/google/licensecheck v0.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gookit/color v1.6.0
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2
|
||||
github.com/hashicorp/go-getter v1.8.0
|
||||
github.com/hashicorp/go-getter v1.8.2
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/hcl/v2 v2.24.0
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
@@ -62,13 +62,13 @@ require (
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||
github.com/magiconair/properties v1.8.10
|
||||
github.com/mholt/archives v0.1.3
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/moby/sys/mountinfo v0.7.2
|
||||
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1
|
||||
github.com/olekukonko/tablewriter v1.0.9
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.23
|
||||
github.com/rust-secure-code/go-rustaudit v0.0.0-20250226111315-e20ec32e963c
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
|
||||
github.com/sanity-io/litter v1.5.8
|
||||
@@ -90,9 +90,9 @@ require (
|
||||
go.uber.org/goleak v1.3.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
|
||||
golang.org/x/mod v0.28.0
|
||||
golang.org/x/net v0.44.0
|
||||
modernc.org/sqlite v1.39.0
|
||||
golang.org/x/mod v0.29.0
|
||||
golang.org/x/net v0.46.0
|
||||
modernc.org/sqlite v1.39.1
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -110,11 +110,11 @@ require (
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.7 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/STARRY-S/zip v0.2.1 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/agext/levenshtein v1.2.1 // indirect; indirectt
|
||||
github.com/anchore/go-lzo v0.1.0 // indirect
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
|
||||
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/aquasecurity/go-version v0.0.1 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
@@ -122,7 +122,7 @@ require (
|
||||
github.com/becheran/wildmatch-go v1.0.0 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.1 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||
@@ -144,9 +144,9 @@ require (
|
||||
github.com/containerd/typeurl/v2 v2.2.0 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/cli v28.4.0+incompatible // indirect
|
||||
github.com/docker/cli v28.5.1+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker v28.4.0+incompatible // indirect
|
||||
github.com/docker/docker v28.5.1+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||
@@ -174,11 +174,10 @@ require (
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-safetemp v1.0.0 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
|
||||
github.com/iancoleman/orderedmap v0.3.0
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
@@ -195,7 +194,7 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/minlz v1.0.0 // indirect
|
||||
github.com/minio/minlz v1.0.1 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
@@ -211,7 +210,7 @@ require (
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.0.9 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
@@ -233,7 +232,7 @@ require (
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.5 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/cast v1.7.1 // indirect
|
||||
github.com/spf13/pflag v1.0.9 // indirect
|
||||
@@ -265,14 +264,14 @@ require (
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/crypto v0.43.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.17.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/term v0.35.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/term v0.36.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
golang.org/x/tools v0.37.0
|
||||
golang.org/x/tools v0.38.0
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/api v0.203.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
||||
@@ -281,8 +280,8 @@ require (
|
||||
google.golang.org/grpc v1.67.3 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.66.3 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/libc v1.66.10 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
@@ -73,8 +73,8 @@ github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.2 h1:688QHn2X/5nRezKe2ueIVCt+NRqf7fl3AVQk+vaFcIo=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.2/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.3 h1:Pyk/lwavPz7AaZNvugKFkdWOm93MzaIyWmBwmBo3aUI=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.3/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
|
||||
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
@@ -94,8 +94,8 @@ github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8
|
||||
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg=
|
||||
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
|
||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
|
||||
@@ -138,11 +138,11 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
|
||||
github.com/anchore/stereoscope v0.1.10 h1:BogafIMaW/L1lOUoVS96Hu1jTSP2JktxIayVqcxvcBI=
|
||||
github.com/anchore/stereoscope v0.1.10/go.mod h1:RWFAkQE8tp8yyaf4V83Kq1bO6hX3bzi8gpLCcKgZLIk=
|
||||
github.com/anchore/stereoscope v0.1.11 h1:YP/XUNcJyMbOOPAWPkeZNCVlKKTRO2cnBTEeUW6I40Y=
|
||||
github.com/anchore/stereoscope v0.1.11/go.mod h1:G3PZlzPbxFhylj9pQwtqfVPaahuWmy/UCtv5FTIIMvg=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ=
|
||||
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
@@ -217,8 +217,8 @@ github.com/bmatcuk/doublestar/v4 v4.9.1 h1:X8jg9rRZmJd4yRy7ZeNDRnM+T3ZfHv15JiBJ/
|
||||
github.com/bmatcuk/doublestar/v4 v4.9.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||
github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
|
||||
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
|
||||
github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4=
|
||||
github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8=
|
||||
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
|
||||
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
|
||||
@@ -235,8 +235,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
||||
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
||||
github.com/charmbracelet/bubbletea v1.3.9 h1:OBYdfRo6QnlIcXNmcoI2n1NNS65Nk6kI2L2FO1puS/4=
|
||||
github.com/charmbracelet/bubbletea v1.3.9/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
||||
@@ -322,12 +322,12 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||
github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY=
|
||||
github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v28.5.1+incompatible h1:ESutzBALAD6qyCLqbQSEf1a/U8Ybms5agw59yGVc+yY=
|
||||
github.com/docker/cli v28.5.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v28.4.0+incompatible h1:KVC7bz5zJY/4AZe/78BIvCnPsLaC9T/zh72xnlrTTOk=
|
||||
github.com/docker/docker v28.4.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM=
|
||||
github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
@@ -391,8 +391,8 @@ github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BN
|
||||
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
|
||||
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
|
||||
github.com/gkampitakis/go-snaps v0.5.14 h1:3fAqdB6BCPKHDMHAKRwtPUwYexKtGrNuw8HX/T/4neo=
|
||||
github.com/gkampitakis/go-snaps v0.5.14/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
|
||||
github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
|
||||
github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
|
||||
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
|
||||
github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
@@ -403,8 +403,8 @@ github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UN
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-git/go-git/v5 v5.16.3 h1:Z8BtvxZ09bYm/yYNgPKCzgWtaRqDTgIKRgIRHBfU6Z8=
|
||||
github.com/go-git/go-git/v5 v5.16.3/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -434,8 +434,8 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/gohugoio/hashstructure v0.5.0 h1:G2fjSBU36RdwEJBWJ+919ERvOVqAg9tfcYp47K9swqg=
|
||||
github.com/gohugoio/hashstructure v0.5.0/go.mod h1:Ser0TniXuu/eauYmrwM4o64EBvySxNzITEOLlm4igec=
|
||||
github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg=
|
||||
github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -556,8 +556,8 @@ github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtng
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-getter v1.8.0 h1:GMRdoMBDz12Mim366pWsRVIrrkugJ19rrmykkv0Nhzo=
|
||||
github.com/hashicorp/go-getter v1.8.0/go.mod h1:/K0O5zR6R72O3r2x3z2UHadiC0XHMbbzHO9pS8ZeJPA=
|
||||
github.com/hashicorp/go-getter v1.8.2 h1:CGCK+bZQLl44PYiwJweVzfpjg7bBwtuXu3AGcLiod2o=
|
||||
github.com/hashicorp/go-getter v1.8.2/go.mod h1:CUTt9x2bCtJ/sV8ihgrITL3IUE+0BE1j/e4n5P/GIM4=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.0.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
@@ -569,8 +569,6 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo=
|
||||
github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
@@ -594,8 +592,9 @@ github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKEN
|
||||
github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4=
|
||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk=
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
|
||||
github.com/iancoleman/orderedmap v0.3.0 h1:5cbR2grmZR/DiVt+VJopEhtVs9YGInGIxAoMJn+Ichc=
|
||||
github.com/iancoleman/orderedmap v0.3.0/go.mod h1:XuLcCUkdL5owUCQeF2Ue9uuw1EptkJDkXXS7VoV7XGE=
|
||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||
@@ -682,15 +681,15 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/mholt/archives v0.1.3 h1:aEAaOtNra78G+TvV5ohmXrJOAzf++dIlYeDW3N9q458=
|
||||
github.com/mholt/archives v0.1.3/go.mod h1:LUCGp++/IbV/I0Xq4SzcIR6uwgeh2yjnQWamjRQfLTU=
|
||||
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
||||
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
||||
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
|
||||
github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
|
||||
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
|
||||
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
@@ -744,8 +743,8 @@ github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1a
|
||||
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
|
||||
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
|
||||
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
|
||||
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=
|
||||
github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
|
||||
@@ -806,8 +805,8 @@ github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsT
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg=
|
||||
github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
@@ -852,8 +851,8 @@ github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yf
|
||||
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg=
|
||||
github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
|
||||
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
@@ -1021,8 +1020,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1062,8 +1061,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.28.0 h1:gQBtGhjxykdjY9YhZpSlZIsbnaE2+PgjfLWUQTnoZ1U=
|
||||
golang.org/x/mod v0.28.0/go.mod h1:yfB/L0NOf/kmEbXjzCPOx1iK1fRutOydrCMsqRhEBxI=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1108,8 +1107,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4=
|
||||
golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1218,13 +1217,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1235,8 +1234,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1297,8 +1296,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE=
|
||||
golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1490,18 +1489,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
modernc.org/cc/v4 v4.26.2 h1:991HMkLjJzYBIfha6ECZdjrIYz2/1ayr+FL8GN+CNzM=
|
||||
modernc.org/cc/v4 v4.26.2/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.8 h1:qtzNm7ED75pd1C7WgAGcK4edm4fvhtBsEiI/0NQ54YM=
|
||||
modernc.org/fileutil v1.3.8/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/cc/v4 v4.26.5 h1:xM3bX7Mve6G8K8b+T11ReenJOT+BmVqQj0FY5T4+5Y4=
|
||||
modernc.org/cc/v4 v4.26.5/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.1 h1:wPKYn5EC/mYTqBO373jKjvX2n+3+aK7+sICCv4Fjy1A=
|
||||
modernc.org/ccgo/v4 v4.28.1/go.mod h1:uD+4RnfrVgE6ec9NGguUNdhqzNIeeomeXf6CL0GTE5Q=
|
||||
modernc.org/fileutil v1.3.40 h1:ZGMswMNc9JOCrcrakF1HrvmergNLAmxOPjizirpfqBA=
|
||||
modernc.org/fileutil v1.3.40/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.66.3 h1:cfCbjTUcdsKyyZZfEUKfoHcP3S0Wkvz3jgSzByEWVCQ=
|
||||
modernc.org/libc v1.66.3/go.mod h1:XD9zO8kt59cANKvHPXpx7yS2ELPheAey0vjIuZOhOU8=
|
||||
modernc.org/libc v1.66.10 h1:yZkb3YeLx4oynyR+iUsXsybsX4Ubx7MQlSYEw4yj59A=
|
||||
modernc.org/libc v1.66.10/go.mod h1:8vGSEwvoUoltr4dlywvHqjtAqHBaw0j1jI7iFBTAr2I=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
@@ -1510,8 +1509,8 @@ modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.39.0 h1:6bwu9Ooim0yVYA7IZn9demiQk/Ejp0BtTjBWFLymSeY=
|
||||
modernc.org/sqlite v1.39.0/go.mod h1:cPTJYSlgg3Sfg046yBShXENNtPrWrDX8bsbAQBzgQ5E=
|
||||
modernc.org/sqlite v1.39.1 h1:H+/wGFzuSCIEVCvXYVHX5RQglwhMOvtHSv+VtidL2r4=
|
||||
modernc.org/sqlite v1.39.1/go.mod h1:9fjQZ0mB1LLP0GYrp39oOJXx/I2sxEnZtzCmEQIKvGE=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
Vendored
+1
-1
@@ -18,7 +18,7 @@ based on the _structure_ of the provided type.
|
||||
If the structure changes in any way it will end up with a new version key and all will re populate this new key,
|
||||
ignoring cached values from older, different versions.
|
||||
The resolver will store items using the `json` package to serialize/deserialize values, so to save space
|
||||
it is encouraged to use `omitempty`. For example:
|
||||
it is encouraged to use `omitempty`. For example:
|
||||
|
||||
```go
|
||||
type myCacheItem struct {
|
||||
|
||||
@@ -3,5 +3,5 @@ package internal
|
||||
const (
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "16.0.39"
|
||||
JSONSchemaVersion = "16.0.40"
|
||||
)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Please see [schema/json/README.md](../../schema/json/README.md) for more information on the JSON schema files in this directory.
|
||||
@@ -0,0 +1,159 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/invopop/jsonschema"
|
||||
)
|
||||
|
||||
func copyAliasFieldComments(commentMap map[string]string, repoRoot string) {
|
||||
// find all type aliases by parsing Go source files
|
||||
aliases := findTypeAliases(repoRoot)
|
||||
|
||||
// for each alias, copy field comments from the source type
|
||||
for aliasName, sourceName := range aliases {
|
||||
// find all field comments for the source type
|
||||
for key, comment := range commentMap {
|
||||
// check if this is a field comment for the source type
|
||||
// format: "github.com/anchore/syft/syft/pkg.SourceType.FieldName"
|
||||
if strings.Contains(key, "."+sourceName+".") {
|
||||
// create the corresponding key for the alias
|
||||
aliasKey := strings.Replace(key, "."+sourceName+".", "."+aliasName+".", 1)
|
||||
commentMap[aliasKey] = comment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func findTypeAliases(repoRoot string) map[string]string {
|
||||
aliases := make(map[string]string)
|
||||
fset := token.NewFileSet()
|
||||
|
||||
// walk through all Go files in the repo
|
||||
err := filepath.Walk(repoRoot, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() || !strings.HasSuffix(path, ".go") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// parse the file
|
||||
file, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// look for type alias declarations
|
||||
ast.Inspect(file, func(n ast.Node) bool {
|
||||
typeSpec, ok := n.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// check if this is a type alias (e.g., type A B where B is an identifier)
|
||||
ident, ok := typeSpec.Type.(*ast.Ident)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
// store the alias mapping: aliasName -> sourceName
|
||||
aliases[typeSpec.Name.Name] = ident.Name
|
||||
return true
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error: failed to find type aliases: %v\n", err)
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return aliases
|
||||
}
|
||||
|
||||
func hasDescriptionInAlternatives(schema *jsonschema.Schema) bool {
|
||||
// check oneOf alternatives
|
||||
for _, alt := range schema.OneOf {
|
||||
if alt.Description != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
// check anyOf alternatives
|
||||
for _, alt := range schema.AnyOf {
|
||||
if alt.Description != "" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func warnMissingDescriptions(schema *jsonschema.Schema, metadataNames []string) { //nolint:gocognit
|
||||
var missingTypeDescriptions []string
|
||||
var missingFieldDescriptions []string
|
||||
|
||||
// check metadata types for missing descriptions
|
||||
for _, name := range metadataNames {
|
||||
def, ok := schema.Definitions[name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// check if type has a description
|
||||
if def.Description == "" {
|
||||
missingTypeDescriptions = append(missingTypeDescriptions, name)
|
||||
}
|
||||
|
||||
// check if fields have descriptions
|
||||
if def.Properties != nil {
|
||||
for _, fieldName := range def.Properties.Keys() {
|
||||
fieldSchemaRaw, _ := def.Properties.Get(fieldName)
|
||||
fieldSchema, ok := fieldSchemaRaw.(*jsonschema.Schema)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip if field has a description
|
||||
if fieldSchema.Description != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip if field is a reference (descriptions come from the referenced type)
|
||||
if fieldSchema.Ref != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip if field is an array/object with items that are references
|
||||
if fieldSchema.Items != nil && fieldSchema.Items.Ref != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip if field uses oneOf/anyOf with descriptions in the alternatives
|
||||
if hasDescriptionInAlternatives(fieldSchema) {
|
||||
continue
|
||||
}
|
||||
|
||||
missingFieldDescriptions = append(missingFieldDescriptions, fmt.Sprintf("%s.%s", name, fieldName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// report findings
|
||||
if len(missingTypeDescriptions) > 0 {
|
||||
fmt.Fprintf(os.Stderr, "\nwarning: %d metadata types are missing descriptions:\n", len(missingTypeDescriptions))
|
||||
for _, name := range missingTypeDescriptions {
|
||||
fmt.Fprintf(os.Stderr, " - %s\n", name)
|
||||
}
|
||||
}
|
||||
|
||||
if len(missingFieldDescriptions) > 0 {
|
||||
fmt.Fprintf(os.Stderr, "\nwarning: %d fields are missing descriptions:\n", len(missingFieldDescriptions))
|
||||
for _, field := range missingFieldDescriptions {
|
||||
fmt.Fprintf(os.Stderr, " - %s\n", field)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/iancoleman/orderedmap"
|
||||
"github.com/invopop/jsonschema"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// TestCopyAliasFieldComments verifies that field comments from source types are correctly copied to alias types.
|
||||
// This is important for type aliases like `type RpmArchive RpmDBEntry` where the alias should inherit all field descriptions.
|
||||
func TestCopyAliasFieldComments(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
commentMap map[string]string
|
||||
aliases map[string]string
|
||||
wantComments map[string]string
|
||||
}{
|
||||
{
|
||||
name: "copies field comments from source type to alias",
|
||||
commentMap: map[string]string{
|
||||
"github.com/anchore/syft/syft/pkg.RpmDBEntry": "RpmDBEntry represents all captured data from a RPM DB package entry.",
|
||||
"github.com/anchore/syft/syft/pkg.RpmDBEntry.Name": "Name is the RPM package name.",
|
||||
"github.com/anchore/syft/syft/pkg.RpmDBEntry.Epoch": "Epoch is the version epoch.",
|
||||
},
|
||||
aliases: map[string]string{
|
||||
"RpmArchive": "RpmDBEntry",
|
||||
},
|
||||
wantComments: map[string]string{
|
||||
"github.com/anchore/syft/syft/pkg.RpmDBEntry": "RpmDBEntry represents all captured data from a RPM DB package entry.",
|
||||
"github.com/anchore/syft/syft/pkg.RpmDBEntry.Name": "Name is the RPM package name.",
|
||||
"github.com/anchore/syft/syft/pkg.RpmDBEntry.Epoch": "Epoch is the version epoch.",
|
||||
"github.com/anchore/syft/syft/pkg.RpmArchive.Name": "Name is the RPM package name.",
|
||||
"github.com/anchore/syft/syft/pkg.RpmArchive.Epoch": "Epoch is the version epoch.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "handles multiple aliases",
|
||||
commentMap: map[string]string{
|
||||
"github.com/anchore/syft/syft/pkg.DpkgDBEntry": "DpkgDBEntry represents data from dpkg.",
|
||||
"github.com/anchore/syft/syft/pkg.DpkgDBEntry.Package": "Package is the package name.",
|
||||
"github.com/anchore/syft/syft/pkg.DpkgDBEntry.Architecture": "Architecture is the target arch.",
|
||||
},
|
||||
aliases: map[string]string{
|
||||
"DpkgArchiveEntry": "DpkgDBEntry",
|
||||
"DpkgSnapshot": "DpkgDBEntry",
|
||||
},
|
||||
wantComments: map[string]string{
|
||||
"github.com/anchore/syft/syft/pkg.DpkgDBEntry": "DpkgDBEntry represents data from dpkg.",
|
||||
"github.com/anchore/syft/syft/pkg.DpkgDBEntry.Package": "Package is the package name.",
|
||||
"github.com/anchore/syft/syft/pkg.DpkgDBEntry.Architecture": "Architecture is the target arch.",
|
||||
"github.com/anchore/syft/syft/pkg.DpkgArchiveEntry.Package": "Package is the package name.",
|
||||
"github.com/anchore/syft/syft/pkg.DpkgArchiveEntry.Architecture": "Architecture is the target arch.",
|
||||
"github.com/anchore/syft/syft/pkg.DpkgSnapshot.Package": "Package is the package name.",
|
||||
"github.com/anchore/syft/syft/pkg.DpkgSnapshot.Architecture": "Architecture is the target arch.",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "does not copy non-field comments",
|
||||
commentMap: map[string]string{
|
||||
"github.com/anchore/syft/syft/pkg.SomeType": "SomeType struct comment.",
|
||||
"github.com/anchore/syft/syft/pkg.SomeType.Field": "Field comment.",
|
||||
},
|
||||
aliases: map[string]string{
|
||||
"AliasType": "SomeType",
|
||||
},
|
||||
wantComments: map[string]string{
|
||||
"github.com/anchore/syft/syft/pkg.SomeType": "SomeType struct comment.",
|
||||
"github.com/anchore/syft/syft/pkg.SomeType.Field": "Field comment.",
|
||||
"github.com/anchore/syft/syft/pkg.AliasType.Field": "Field comment.",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// create temp dir for testing
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// create a test go file with type aliases
|
||||
testFile := filepath.Join(tmpDir, "test.go")
|
||||
content := "package test\n\n"
|
||||
for alias, source := range tt.aliases {
|
||||
content += "type " + alias + " " + source + "\n"
|
||||
}
|
||||
err := os.WriteFile(testFile, []byte(content), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
// make a copy of the comment map since the function modifies it
|
||||
commentMap := make(map[string]string)
|
||||
for k, v := range tt.commentMap {
|
||||
commentMap[k] = v
|
||||
}
|
||||
|
||||
// run the function
|
||||
copyAliasFieldComments(commentMap, tmpDir)
|
||||
|
||||
// verify results
|
||||
assert.Equal(t, tt.wantComments, commentMap)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindTypeAliases(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fileContent string
|
||||
wantAliases map[string]string
|
||||
}{
|
||||
{
|
||||
name: "finds simple type alias",
|
||||
fileContent: `package test
|
||||
|
||||
type RpmArchive RpmDBEntry
|
||||
type DpkgArchiveEntry DpkgDBEntry
|
||||
`,
|
||||
wantAliases: map[string]string{
|
||||
"RpmArchive": "RpmDBEntry",
|
||||
"DpkgArchiveEntry": "DpkgDBEntry",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignores struct definitions",
|
||||
fileContent: `package test
|
||||
|
||||
type MyStruct struct {
|
||||
Field string
|
||||
}
|
||||
|
||||
type AliasType BaseType
|
||||
`,
|
||||
wantAliases: map[string]string{
|
||||
"AliasType": "BaseType",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignores interface definitions",
|
||||
fileContent: `package test
|
||||
|
||||
type MyInterface interface {
|
||||
Method()
|
||||
}
|
||||
|
||||
type AliasType BaseType
|
||||
`,
|
||||
wantAliases: map[string]string{
|
||||
"AliasType": "BaseType",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "handles multiple files",
|
||||
fileContent: `package test
|
||||
|
||||
type Alias1 Base1
|
||||
type Alias2 Base2
|
||||
`,
|
||||
wantAliases: map[string]string{
|
||||
"Alias1": "Base1",
|
||||
"Alias2": "Base2",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// create temp dir
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
// write test file
|
||||
testFile := filepath.Join(tmpDir, "test.go")
|
||||
err := os.WriteFile(testFile, []byte(tt.fileContent), 0644)
|
||||
require.NoError(t, err)
|
||||
|
||||
// run function
|
||||
aliases := findTypeAliases(tmpDir)
|
||||
|
||||
// verify
|
||||
assert.Equal(t, tt.wantAliases, aliases)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasDescriptionInAlternatives(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
schema *jsonschema.Schema
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "returns true when oneOf has description",
|
||||
schema: &jsonschema.Schema{
|
||||
OneOf: []*jsonschema.Schema{
|
||||
{Description: "First alternative"},
|
||||
{Type: "null"},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "returns true when anyOf has description",
|
||||
schema: &jsonschema.Schema{
|
||||
AnyOf: []*jsonschema.Schema{
|
||||
{Description: "First alternative"},
|
||||
{Type: "null"},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "returns false when no alternatives have descriptions",
|
||||
schema: &jsonschema.Schema{
|
||||
OneOf: []*jsonschema.Schema{
|
||||
{Type: "integer"},
|
||||
{Type: "null"},
|
||||
},
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "returns false when no oneOf or anyOf",
|
||||
schema: &jsonschema.Schema{
|
||||
Type: "string",
|
||||
},
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "returns true when any alternative in oneOf has description",
|
||||
schema: &jsonschema.Schema{
|
||||
OneOf: []*jsonschema.Schema{
|
||||
{Type: "integer"},
|
||||
{Type: "string", Description: "Second alternative"},
|
||||
{Type: "null"},
|
||||
},
|
||||
},
|
||||
want: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := hasDescriptionInAlternatives(tt.schema)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWarnMissingDescriptions(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
schema *jsonschema.Schema
|
||||
metadataNames []string
|
||||
wantTypeWarnings int
|
||||
wantFieldWarnings int
|
||||
}{
|
||||
{
|
||||
name: "no warnings when all types have descriptions",
|
||||
schema: &jsonschema.Schema{
|
||||
Definitions: map[string]*jsonschema.Schema{
|
||||
"TypeA": {
|
||||
Description: "Type A description",
|
||||
Properties: newOrderedMap(map[string]*jsonschema.Schema{
|
||||
"field1": {Type: "string", Description: "Field 1"},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
metadataNames: []string{"TypeA"},
|
||||
wantTypeWarnings: 0,
|
||||
wantFieldWarnings: 0,
|
||||
},
|
||||
{
|
||||
name: "warns about missing type description",
|
||||
schema: &jsonschema.Schema{
|
||||
Definitions: map[string]*jsonschema.Schema{
|
||||
"TypeA": {
|
||||
Properties: newOrderedMap(map[string]*jsonschema.Schema{
|
||||
"field1": {Type: "string", Description: "Field 1"},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
metadataNames: []string{"TypeA"},
|
||||
wantTypeWarnings: 1,
|
||||
wantFieldWarnings: 0,
|
||||
},
|
||||
{
|
||||
name: "warns about missing field description",
|
||||
schema: &jsonschema.Schema{
|
||||
Definitions: map[string]*jsonschema.Schema{
|
||||
"TypeA": {
|
||||
Description: "Type A description",
|
||||
Properties: newOrderedMap(map[string]*jsonschema.Schema{
|
||||
"field1": {Type: "string"},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
metadataNames: []string{"TypeA"},
|
||||
wantTypeWarnings: 0,
|
||||
wantFieldWarnings: 1,
|
||||
},
|
||||
{
|
||||
name: "skips fields with references",
|
||||
schema: &jsonschema.Schema{
|
||||
Definitions: map[string]*jsonschema.Schema{
|
||||
"TypeA": {
|
||||
Description: "Type A description",
|
||||
Properties: newOrderedMap(map[string]*jsonschema.Schema{
|
||||
"field1": {Ref: "#/$defs/OtherType"},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
metadataNames: []string{"TypeA"},
|
||||
wantTypeWarnings: 0,
|
||||
wantFieldWarnings: 0,
|
||||
},
|
||||
{
|
||||
name: "skips fields with items that are references",
|
||||
schema: &jsonschema.Schema{
|
||||
Definitions: map[string]*jsonschema.Schema{
|
||||
"TypeA": {
|
||||
Description: "Type A description",
|
||||
Properties: newOrderedMap(map[string]*jsonschema.Schema{
|
||||
"field1": {
|
||||
Type: "array",
|
||||
Items: &jsonschema.Schema{Ref: "#/$defs/OtherType"},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
metadataNames: []string{"TypeA"},
|
||||
wantTypeWarnings: 0,
|
||||
wantFieldWarnings: 0,
|
||||
},
|
||||
{
|
||||
name: "skips fields with oneOf containing descriptions",
|
||||
schema: &jsonschema.Schema{
|
||||
Definitions: map[string]*jsonschema.Schema{
|
||||
"TypeA": {
|
||||
Description: "Type A description",
|
||||
Properties: newOrderedMap(map[string]*jsonschema.Schema{
|
||||
"field1": {
|
||||
OneOf: []*jsonschema.Schema{
|
||||
{Type: "integer", Description: "Integer value"},
|
||||
{Type: "null"},
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
metadataNames: []string{"TypeA"},
|
||||
wantTypeWarnings: 0,
|
||||
wantFieldWarnings: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// capture stderr output would require more complex testing
|
||||
// for now, just verify the function runs without panicking
|
||||
require.NotPanics(t, func() {
|
||||
warnMissingDescriptions(tt.schema, tt.metadataNames)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// helper to create an ordered map from a regular map
|
||||
func newOrderedMap(m map[string]*jsonschema.Schema) *orderedmap.OrderedMap {
|
||||
om := orderedmap.New()
|
||||
for k, v := range m {
|
||||
om.Set(k, v)
|
||||
}
|
||||
return om
|
||||
}
|
||||
@@ -15,8 +15,8 @@ import (
|
||||
"github.com/invopop/jsonschema"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/packagemetadata"
|
||||
syftJsonModel "github.com/anchore/syft/syft/format/syftjson/model"
|
||||
"github.com/anchore/syft/syft/internal/packagemetadata"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -26,6 +26,17 @@ are not captured (empty interfaces). This means that pkg.Package.Metadata is not
|
||||
can be extended to include specific package metadata struct shapes in the future.
|
||||
*/
|
||||
|
||||
var repoRoot string
|
||||
|
||||
func init() {
|
||||
var err error
|
||||
repoRoot, err = packagemetadata.RepoRoot()
|
||||
if err != nil {
|
||||
fmt.Println("unable to determine repo root")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
write(encode(build()))
|
||||
}
|
||||
@@ -60,7 +71,7 @@ func assembleTypeContainer(items []any) (any, map[string]string) {
|
||||
}
|
||||
|
||||
if len(typesMissingNames) > 0 {
|
||||
fmt.Println("the following types are missing JSON names (manually curated in ./syft/internal/packagemetadata/names.go):")
|
||||
fmt.Println("the following types are missing JSON names (manually curated in ./internal/packagemetadata/names.go):")
|
||||
for _, t := range typesMissingNames {
|
||||
fmt.Println(" - ", t.Name())
|
||||
}
|
||||
@@ -78,6 +89,38 @@ func build() *jsonschema.Schema {
|
||||
Namer: func(r reflect.Type) string {
|
||||
return strings.TrimPrefix(r.Name(), "JSON")
|
||||
},
|
||||
CommentMap: make(map[string]string),
|
||||
}
|
||||
|
||||
// extract comments from Go source files to enrich schema descriptions
|
||||
//
|
||||
// note: AddGoComments parses from the module root and creates keys like "syft/pkg.TypeName",
|
||||
// but the reflector expects fully qualified paths like "github.com/anchore/syft/syft/pkg.TypeName".
|
||||
// We fix up the keys after extraction to match the expected format.
|
||||
if err := reflector.AddGoComments("github.com/anchore/syft", repoRoot); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: failed to extract Go comments: %v\n", err)
|
||||
} else {
|
||||
// fix up comment map keys to use fully qualified import paths
|
||||
// note: AddGoComments includes the absolute repo path WITHOUT the leading slash
|
||||
repoRootNoSlash := strings.TrimPrefix(repoRoot, "/")
|
||||
fixedMap := make(map[string]string)
|
||||
for k, v := range reflector.CommentMap {
|
||||
newKey := k
|
||||
if !strings.HasPrefix(k, "github.com/") {
|
||||
// key doesn't have module prefix, add it
|
||||
newKey = "github.com/anchore/syft/" + k
|
||||
} else if strings.Contains(k, repoRootNoSlash) {
|
||||
// key has the absolute repo path embedded, strip it
|
||||
// format: github.com/anchore/syft/Users/wagoodman/code/syft-manual/syft/pkg.Type
|
||||
// should be: github.com/anchore/syft/syft/pkg.Type
|
||||
newKey = strings.Replace(k, repoRootNoSlash+"/", "", 1)
|
||||
}
|
||||
fixedMap[newKey] = v
|
||||
}
|
||||
reflector.CommentMap = fixedMap
|
||||
|
||||
// copy field comments for type aliases (e.g., type RpmArchive RpmDBEntry)
|
||||
copyAliasFieldComments(reflector.CommentMap, repoRoot)
|
||||
}
|
||||
|
||||
pkgMetadataContainer, pkgMetadataMapping := assembleTypeContainer(packagemetadata.AllTypes())
|
||||
@@ -130,6 +173,9 @@ func build() *jsonschema.Schema {
|
||||
"anyOf": metadataTypes,
|
||||
})
|
||||
|
||||
// warn about missing descriptions
|
||||
warnMissingDescriptions(documentSchema, metadataNames)
|
||||
|
||||
return documentSchema
|
||||
}
|
||||
|
||||
@@ -148,11 +194,6 @@ func encode(schema *jsonschema.Schema) []byte {
|
||||
}
|
||||
|
||||
func write(schema []byte) {
|
||||
repoRoot, err := packagemetadata.RepoRoot()
|
||||
if err != nil {
|
||||
fmt.Println("unable to determine repo root")
|
||||
os.Exit(1)
|
||||
}
|
||||
schemaPath := filepath.Join(repoRoot, "schema", "json", fmt.Sprintf("schema-%s.json", internal.JSONSchemaVersion))
|
||||
latestSchemaPath := filepath.Join(repoRoot, "schema", "json", "schema-latest.json")
|
||||
|
||||
+2
-2
@@ -24,7 +24,7 @@ var knownNonMetadataTypeNames = strset.New(
|
||||
|
||||
// these are names that would be removed due to common convention (e.g. used within another metadata type) but are
|
||||
// known to be metadata types themselves. Adding to this list will prevent the removal of the type from the schema.
|
||||
var knownMetadaTypeNames = strset.New(
|
||||
var knownMetadataTypeNames = strset.New(
|
||||
"DotnetPortableExecutableEntry",
|
||||
)
|
||||
|
||||
@@ -72,7 +72,7 @@ func findMetadataDefinitionNames(paths ...string) ([]string, error) {
|
||||
}
|
||||
|
||||
// any definition that is used within another struct should not be considered a top-level metadata definition
|
||||
removeNames := strset.Difference(usedNames, knownMetadaTypeNames)
|
||||
removeNames := strset.Difference(usedNames, knownMetadataTypeNames)
|
||||
names.Remove(removeNames.List()...)
|
||||
|
||||
// remove known exceptions, that is, types exported in the pkg Package that are not used
|
||||
+2
-2
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/dave/jennifer/jen"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/internal/packagemetadata"
|
||||
)
|
||||
|
||||
// This program is invoked from syft/internal and generates packagemetadata/generated.go
|
||||
@@ -31,7 +31,7 @@ func main() {
|
||||
fmt.Printf("updating package metadata type list with %+v types\n", len(typeNames))
|
||||
|
||||
f := jen.NewFile("packagemetadata")
|
||||
f.HeaderComment("DO NOT EDIT: generated by syft/internal/packagemetadata/generate/main.go")
|
||||
f.HeaderComment("DO NOT EDIT: generated by internal/packagemetadata/generate/main.go")
|
||||
f.ImportName(pkgImport, "pkg")
|
||||
f.Comment("AllTypes returns a list of all pkg metadata types that syft supports (that are represented in the pkg.Package.Metadata field).")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// DO NOT EDIT: generated by syft/internal/packagemetadata/generate/main.go
|
||||
// DO NOT EDIT: generated by internal/packagemetadata/generate/main.go
|
||||
|
||||
package packagemetadata
|
||||
|
||||
@@ -61,6 +61,7 @@ func AllTypes() []any {
|
||||
pkg.RubyGemspec{},
|
||||
pkg.RustBinaryAuditEntry{},
|
||||
pkg.RustCargoLockEntry{},
|
||||
pkg.SnapEntry{},
|
||||
pkg.SwiftPackageManagerResolvedEntry{},
|
||||
pkg.SwiplPackEntry{},
|
||||
pkg.TerraformLockProviderEntry{},
|
||||
@@ -115,6 +115,7 @@ var jsonTypes = makeJSONTypes(
|
||||
jsonNames(pkg.OpamPackage{}, "opam-package"),
|
||||
jsonNames(pkg.RustCargoLockEntry{}, "rust-cargo-lock-entry", "RustCargoPackageMetadata"),
|
||||
jsonNamesWithoutLookup(pkg.RustBinaryAuditEntry{}, "rust-cargo-audit-entry", "RustCargoPackageMetadata"), // the legacy value is split into two types, where the other is preferred
|
||||
jsonNames(pkg.SnapEntry{}, "snap-entry"),
|
||||
jsonNames(pkg.WordpressPluginEntry{}, "wordpress-plugin-entry", "WordpressMetadata"),
|
||||
jsonNames(pkg.HomebrewFormula{}, "homebrew-formula"),
|
||||
jsonNames(pkg.LuaRocksPackage{}, "luarocks-package"),
|
||||
+2
-2
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/dave/jennifer/jen"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
)
|
||||
|
||||
// This program is invoked from syft/internal and generates sourcemetadata/generated.go
|
||||
@@ -25,7 +25,7 @@ func main() {
|
||||
fmt.Printf("updating source metadata type list with %+v types\n", len(typeNames))
|
||||
|
||||
f := jen.NewFile("sourcemetadata")
|
||||
f.HeaderComment("DO NOT EDIT: generated by syft/internal/sourcemetadata/generate/main.go")
|
||||
f.HeaderComment("DO NOT EDIT: generated by internal/sourcemetadata/generate/main.go")
|
||||
f.ImportName(srcImport, "source")
|
||||
f.Comment("AllTypes returns a list of all source metadata types that syft supports (that are represented in the source.Description.Metadata field).")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// DO NOT EDIT: generated by syft/internal/sourcemetadata/generate/main.go
|
||||
// DO NOT EDIT: generated by internal/sourcemetadata/generate/main.go
|
||||
|
||||
package sourcemetadata
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/ruby"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/rust"
|
||||
sbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/sbom"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/snap"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/swift"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/swipl"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/terraform"
|
||||
@@ -173,6 +174,7 @@ func DefaultPackageTaskFactories() Factories {
|
||||
newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"),
|
||||
newSimplePackageTaskFactory(homebrew.NewCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "homebrew"),
|
||||
newSimplePackageTaskFactory(conda.NewCondaMetaCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.PackageTag, "conda"),
|
||||
newSimplePackageTaskFactory(snap.NewCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "snap"),
|
||||
|
||||
// deprecated catalogers ////////////////////////////////////////
|
||||
// these are catalogers that should not be selectable other than specific inclusion via name or "deprecated" tag (to remain backwards compatible)
|
||||
|
||||
@@ -13,6 +13,6 @@ prior spec version (e.g. if updating to 1.7, compare the files in this directory
|
||||
equivalents).
|
||||
|
||||
One can also update the schemas and observe the errors in order to make the necessary updates.
|
||||
At the time of writing, the cyclonedx.xsd needed modifications to link to the local spdx.xsd,
|
||||
and also to changes the minOccurs for a license tag to 0. (The json schema does not require
|
||||
At the time of writing, the cyclonedx.xsd needed modifications to link to the local spdx.xsd,
|
||||
and also to change the minOccurs for a license tag to 0. (The json schema does not require
|
||||
modification for the generated file to lint properly, but can simply be copy/pasted).
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+1233
-599
File diff suppressed because it is too large
Load Diff
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/scylladb/go-set/strset"
|
||||
)
|
||||
|
||||
// CoordinateSet provides a unique collection of Coordinates with set operations.
|
||||
type CoordinateSet struct {
|
||||
set map[Coordinates]struct{}
|
||||
}
|
||||
|
||||
@@ -9,8 +9,11 @@ import (
|
||||
|
||||
// Coordinates contains the minimal information needed to describe how to find a file within any possible source object (e.g. image and directory sources)
|
||||
type Coordinates struct {
|
||||
RealPath string `json:"path" cyclonedx:"path"` // The path where all path ancestors have no hardlinks / symlinks
|
||||
FileSystemID string `json:"layerID,omitempty" cyclonedx:"layerID"` // An ID representing the filesystem. For container images, this is a layer digest. For directories or a root filesystem, this is blank.
|
||||
// RealPath is the canonical absolute form of the path accessed (all symbolic links have been followed and relative path components like '.' and '..' have been removed).
|
||||
RealPath string `json:"path" cyclonedx:"path"`
|
||||
|
||||
// FileSystemID is an ID representing and entire filesystem. For container images, this is a layer digest. For directories or a root filesystem, this is blank.
|
||||
FileSystemID string `json:"layerID,omitempty" cyclonedx:"layerID"`
|
||||
}
|
||||
|
||||
func NewCoordinates(realPath, fsID string) Coordinates {
|
||||
|
||||
+5
-1
@@ -1,6 +1,10 @@
|
||||
package file
|
||||
|
||||
// Digest represents a cryptographic hash of file contents.
|
||||
type Digest struct {
|
||||
// Algorithm specifies the hash algorithm used (e.g., "sha256", "md5").
|
||||
Algorithm string `json:"algorithm"`
|
||||
Value string `json:"value"`
|
||||
|
||||
// Value is the hexadecimal string representation of the hash.
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
+36
-16
@@ -1,40 +1,60 @@
|
||||
package file
|
||||
|
||||
type (
|
||||
ExecutableFormat string
|
||||
// ExecutableFormat represents the binary executable format type.
|
||||
ExecutableFormat string
|
||||
|
||||
// RelocationReadOnly indicates the RELRO security protection level applied to an ELF binary.
|
||||
RelocationReadOnly string
|
||||
)
|
||||
|
||||
const (
|
||||
ELF ExecutableFormat = "elf"
|
||||
MachO ExecutableFormat = "macho"
|
||||
PE ExecutableFormat = "pe"
|
||||
ELF ExecutableFormat = "elf" // Executable and Linkable Format used on Unix-like systems
|
||||
MachO ExecutableFormat = "macho" // Mach object file format used on macOS and iOS
|
||||
PE ExecutableFormat = "pe" // Portable Executable format used on Windows
|
||||
|
||||
RelocationReadOnlyNone RelocationReadOnly = "none"
|
||||
RelocationReadOnlyPartial RelocationReadOnly = "partial"
|
||||
RelocationReadOnlyFull RelocationReadOnly = "full"
|
||||
RelocationReadOnlyNone RelocationReadOnly = "none" // no RELRO protection
|
||||
RelocationReadOnlyPartial RelocationReadOnly = "partial" // partial RELRO protection
|
||||
RelocationReadOnlyFull RelocationReadOnly = "full" // full RELRO protection
|
||||
)
|
||||
|
||||
// Executable contains metadata about binary files and their security features.
|
||||
type Executable struct {
|
||||
// Format denotes either ELF, Mach-O, or PE
|
||||
Format ExecutableFormat `json:"format" yaml:"format" mapstructure:"format"`
|
||||
|
||||
HasExports bool `json:"hasExports" yaml:"hasExports" mapstructure:"hasExports"`
|
||||
HasEntrypoint bool `json:"hasEntrypoint" yaml:"hasEntrypoint" mapstructure:"hasEntrypoint"`
|
||||
ImportedLibraries []string `json:"importedLibraries" yaml:"importedLibraries" mapstructure:"importedLibraries"`
|
||||
// HasExports indicates whether the binary exports symbols.
|
||||
HasExports bool `json:"hasExports" yaml:"hasExports" mapstructure:"hasExports"`
|
||||
|
||||
// HasEntrypoint indicates whether the binary has an entry point function.
|
||||
HasEntrypoint bool `json:"hasEntrypoint" yaml:"hasEntrypoint" mapstructure:"hasEntrypoint"`
|
||||
|
||||
// ImportedLibraries lists the shared libraries required by this executable.
|
||||
ImportedLibraries []string `json:"importedLibraries" yaml:"importedLibraries" mapstructure:"importedLibraries"`
|
||||
|
||||
// ELFSecurityFeatures contains ELF-specific security hardening information when Format is ELF.
|
||||
ELFSecurityFeatures *ELFSecurityFeatures `json:"elfSecurityFeatures,omitempty" yaml:"elfSecurityFeatures" mapstructure:"elfSecurityFeatures"`
|
||||
}
|
||||
|
||||
// ELFSecurityFeatures captures security hardening and protection mechanisms in ELF binaries.
|
||||
type ELFSecurityFeatures struct {
|
||||
// SymbolTableStripped indicates whether debugging symbols have been removed.
|
||||
SymbolTableStripped bool `json:"symbolTableStripped" yaml:"symbolTableStripped" mapstructure:"symbolTableStripped"`
|
||||
|
||||
// classic protections
|
||||
// StackCanary indicates whether stack smashing protection is enabled.
|
||||
StackCanary *bool `json:"stackCanary,omitempty" yaml:"stackCanary" mapstructure:"stackCanary"`
|
||||
|
||||
StackCanary *bool `json:"stackCanary,omitempty" yaml:"stackCanary" mapstructure:"stackCanary"`
|
||||
NoExecutable bool `json:"nx" yaml:"nx" mapstructure:"nx"`
|
||||
RelocationReadOnly RelocationReadOnly `json:"relRO" yaml:"relRO" mapstructure:"relRO"`
|
||||
PositionIndependentExecutable bool `json:"pie" yaml:"pie" mapstructure:"pie"`
|
||||
DynamicSharedObject bool `json:"dso" yaml:"dso" mapstructure:"dso"`
|
||||
// NoExecutable indicates whether NX (no-execute) protection is enabled for the stack.
|
||||
NoExecutable bool `json:"nx" yaml:"nx" mapstructure:"nx"`
|
||||
|
||||
// RelocationReadOnly indicates the RELRO protection level.
|
||||
RelocationReadOnly RelocationReadOnly `json:"relRO" yaml:"relRO" mapstructure:"relRO"`
|
||||
|
||||
// PositionIndependentExecutable indicates whether the binary is compiled as PIE.
|
||||
PositionIndependentExecutable bool `json:"pie" yaml:"pie" mapstructure:"pie"`
|
||||
|
||||
// DynamicSharedObject indicates whether the binary is a shared library.
|
||||
DynamicSharedObject bool `json:"dso" yaml:"dso" mapstructure:"dso"`
|
||||
|
||||
// LlvmSafeStack represents a compiler-based security mechanism that separates the stack into a safe stack for storing return addresses and other critical data, and an unsafe stack for everything else, to mitigate stack-based memory corruption errors
|
||||
// see https://clang.llvm.org/docs/SafeStack.html
|
||||
|
||||
+22
-7
@@ -5,18 +5,33 @@ import (
|
||||
"github.com/anchore/syft/syft/license"
|
||||
)
|
||||
|
||||
// License represents license information discovered within a file.
|
||||
type License struct {
|
||||
Value string
|
||||
SPDXExpression string
|
||||
Type license.Type
|
||||
LicenseEvidence *LicenseEvidence // evidence from license classifier
|
||||
Contents string `hash:"ignore"`
|
||||
// Value is the raw license string as found in the file.
|
||||
Value string
|
||||
|
||||
// SPDXExpression is the parsed SPDX license expression if available.
|
||||
SPDXExpression string
|
||||
|
||||
// Type categorizes how the license was determined (e.g., declared, concluded -- following the same semantics as SPDX).
|
||||
Type license.Type
|
||||
|
||||
LicenseEvidence *LicenseEvidence
|
||||
|
||||
// Contents optionally stores the full license text.
|
||||
Contents string `hash:"ignore"`
|
||||
}
|
||||
|
||||
// LicenseEvidence contains details from license classifier analysis.
|
||||
type LicenseEvidence struct {
|
||||
// Confidence is a score indicating certainty of the license match.
|
||||
Confidence int
|
||||
Offset int
|
||||
Extent int
|
||||
|
||||
// Offset is the byte position where the license text begins in the file.
|
||||
Offset int
|
||||
|
||||
// Extent is the length in bytes of the matched license text.
|
||||
Extent int
|
||||
}
|
||||
|
||||
func NewLicense(value string) License {
|
||||
|
||||
@@ -27,18 +27,24 @@ type Location struct {
|
||||
LocationMetadata `cyclonedx:""`
|
||||
}
|
||||
|
||||
// LocationData contains the core identifying information for a file location.
|
||||
type LocationData struct {
|
||||
Coordinates `cyclonedx:""` // Empty string here means there is no intermediate property name, e.g. syft:locations:0:path without "coordinates"
|
||||
// note: it is IMPORTANT to ignore anything but the coordinates for a Location when considering the ID (hash value)
|
||||
// since the coordinates are the minimally correct ID for a location (symlinks should not come into play)
|
||||
AccessPath string `hash:"ignore" json:"accessPath"` // The path to the file which may or may not have hardlinks / symlinks
|
||||
ref file.Reference `hash:"ignore"` // The file reference relative to the stereoscope.FileCatalog that has more information about this location.
|
||||
|
||||
// AccessPath is the path used to retrieve file contents (which may or may not have hardlinks / symlinks in the path)
|
||||
AccessPath string `hash:"ignore" json:"accessPath"`
|
||||
|
||||
// ref is the stereoscope file reference relative to the stereoscope.FileCatalog that has more information about this location.
|
||||
ref file.Reference `hash:"ignore"`
|
||||
}
|
||||
|
||||
func (l LocationData) Reference() file.Reference {
|
||||
return l.ref
|
||||
}
|
||||
|
||||
// LocationMetadata provides additional contextual information about a file location.
|
||||
type LocationMetadata struct {
|
||||
Annotations map[string]string `json:"annotations,omitempty"` // Arbitrary key-value pairs that can be used to annotate a location
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package file
|
||||
|
||||
import "io"
|
||||
|
||||
// LocationReadCloser combines a Location with a ReadCloser for accessing file content with location metadata.
|
||||
type LocationReadCloser struct {
|
||||
Location
|
||||
io.ReadCloser
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
// LocationSet provides a unique collection of Locations with metadata and set operations.
|
||||
type LocationSet struct {
|
||||
set map[LocationData]LocationMetadata
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
var locationSorterWithoutLayers = LocationSorter(nil)
|
||||
|
||||
// Locations is a sortable slice of Location values.
|
||||
type Locations []Location
|
||||
|
||||
func (l Locations) Len() int {
|
||||
|
||||
@@ -18,6 +18,7 @@ type ContentResolver interface {
|
||||
FileContentsByLocation(Location) (io.ReadCloser, error)
|
||||
}
|
||||
|
||||
// MetadataResolver provides file metadata lookup by location.
|
||||
type MetadataResolver interface {
|
||||
FileMetadataByLocation(Location) (Metadata, error)
|
||||
}
|
||||
@@ -51,6 +52,7 @@ type PathResolver interface {
|
||||
RelativeFileByPath(_ Location, path string) *Location
|
||||
}
|
||||
|
||||
// LocationResolver provides iteration over all file locations in a source.
|
||||
type LocationResolver interface {
|
||||
// AllLocations returns a channel of all file references from the underlying source.
|
||||
// The implementation for this may vary, however, generally the following considerations should be made:
|
||||
@@ -59,6 +61,7 @@ type LocationResolver interface {
|
||||
AllLocations(ctx context.Context) <-chan Location
|
||||
}
|
||||
|
||||
// WritableResolver extends Resolver with the ability to write file content.
|
||||
type WritableResolver interface {
|
||||
Resolver
|
||||
|
||||
|
||||
@@ -4,13 +4,25 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// SearchResult represents a match found during content scanning, such as secret detection.
|
||||
type SearchResult struct {
|
||||
// Classification identifies the type or category of the matched content.
|
||||
Classification string `json:"classification"`
|
||||
LineNumber int64 `json:"lineNumber"`
|
||||
LineOffset int64 `json:"lineOffset"`
|
||||
SeekPosition int64 `json:"seekPosition"`
|
||||
Length int64 `json:"length"`
|
||||
Value string `json:"value,omitempty"`
|
||||
|
||||
// LineNumber is the 1-indexed line number where the match was found.
|
||||
LineNumber int64 `json:"lineNumber"`
|
||||
|
||||
// LineOffset is the character offset from the start of the line where the match begins.
|
||||
LineOffset int64 `json:"lineOffset"`
|
||||
|
||||
// SeekPosition is the absolute byte offset from the start of the file.
|
||||
SeekPosition int64 `json:"seekPosition"`
|
||||
|
||||
// Length is the size in bytes of the matched content.
|
||||
Length int64 `json:"length"`
|
||||
|
||||
// Value optionally contains the actual matched content.
|
||||
Value string `json:"value,omitempty"`
|
||||
}
|
||||
|
||||
func (s SearchResult) String() string {
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
package file
|
||||
|
||||
const (
|
||||
NoFilesSelection Selection = "none"
|
||||
FilesOwnedByPackageSelection Selection = "owned-by-package"
|
||||
AllFilesSelection Selection = "all"
|
||||
NoFilesSelection Selection = "none" // no files are selected
|
||||
FilesOwnedByPackageSelection Selection = "owned-by-package" // only files owned by packages are selected
|
||||
AllFilesSelection Selection = "all" // all files are selected
|
||||
)
|
||||
|
||||
// Selection defines which files should be included during cataloging operations.
|
||||
type Selection string
|
||||
|
||||
@@ -15,10 +15,10 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/relationship"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/format/internal/spdxutil/helpers"
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
|
||||
@@ -8,9 +8,9 @@ import (
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/format/internal"
|
||||
"github.com/anchore/syft/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
@@ -47,6 +47,7 @@ func Test_OriginatorSupplier(t *testing.T) {
|
||||
pkg.PythonUvLockEntry{},
|
||||
pkg.RustBinaryAuditEntry{},
|
||||
pkg.RustCargoLockEntry{},
|
||||
pkg.SnapEntry{},
|
||||
pkg.SwiftPackageManagerResolvedEntry{},
|
||||
pkg.SwiplPackEntry{},
|
||||
pkg.OpamPackage{},
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/iancoleman/strcase"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/internal/packagemetadata"
|
||||
)
|
||||
|
||||
type schema struct {
|
||||
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
formatInternal "github.com/anchore/syft/syft/format/internal"
|
||||
"github.com/anchore/syft/syft/format/syftjson/model"
|
||||
"github.com/anchore/syft/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
|
||||
@@ -10,9 +10,9 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/format/syftjson/model"
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
@@ -11,10 +11,10 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
stereoFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/format/syftjson/model"
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/internal/sourcemetadata"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
@@ -1 +1 @@
|
||||
this mimics a partial layout on a linux system within /sys/devices/virtual to help with ensuring globbing for files does not end up in a infinite loop
|
||||
This mimics a partial layout on a Linux system within /sys/devices/virtual to help ensure globbing for files does not end up in an infinite loop.
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Please see [schema/json/README.md](../../../schema/json/README.md) for more information on the JSON schema files in this directory.
|
||||
+64
-22
@@ -13,32 +13,74 @@ var _ FileOwner = (*AlpmDBEntry)(nil)
|
||||
|
||||
const AlpmDBGlob = "**/var/lib/pacman/local/**/desc"
|
||||
|
||||
// AlpmDBEntry is a struct that represents the package data stored in the pacman fla-filet stores for arch linux.
|
||||
// AlpmDBEntry is a struct that represents the package data stored in the pacman flat-file stores for arch linux.
|
||||
type AlpmDBEntry struct {
|
||||
BasePackage string `mapstructure:"base" json:"basepackage" cyclonedx:"basepackage"`
|
||||
Package string `mapstructure:"name" json:"package" cyclonedx:"package"`
|
||||
Version string `mapstructure:"version" json:"version" cyclonedx:"version"`
|
||||
Description string `mapstructure:"desc" json:"description" cyclonedx:"description"`
|
||||
Architecture string `mapstructure:"arch" json:"architecture" cyclonedx:"architecture"`
|
||||
Size int `mapstructure:"size" json:"size" cyclonedx:"size"`
|
||||
Packager string `mapstructure:"packager" json:"packager"`
|
||||
URL string `mapstructure:"url" json:"url"`
|
||||
Validation string `mapstructure:"validation" json:"validation"`
|
||||
Reason int `mapstructure:"reason" json:"reason"`
|
||||
Files []AlpmFileRecord `mapstructure:"files" json:"files"`
|
||||
Backup []AlpmFileRecord `mapstructure:"backup" json:"backup"`
|
||||
Provides []string `mapstructure:"provides" json:"provides,omitempty"`
|
||||
Depends []string `mapstructure:"depends" json:"depends,omitempty"`
|
||||
// BasePackage is the base package name this package was built from (source package in Arch build system)
|
||||
BasePackage string `mapstructure:"base" json:"basepackage" cyclonedx:"basepackage"`
|
||||
|
||||
// Package is the package name as found in the desc file
|
||||
Package string `mapstructure:"name" json:"package" cyclonedx:"package"`
|
||||
|
||||
// Version is the package version as found in the desc file
|
||||
Version string `mapstructure:"version" json:"version" cyclonedx:"version"`
|
||||
|
||||
// Description is a human-readable package description
|
||||
Description string `mapstructure:"desc" json:"description" cyclonedx:"description"`
|
||||
|
||||
// Architecture is the target CPU architecture as defined in Arch architecture spec (e.g. x86_64, aarch64, or "any" for arch-independent packages)
|
||||
Architecture string `mapstructure:"arch" json:"architecture" cyclonedx:"architecture"`
|
||||
|
||||
// Size is the installed size in bytes
|
||||
Size int `mapstructure:"size" json:"size" cyclonedx:"size"`
|
||||
|
||||
// Packager is the name and email of the person who packaged this (RFC822 format)
|
||||
Packager string `mapstructure:"packager" json:"packager"`
|
||||
|
||||
// URL is the upstream project URL
|
||||
URL string `mapstructure:"url" json:"url"`
|
||||
|
||||
// Validation is the validation method used for package integrity (e.g. pgp signature, sha256 checksum)
|
||||
Validation string `mapstructure:"validation" json:"validation"`
|
||||
|
||||
// Reason is the installation reason tracked by pacman (0=explicitly installed by user, 1=installed as dependency)
|
||||
Reason int `mapstructure:"reason" json:"reason"`
|
||||
|
||||
// Files are the files installed by this package
|
||||
Files []AlpmFileRecord `mapstructure:"files" json:"files"`
|
||||
|
||||
// Backup is the list of configuration files that pacman backs up before upgrades
|
||||
Backup []AlpmFileRecord `mapstructure:"backup" json:"backup"`
|
||||
|
||||
// Provides are virtual packages provided by this package (allows other packages to depend on capabilities rather than specific packages)
|
||||
Provides []string `mapstructure:"provides" json:"provides,omitempty"`
|
||||
|
||||
// Depends are the runtime dependencies required by this package
|
||||
Depends []string `mapstructure:"depends" json:"depends,omitempty"`
|
||||
}
|
||||
|
||||
type AlpmFileRecord struct {
|
||||
Path string `mapstruture:"path" json:"path,omitempty"`
|
||||
Type string `mapstructure:"type" json:"type,omitempty"`
|
||||
UID string `mapstructure:"uid" json:"uid,omitempty"`
|
||||
GID string `mapstructure:"gid" json:"gid,omitempty"`
|
||||
Time time.Time `mapstructure:"time" json:"time,omitempty"`
|
||||
Size string `mapstructure:"size" json:"size,omitempty"`
|
||||
Link string `mapstructure:"link" json:"link,omitempty"`
|
||||
// Path is the file path relative to the filesystem root
|
||||
Path string `mapstruture:"path" json:"path,omitempty"`
|
||||
|
||||
// Type is the file type (e.g. regular file, directory, symlink)
|
||||
Type string `mapstructure:"type" json:"type,omitempty"`
|
||||
|
||||
// UID is the file owner user ID as recorded by pacman
|
||||
UID string `mapstructure:"uid" json:"uid,omitempty"`
|
||||
|
||||
// GID is the file owner group ID as recorded by pacman
|
||||
GID string `mapstructure:"gid" json:"gid,omitempty"`
|
||||
|
||||
// Time is the file modification timestamp
|
||||
Time time.Time `mapstructure:"time" json:"time,omitempty"`
|
||||
|
||||
// Size is the file size in bytes
|
||||
Size string `mapstructure:"size" json:"size,omitempty"`
|
||||
|
||||
// Link is the symlink target path if this is a symlink
|
||||
Link string `mapstructure:"link" json:"link,omitempty"`
|
||||
|
||||
// Digests contains file content hashes for integrity verification
|
||||
Digests []file.Digest `mapstructure:"digests" json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
+56
-19
@@ -23,22 +23,50 @@ var _ FileOwner = (*ApkDBEntry)(nil)
|
||||
// - https://git.alpinelinux.org/apk-tools/tree/src/package.c
|
||||
// - https://git.alpinelinux.org/apk-tools/tree/src/database.c
|
||||
type ApkDBEntry struct {
|
||||
Package string `mapstructure:"P" json:"package"`
|
||||
OriginPackage string `mapstructure:"o" json:"originPackage" cyclonedx:"originPackage"`
|
||||
Maintainer string `mapstructure:"m" json:"maintainer"`
|
||||
Version string `mapstructure:"V" json:"version"`
|
||||
Architecture string `mapstructure:"A" json:"architecture"`
|
||||
URL string `mapstructure:"U" json:"url"`
|
||||
Description string `mapstructure:"T" json:"description"`
|
||||
Size int `mapstructure:"S" json:"size" cyclonedx:"size"`
|
||||
InstalledSize int `mapstructure:"I" json:"installedSize" cyclonedx:"installedSize"`
|
||||
Dependencies []string `mapstructure:"D" json:"pullDependencies" cyclonedx:"pullDependencies"`
|
||||
Provides []string `mapstructure:"p" json:"provides" cyclonedx:"provides"`
|
||||
Checksum string `mapstructure:"C" json:"pullChecksum" cyclonedx:"pullChecksum"`
|
||||
GitCommit string `mapstructure:"c" json:"gitCommitOfApkPort" cyclonedx:"gitCommitOfApkPort"`
|
||||
Files []ApkFileRecord `json:"files"`
|
||||
// Package is the package name as found in the installed file
|
||||
Package string `mapstructure:"P" json:"package"`
|
||||
|
||||
// OriginPackage is the original source package name this binary was built from (used to track which aport/source built this)
|
||||
OriginPackage string `mapstructure:"o" json:"originPackage" cyclonedx:"originPackage"`
|
||||
|
||||
// Maintainer is the package maintainer name and email
|
||||
Maintainer string `mapstructure:"m" json:"maintainer"`
|
||||
|
||||
// Version is the package version as found in the installed file
|
||||
Version string `mapstructure:"V" json:"version"`
|
||||
|
||||
// Architecture is the target CPU architecture
|
||||
Architecture string `mapstructure:"A" json:"architecture"`
|
||||
|
||||
// URL is the upstream project URL
|
||||
URL string `mapstructure:"U" json:"url"`
|
||||
|
||||
// Description is a human-readable package description
|
||||
Description string `mapstructure:"T" json:"description"`
|
||||
|
||||
// Size is the package archive size in bytes (.apk file size)
|
||||
Size int `mapstructure:"S" json:"size" cyclonedx:"size"`
|
||||
|
||||
// InstalledSize is the total size of installed files in bytes
|
||||
InstalledSize int `mapstructure:"I" json:"installedSize" cyclonedx:"installedSize"`
|
||||
|
||||
// Dependencies are the runtime dependencies required by this package
|
||||
Dependencies []string `mapstructure:"D" json:"pullDependencies" cyclonedx:"pullDependencies"`
|
||||
|
||||
// Provides are virtual packages provided by this package (for capability-based dependencies)
|
||||
Provides []string `mapstructure:"p" json:"provides" cyclonedx:"provides"`
|
||||
|
||||
// Checksum is the package content checksum for integrity verification
|
||||
Checksum string `mapstructure:"C" json:"pullChecksum" cyclonedx:"pullChecksum"`
|
||||
|
||||
// GitCommit is the git commit hash of the APK port definition in Alpine's aports repository
|
||||
GitCommit string `mapstructure:"c" json:"gitCommitOfApkPort" cyclonedx:"gitCommitOfApkPort"`
|
||||
|
||||
// Files are the files installed by this package
|
||||
Files []ApkFileRecord `json:"files"`
|
||||
}
|
||||
|
||||
// spaceDelimitedStringSlice is an internal helper type for unmarshaling space-delimited strings from JSON into a string slice.
|
||||
type spaceDelimitedStringSlice []string
|
||||
|
||||
func (m *ApkDBEntry) UnmarshalJSON(data []byte) error {
|
||||
@@ -95,11 +123,20 @@ func (a *spaceDelimitedStringSlice) UnmarshalJSON(data []byte) error {
|
||||
|
||||
// ApkFileRecord represents a single file listing and metadata from a APK DB entry (which may have many of these file records).
|
||||
type ApkFileRecord struct {
|
||||
Path string `json:"path"`
|
||||
OwnerUID string `json:"ownerUid,omitempty"`
|
||||
OwnerGID string `json:"ownerGid,omitempty"`
|
||||
Permissions string `json:"permissions,omitempty"`
|
||||
Digest *file.Digest `json:"digest,omitempty"`
|
||||
// Path is the file path relative to the filesystem root
|
||||
Path string `json:"path"`
|
||||
|
||||
// OwnerUID is the file owner user ID
|
||||
OwnerUID string `json:"ownerUid,omitempty"`
|
||||
|
||||
// OwnerGID is the file owner group ID
|
||||
OwnerGID string `json:"ownerGid,omitempty"`
|
||||
|
||||
// Permissions is the file permission mode string (e.g. "0755", "0644")
|
||||
Permissions string `json:"permissions,omitempty"`
|
||||
|
||||
// Digest is the file content hash for integrity verification
|
||||
Digest *file.Digest `json:"digest,omitempty"`
|
||||
}
|
||||
|
||||
func (m ApkDBEntry) OwnedFiles() (result []string) {
|
||||
|
||||
+6
-4
@@ -13,9 +13,9 @@ type ClassifierMatch struct {
|
||||
Location file.Location `mapstructure:"Location" json:"location"`
|
||||
}
|
||||
|
||||
// ELFBinaryPackageNoteJSONPayload Represents metadata captured from the .note.package section of the binary
|
||||
// ELFBinaryPackageNoteJSONPayload Represents metadata captured from the .note.package section of an ELF-formatted binary
|
||||
type ELFBinaryPackageNoteJSONPayload struct {
|
||||
// these are well-known fields as defined by systemd ELF package metadata "spec" https://systemd.io/ELF_PACKAGE_METADATA/
|
||||
// (these are well-known fields as defined by systemd ELF package metadata "spec" https://systemd.io/ELF_PACKAGE_METADATA/)
|
||||
|
||||
// Type is the type of the package (e.g. "rpm", "deb", "apk", etc.)
|
||||
Type string `json:"type,omitempty"`
|
||||
@@ -23,7 +23,7 @@ type ELFBinaryPackageNoteJSONPayload struct {
|
||||
// Architecture of the binary package (e.g. "amd64", "arm", etc.)
|
||||
Architecture string `json:"architecture,omitempty"`
|
||||
|
||||
// OS CPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. cpe:/o:fedoraproject:fedora:33)
|
||||
// OSCPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. cpe:/o:fedoraproject:fedora:33)
|
||||
OSCPE string `json:"osCPE,omitempty"`
|
||||
|
||||
// OS is the OS name, typically corresponding to ID in os-release (e.g. "fedora")
|
||||
@@ -32,7 +32,8 @@ type ELFBinaryPackageNoteJSONPayload struct {
|
||||
// osVersion is the version of the OS, typically corresponding to VERSION_ID in os-release (e.g. "33")
|
||||
OSVersion string `json:"osVersion,omitempty"`
|
||||
|
||||
// these are additional fields that are not part of the systemd spec
|
||||
/////////////////////////////////////////////////////////////////////////////////
|
||||
// (these are additional fields that are not part of the systemd spec)
|
||||
|
||||
// System is a context-specific name for the system that the binary package is intended to run on or a part of
|
||||
System string `json:"system,omitempty"`
|
||||
@@ -49,5 +50,6 @@ type ELFBinaryPackageNoteJSONPayload struct {
|
||||
|
||||
// PEBinary represents metadata captured from a Portable Executable formatted binary (dll, exe, etc.)
|
||||
type PEBinary struct {
|
||||
// VersionResources contains key-value pairs extracted from the PE file's version resource section (e.g., FileVersion, ProductName, CompanyName).
|
||||
VersionResources KeyValues
|
||||
}
|
||||
|
||||
+20
-7
@@ -3,13 +3,26 @@ package pkg
|
||||
// BitnamiSBOMEntry represents all captured data from Bitnami packages
|
||||
// described in Bitnami' SPDX files.
|
||||
type BitnamiSBOMEntry struct {
|
||||
Name string `mapstructure:"name" json:"name"`
|
||||
Architecture string `mapstructure:"arch" json:"arch"`
|
||||
Distro string `mapstructure:"distro" json:"distro"`
|
||||
Revision string `mapstructure:"revision" json:"revision"`
|
||||
Version string `mapstructure:"version" json:"version"`
|
||||
Path string `mapstructure:"path" json:"path"`
|
||||
Files []string `mapstructure:"files" json:"files"`
|
||||
// Name is the package name as found in the Bitnami SPDX file
|
||||
Name string `mapstructure:"name" json:"name"`
|
||||
|
||||
// Architecture is the target CPU architecture (amd64 or arm64 in Bitnami images)
|
||||
Architecture string `mapstructure:"arch" json:"arch"`
|
||||
|
||||
// Distro is the distribution name this package is for (base OS like debian, ubuntu, etc.)
|
||||
Distro string `mapstructure:"distro" json:"distro"`
|
||||
|
||||
// Revision is the Bitnami-specific package revision number (incremented for Bitnami rebuilds of same upstream version)
|
||||
Revision string `mapstructure:"revision" json:"revision"`
|
||||
|
||||
// Version is the package version as found in the Bitnami SPDX file
|
||||
Version string `mapstructure:"version" json:"version"`
|
||||
|
||||
// Path is the installation path in the filesystem where the package is located
|
||||
Path string `mapstructure:"path" json:"path"`
|
||||
|
||||
// Files are the file paths owned by this package (tracked via SPDX relationships)
|
||||
Files []string `mapstructure:"files" json:"files"`
|
||||
}
|
||||
|
||||
func (b BitnamiSBOMEntry) OwnedFiles() (result []string) {
|
||||
|
||||
@@ -1392,6 +1392,17 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("ffmpeg-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "ffmpeg-shared-libs/5.1.4/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "ffmpeg",
|
||||
Version: "5.1.4",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ffmpeg@5.1.4",
|
||||
Locations: locations("libavcodec-9aae324f.so.59.37.100"),
|
||||
Metadata: metadata("ffmpeg-library"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -639,6 +639,30 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
PURL: mustPURL("pkg:generic/ffmpeg@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:ffmpeg:ffmpeg:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "ffmpeg-library",
|
||||
FileGlob: "**/libav*",
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// Primary pattern: FFmpeg version found in most libraries
|
||||
m.FileContentsVersionMatcher(`(?m)FFmpeg version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?)`),
|
||||
// Fallback: library-specific version patterns for libavcodec and libavformat
|
||||
m.FileContentsVersionMatcher(`(?m)Lavc(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
m.FileContentsVersionMatcher(`(?m)Lavf(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
),
|
||||
Package: "ffmpeg",
|
||||
PURL: mustPURL("pkg:generic/ffmpeg@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:ffmpeg:ffmpeg:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "ffmpeg-library",
|
||||
FileGlob: "**/libswresample*",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
// FFmpeg version pattern for libswresample
|
||||
`(?m)FFmpeg version (?P<version>[0-9]+\.[0-9]+(\.[0-9]+)?)`),
|
||||
Package: "ffmpeg",
|
||||
PURL: mustPURL("pkg:generic/ffmpeg@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:ffmpeg:ffmpeg:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
}
|
||||
|
||||
return append(classifiers, defaultJavaClassifiers()...)
|
||||
|
||||
BIN
Binary file not shown.
@@ -830,3 +830,12 @@ from-images:
|
||||
platform: linux/arm64
|
||||
paths:
|
||||
- /usr/local/bin/ffmpeg
|
||||
|
||||
- name: ffmpeg-shared-libs
|
||||
version: 5.1.4
|
||||
images:
|
||||
- ref: demisto/opencv:1.0.0.111908@sha256:abba3aab213990c666900a93db03420238527b757bf5825543cf7b4c5e11ac65
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /usr/local/lib/python3.11/site-packages/opencv_contrib_python.libs/libavcodec-9aae324f.so.59.37.100
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ Contents of section .note.package:
|
||||
```
|
||||
|
||||
#### Binaries related to Libraries
|
||||
The resulting SBOM shoult show the following relationships:
|
||||
The resulting SBOM should show the following relationships:
|
||||
```
|
||||
elfbinwithnestedlib -> libhello_world.so
|
||||
elfwithparallellibbin2 -> libhello_world.so
|
||||
|
||||
@@ -2,11 +2,15 @@ package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary/test-fixtures/manager/internal/config"
|
||||
)
|
||||
|
||||
@@ -195,3 +199,17 @@ func (e Entries) BinaryFromImageHasSnippet(cfg config.BinaryFromImage) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (e Entries) Sorted() iter.Seq2[LogicalEntryKey, EntryInfo] {
|
||||
keys := maps.Keys(e)
|
||||
slices.SortFunc(keys, func(a, b LogicalEntryKey) int {
|
||||
return strings.Compare(a.Path(), b.Path())
|
||||
})
|
||||
return func(yield func(LogicalEntryKey, EntryInfo) bool) {
|
||||
for _, k := range keys {
|
||||
if !yield(k, e[k]) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string {
|
||||
require.NoError(t, err)
|
||||
|
||||
var fixturePath string
|
||||
for k, v := range entries {
|
||||
for k, v := range entries.Sorted() {
|
||||
if filepath.Dir(k.Path()) == path {
|
||||
// prefer the snippet over the binary
|
||||
if !requireBinary {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
version "github.com/bitnami/go-version/pkg/version"
|
||||
"github.com/bitnami/go-version/pkg/version"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
|
||||
@@ -6,7 +6,7 @@ This folder contains the test data for the Conan package manager.
|
||||
|
||||
The conan lock file is created in the following way.
|
||||
|
||||
We explicitly use a package which has dependencies, which in turn also have dependendencies.
|
||||
We explicitly use a package which has dependencies, which in turn also have dependencies.
|
||||
This is necessary to verify that the dependency tree is properly parsed.
|
||||
|
||||
1. Use `conan lock create --reference "mfast/1.2.2#c6f6387c9b99780f0ee05e25f99d0f39"`
|
||||
|
||||
@@ -2,17 +2,21 @@ package dotnet
|
||||
|
||||
type CatalogerConfig struct {
|
||||
// DepPackagesMustHaveDLL allows for deps.json packages to be included only if there is a DLL on disk for that package.
|
||||
// app-config: dotnet.dep-packages-must-have-dll
|
||||
DepPackagesMustHaveDLL bool `mapstructure:"dep-packages-must-have-dll" json:"dep-packages-must-have-dll" yaml:"dep-packages-must-have-dll"`
|
||||
|
||||
// DepPackagesMustClaimDLL allows for deps.json packages to be included only if there is a runtime/resource DLL claimed in the deps.json targets section.
|
||||
// This does not require such claimed DLLs to exist on disk. The behavior of this
|
||||
// app-config: dotnet.dep-packages-must-claim-dll
|
||||
DepPackagesMustClaimDLL bool `mapstructure:"dep-packages-must-claim-dll" json:"dep-packages-must-claim-dll" yaml:"dep-packages-must-claim-dll"`
|
||||
|
||||
// PropagateDLLClaimsToParents allows for deps.json packages to be included if any child (transitive) package claims a DLL. This applies to both the claims configuration and evidence-on-disk configurations.
|
||||
// app-config: dotnet.propagate-dll-claims-to-parents
|
||||
PropagateDLLClaimsToParents bool `mapstructure:"propagate-dll-claims-to-parents" json:"propagate-dll-claims-to-parents" yaml:"propagate-dll-claims-to-parents"`
|
||||
|
||||
// RelaxDLLClaimsWhenBundlingDetected will look for indications of IL bundle tooling via deps.json package names
|
||||
// and, if found (and this config option is enabled), will relax the DepPackagesMustClaimDLL value to `false` only in those cases.
|
||||
// app-config: dotnet.relax-dll-claims-when-bundling-detected
|
||||
RelaxDLLClaimsWhenBundlingDetected bool `mapstructure:"relax-dll-claims-when-bundling-detected" json:"relax-dll-claims-when-bundling-detected" yaml:"relax-dll-claims-when-bundling-detected"`
|
||||
}
|
||||
|
||||
|
||||
@@ -19,19 +19,48 @@ var (
|
||||
)
|
||||
|
||||
type CatalogerConfig struct {
|
||||
SearchLocalModCacheLicenses bool `yaml:"search-local-mod-cache-licenses" json:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
|
||||
LocalModCacheDir string `yaml:"local-mod-cache-dir" json:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
|
||||
SearchLocalVendorLicenses bool `yaml:"search-local-vendor-licenses" json:"search-local-vendor-licenses" mapstructure:"search-local-vendor-licenses"`
|
||||
LocalVendorDir string `yaml:"local-vendor-dir" json:"local-vendor-dir" mapstructure:"local-vendor-dir"`
|
||||
SearchRemoteLicenses bool `yaml:"search-remote-licenses" json:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
Proxies []string `yaml:"proxies,omitempty" json:"proxies,omitempty" mapstructure:"proxies"`
|
||||
NoProxy []string `yaml:"no-proxy,omitempty" json:"no-proxy,omitempty" mapstructure:"no-proxy"`
|
||||
MainModuleVersion MainModuleVersionConfig `yaml:"main-module-version" json:"main-module-version" mapstructure:"main-module-version"`
|
||||
// SearchLocalModCacheLicenses enables searching for go package licenses in the local GOPATH mod cache.
|
||||
// app-config: golang.search-local-mod-cache-licenses
|
||||
SearchLocalModCacheLicenses bool `yaml:"search-local-mod-cache-licenses" json:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
|
||||
|
||||
// LocalModCacheDir specifies the location of the local go module cache directory. When not set, syft will attempt to discover the GOPATH env or default to $HOME/go.
|
||||
// app-config: golang.local-mod-cache-dir
|
||||
LocalModCacheDir string `yaml:"local-mod-cache-dir" json:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
|
||||
|
||||
// SearchLocalVendorLicenses enables searching for go package licenses in the local vendor directory relative to the go.mod file.
|
||||
// app-config: golang.search-local-vendor-licenses
|
||||
SearchLocalVendorLicenses bool `yaml:"search-local-vendor-licenses" json:"search-local-vendor-licenses" mapstructure:"search-local-vendor-licenses"`
|
||||
|
||||
// LocalVendorDir specifies the location of the local vendor directory. When not set, syft will search for a vendor directory relative to the go.mod file.
|
||||
// app-config: golang.local-vendor-dir
|
||||
LocalVendorDir string `yaml:"local-vendor-dir" json:"local-vendor-dir" mapstructure:"local-vendor-dir"`
|
||||
|
||||
// SearchRemoteLicenses enables downloading go package licenses from the upstream go proxy (typically proxy.golang.org).
|
||||
// app-config: golang.search-remote-licenses
|
||||
SearchRemoteLicenses bool `yaml:"search-remote-licenses" json:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
|
||||
// Proxies is a list of go module proxies to use when fetching go module metadata and licenses. When not set, syft will use the GOPROXY env or default to https://proxy.golang.org,direct.
|
||||
// app-config: golang.proxy
|
||||
Proxies []string `yaml:"proxies,omitempty" json:"proxies,omitempty" mapstructure:"proxies"`
|
||||
|
||||
// NoProxy is a list of glob patterns that match go module names that should not be fetched from the go proxy. When not set, syft will use the GOPRIVATE and GONOPROXY env vars.
|
||||
// app-config: golang.no-proxy
|
||||
NoProxy []string `yaml:"no-proxy,omitempty" json:"no-proxy,omitempty" mapstructure:"no-proxy"`
|
||||
|
||||
MainModuleVersion MainModuleVersionConfig `yaml:"main-module-version" json:"main-module-version" mapstructure:"main-module-version"`
|
||||
}
|
||||
|
||||
type MainModuleVersionConfig struct {
|
||||
FromLDFlags bool `yaml:"from-ld-flags" json:"from-ld-flags" mapstructure:"from-ld-flags"`
|
||||
FromContents bool `yaml:"from-contents" json:"from-contents" mapstructure:"from-contents"`
|
||||
// FromLDFlags enables parsing the main module version from the -ldflags build settings.
|
||||
// app-config: golang.main-module-version.from-ld-flags
|
||||
FromLDFlags bool `yaml:"from-ld-flags" json:"from-ld-flags" mapstructure:"from-ld-flags"`
|
||||
|
||||
// FromContents enables parsing the main module version from the binary contents. This is useful when the version is embedded in the binary but not in the build settings.
|
||||
// app-config: golang.main-module-version.from-contents
|
||||
FromContents bool `yaml:"from-contents" json:"from-contents" mapstructure:"from-contents"`
|
||||
|
||||
// FromBuildSettings enables parsing the main module version from the go build settings.
|
||||
// app-config: golang.main-module-version.from-build-settings
|
||||
FromBuildSettings bool `yaml:"from-build-settings" json:"from-build-settings" mapstructure:"from-build-settings"`
|
||||
}
|
||||
|
||||
|
||||
@@ -6,9 +6,11 @@ import (
|
||||
_ "embed"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
@@ -228,6 +230,11 @@ func candidateVendors(p pkg.Package) []string {
|
||||
vendors.union(candidateVendorsForWordpressPlugin(p))
|
||||
}
|
||||
|
||||
if p.Type == pkg.BinaryPkg && endsWithNumber(p.Name) {
|
||||
// add binary package digit-suffix variations (e.g. Qt5 -> Qt)
|
||||
addBinaryPackageDigitVariations(vendors)
|
||||
}
|
||||
|
||||
// We should no longer be generating vendor candidates with these values ["" and "*"]
|
||||
// (since CPEs will match any other value)
|
||||
vendors.removeByValue("")
|
||||
@@ -286,6 +293,9 @@ func candidateProductSet(p pkg.Package) fieldCandidateSet {
|
||||
if prod != "" {
|
||||
products.addValue(prod)
|
||||
}
|
||||
case p.Type == pkg.BinaryPkg && endsWithNumber(p.Name):
|
||||
// add binary package digit-suffix variations (e.g. Qt5 -> Qt)
|
||||
addBinaryPackageDigitVariations(products)
|
||||
}
|
||||
|
||||
switch p.Metadata.(type) {
|
||||
@@ -404,3 +414,33 @@ func addDelimiterVariations(fields fieldCandidateSet) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// removeTrailingDigits removes all trailing digits from a string
|
||||
func removeTrailingDigits(s string) string {
|
||||
re := regexp.MustCompile(`\d+$`)
|
||||
return re.ReplaceAllString(s, "")
|
||||
}
|
||||
|
||||
// addBinaryPackageDigitVariations adds variations with trailing digits removed for binary packages.For binary package types only, when the name ends with a digit, add a new variation with all suffix-digits removed (e.g. Qt5 -> Qt). This helps generate additional CPE permutations for better vulnerability matching.
|
||||
func addBinaryPackageDigitVariations(fields fieldCandidateSet) {
|
||||
candidatesForVariations := fields.copy()
|
||||
for _, candidate := range candidatesForVariations.values() {
|
||||
// Check if the candidate ends with a digit
|
||||
if len(candidate) > 0 && candidate[len(candidate)-1] >= '0' && candidate[len(candidate)-1] <= '9' {
|
||||
// Create variation with all suffix digits removed
|
||||
withoutDigits := removeTrailingDigits(candidate)
|
||||
if withoutDigits != "" && withoutDigits != candidate {
|
||||
fields.addValue(withoutDigits)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func endsWithNumber(s string) bool {
|
||||
if len(s) == 0 {
|
||||
return false
|
||||
}
|
||||
r := []rune(s)
|
||||
last := r[len(r)-1]
|
||||
return unicode.IsDigit(last)
|
||||
}
|
||||
|
||||
@@ -1145,3 +1145,67 @@ func TestDictionaryFindIsWired(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddBinaryPackageDigitVariations tests the heuristic for binary package types
|
||||
// where names ending with digits get variations with all suffix-digits removed (e.g. Qt5 -> Qt).
|
||||
// This improves vulnerability matching for binary packages like Qt6, libfoo123, etc.
|
||||
func TestAddBinaryPackageDigitVariations(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
packageType pkg.Type
|
||||
inputCandidates []string
|
||||
expectedPresent []string // These should be present in the result
|
||||
expectedAbsent []string // These should NOT be present in the result
|
||||
}{
|
||||
{
|
||||
name: "Qt5 binary package example",
|
||||
packageType: pkg.BinaryPkg,
|
||||
inputCandidates: []string{"Qt5"},
|
||||
expectedPresent: []string{"Qt5", "Qt"},
|
||||
expectedAbsent: []string{},
|
||||
},
|
||||
{
|
||||
name: "package with trailing digits",
|
||||
packageType: pkg.BinaryPkg,
|
||||
inputCandidates: []string{"Qt5", "libfoo123", "bar42", "baz"},
|
||||
expectedPresent: []string{"Qt5", "Qt", "libfoo123", "libfoo", "bar42", "bar", "baz"},
|
||||
expectedAbsent: []string{},
|
||||
},
|
||||
{
|
||||
name: "multiple trailing digits",
|
||||
inputCandidates: []string{"Qt872", "package999"},
|
||||
expectedPresent: []string{"Qt872", "Qt", "package999", "package"},
|
||||
expectedAbsent: []string{},
|
||||
},
|
||||
{
|
||||
name: "package without trailing digits",
|
||||
inputCandidates: []string{"QtCore", "libfoo", "bar"},
|
||||
expectedPresent: []string{"QtCore", "libfoo", "bar"},
|
||||
expectedAbsent: []string{"QtCor", "libfo", "ba"},
|
||||
},
|
||||
{
|
||||
name: "empty candidate set",
|
||||
packageType: pkg.BinaryPkg,
|
||||
inputCandidates: []string{},
|
||||
expectedPresent: []string{},
|
||||
expectedAbsent: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
fields := newFieldCandidateSet(test.inputCandidates...)
|
||||
addBinaryPackageDigitVariations(fields)
|
||||
|
||||
values := fields.uniqueValues()
|
||||
|
||||
for _, expected := range test.expectedPresent {
|
||||
assert.Contains(t, values, expected, "expected %q to be present", expected)
|
||||
}
|
||||
|
||||
for _, notExpected := range test.expectedAbsent {
|
||||
assert.NotContains(t, values, notExpected, "expected %q to be absent", notExpected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,6 +336,9 @@ func (p *CatalogTester) assertPkgs(t *testing.T, pkgs []pkg.Package, relationshi
|
||||
opts = append(opts, p.compareOptions...)
|
||||
opts = append(opts, cmp.Reporter(&r))
|
||||
|
||||
// ignore the "FoundBy" field on relationships as it is set in the generic cataloger before it's presence on the relationship
|
||||
opts = append(opts, cmpopts.IgnoreFields(pkg.Package{}, "FoundBy"))
|
||||
|
||||
// order should not matter
|
||||
relationship.Sort(p.expectedRelationships)
|
||||
relationship.Sort(relationships)
|
||||
|
||||
@@ -257,10 +257,14 @@ func (j *archiveParser) discoverMainPackage(ctx context.Context) (*pkg.Package,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, version, lics, err := j.discoverNameVersionLicense(ctx, manifest)
|
||||
name, version, lics, parsedPom, err := j.discoverNameVersionLicense(ctx, manifest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pkgPomProject *pkg.JavaPomProject
|
||||
if parsedPom != nil {
|
||||
pkgPomProject = newPomProject(ctx, nil, parsedPom.path, parsedPom.project)
|
||||
}
|
||||
|
||||
return &pkg.Package{
|
||||
// TODO: maybe select name should just have a pom properties in it?
|
||||
@@ -275,12 +279,13 @@ func (j *archiveParser) discoverMainPackage(ctx context.Context) (*pkg.Package,
|
||||
Metadata: pkg.JavaArchive{
|
||||
VirtualPath: j.location.Path(),
|
||||
Manifest: manifest,
|
||||
PomProject: pkgPomProject,
|
||||
ArchiveDigests: digests,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest *pkg.JavaManifest) (string, string, []pkg.License, error) {
|
||||
func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest *pkg.JavaManifest) (string, string, []pkg.License, *parsedPomProject, error) {
|
||||
// we use j.location because we want to associate the license declaration with where we discovered the contents in the manifest
|
||||
// TODO: when we support locations of paths within archives we should start passing the specific manifest location object instead of the top jar
|
||||
lics := pkg.NewLicensesFromLocationWithContext(ctx, j.location, selectLicenses(manifest)...)
|
||||
@@ -302,7 +307,7 @@ func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest
|
||||
if len(lics) == 0 {
|
||||
fileLicenses, err := j.getLicenseFromFileInArchive(ctx)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
return "", "", nil, parsedPom, err
|
||||
}
|
||||
if fileLicenses != nil {
|
||||
lics = append(lics, fileLicenses...)
|
||||
@@ -317,7 +322,7 @@ func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest
|
||||
lics = j.findLicenseFromJavaMetadata(ctx, groupID, artifactID, version, parsedPom, manifest)
|
||||
}
|
||||
|
||||
return artifactID, version, lics, nil
|
||||
return artifactID, version, lics, parsedPom, nil
|
||||
}
|
||||
|
||||
// findLicenseFromJavaMetadata attempts to find license information from all available maven metadata properties and pom info
|
||||
@@ -387,43 +392,93 @@ type parsedPomProject struct {
|
||||
|
||||
// discoverMainPackageFromPomInfo attempts to resolve maven groupId, artifactId, version and other info from found pom information
|
||||
func (j *archiveParser) discoverMainPackageFromPomInfo(ctx context.Context) (group, name, version string, parsedPom *parsedPomProject) {
|
||||
var pomProperties pkg.JavaPomProperties
|
||||
|
||||
// Find the pom.properties/pom.xml if the names seem like a plausible match
|
||||
properties, _ := pomPropertiesByParentPath(j.archivePath, j.location, j.fileManifest.GlobMatch(false, pomPropertiesGlob))
|
||||
projects, _ := pomProjectByParentPath(j.archivePath, j.location, j.fileManifest.GlobMatch(false, pomXMLGlob))
|
||||
|
||||
// map of all the artifacts in the pom properties, in order to chek exact match with the filename
|
||||
artifactsMap := j.buildArtifactsMap(properties)
|
||||
pomProperties, parsedPom := j.findBestPomMatch(properties, projects, artifactsMap)
|
||||
|
||||
parsedPom = j.handleSinglePomXML(properties, projects, parsedPom)
|
||||
|
||||
return j.resolveIdentity(ctx, pomProperties, parsedPom)
|
||||
}
|
||||
|
||||
func (j *archiveParser) buildArtifactsMap(properties map[string]pkg.JavaPomProperties) *strset.Set {
|
||||
artifactsMap := strset.New()
|
||||
for _, propertiesObj := range properties {
|
||||
artifactsMap.Add(propertiesObj.ArtifactID)
|
||||
}
|
||||
return artifactsMap
|
||||
}
|
||||
|
||||
func (j *archiveParser) findBestPomMatch(properties map[string]pkg.JavaPomProperties,
|
||||
projects map[string]*parsedPomProject, artifactsMap *strset.Set) (pkg.JavaPomProperties, *parsedPomProject) {
|
||||
var pomProperties pkg.JavaPomProperties
|
||||
var parsedPom *parsedPomProject
|
||||
|
||||
for parentPath, propertiesObj := range sortedIter(properties) {
|
||||
// the logic for selecting the best name is as follows:
|
||||
// if we find an artifact id AND group id which are both contained in the filename
|
||||
// OR if we have an artifact id that exactly matches the filename, prefer this
|
||||
// OTHERWISE track the first matching pom properties with a pom.xml
|
||||
// FINALLY return the first matching pom properties
|
||||
if artifactIDMatchesFilename(propertiesObj.ArtifactID, j.fileInfo.name, artifactsMap) {
|
||||
if pomProperties.ArtifactID == "" { // keep the first match, or overwrite if we find more specific entries
|
||||
pomProperties = propertiesObj
|
||||
}
|
||||
if proj, exists := projects[parentPath]; exists {
|
||||
if parsedPom == nil { // keep the first matching artifact if we don't find an exact match or groupid + artfiact id match
|
||||
pomProperties = propertiesObj // set this, as it may not be the first entry found
|
||||
parsedPom = proj
|
||||
}
|
||||
// if artifact ID is the entire filename or BOTH artifactID and groupID are contained in the artifact, prefer this match
|
||||
if strings.Contains(j.fileInfo.name, propertiesObj.GroupID) || j.fileInfo.name == propertiesObj.ArtifactID {
|
||||
pomProperties = propertiesObj // this is an exact match, use it
|
||||
parsedPom = proj
|
||||
break
|
||||
}
|
||||
}
|
||||
if !artifactIDMatchesFilename(propertiesObj.ArtifactID, j.fileInfo.name, artifactsMap) {
|
||||
continue
|
||||
}
|
||||
|
||||
pomProperties, parsedPom = j.updateMatchIfBetter(pomProperties, parsedPom, propertiesObj, parentPath, projects)
|
||||
|
||||
if j.isExactMatch(propertiesObj, parsedPom) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return pomProperties, parsedPom
|
||||
}
|
||||
|
||||
func (j *archiveParser) updateMatchIfBetter(currentProps pkg.JavaPomProperties, currentPom *parsedPomProject,
|
||||
newProps pkg.JavaPomProperties, parentPath string, projects map[string]*parsedPomProject) (pkg.JavaPomProperties, *parsedPomProject) {
|
||||
// Keep the first match
|
||||
if currentProps.ArtifactID == "" {
|
||||
proj, hasProject := projects[parentPath]
|
||||
if hasProject {
|
||||
return newProps, proj
|
||||
}
|
||||
return newProps, currentPom
|
||||
}
|
||||
|
||||
proj, hasProject := projects[parentPath]
|
||||
if !hasProject {
|
||||
return currentProps, currentPom
|
||||
}
|
||||
|
||||
// Keep the first matching artifact with a pom.xml
|
||||
if currentPom == nil {
|
||||
return newProps, proj
|
||||
}
|
||||
|
||||
// Prefer exact matches
|
||||
if j.isExactMatch(newProps, proj) {
|
||||
return newProps, proj
|
||||
}
|
||||
|
||||
return currentProps, currentPom
|
||||
}
|
||||
|
||||
func (j *archiveParser) isExactMatch(props pkg.JavaPomProperties, pom *parsedPomProject) bool {
|
||||
if pom == nil {
|
||||
return false
|
||||
}
|
||||
return strings.Contains(j.fileInfo.name, props.GroupID) || j.fileInfo.name == props.ArtifactID
|
||||
}
|
||||
|
||||
func (j *archiveParser) handleSinglePomXML(properties map[string]pkg.JavaPomProperties,
|
||||
projects map[string]*parsedPomProject, currentPom *parsedPomProject) *parsedPomProject {
|
||||
if len(properties) == 0 && len(projects) == 1 {
|
||||
for _, projectsObj := range projects {
|
||||
return projectsObj
|
||||
}
|
||||
}
|
||||
return currentPom
|
||||
}
|
||||
|
||||
func (j *archiveParser) resolveIdentity(ctx context.Context, pomProperties pkg.JavaPomProperties,
|
||||
parsedPom *parsedPomProject) (group, name, version string, pom *parsedPomProject) {
|
||||
group = pomProperties.GroupID
|
||||
name = pomProperties.ArtifactID
|
||||
version = pomProperties.Version
|
||||
|
||||
@@ -79,9 +79,12 @@ func TestSearchMavenForLicenses(t *testing.T) {
|
||||
ReadCloser: fixture,
|
||||
}, tc.detectNested, tc.config)
|
||||
defer cleanupFn()
|
||||
require.NoError(t, err)
|
||||
|
||||
// assert licenses are discovered from upstream
|
||||
_, _, _, parsedPom := ap.discoverMainPackageFromPomInfo(context.Background())
|
||||
require.NotNil(t, parsedPom, "expected to find pom information in the fixture")
|
||||
require.NotNil(t, parsedPom.project, "expected parsedPom to have a project")
|
||||
resolvedLicenses, _ := ap.maven.ResolveLicenses(context.Background(), parsedPom.project)
|
||||
assert.Equal(t, tc.expectedLicenses, toPkgLicenses(ctx, nil, resolvedLicenses))
|
||||
})
|
||||
@@ -148,10 +151,23 @@ func TestParseJar(t *testing.T) {
|
||||
},
|
||||
PomProperties: &pkg.JavaPomProperties{
|
||||
Path: "META-INF/maven/io.jenkins.plugins/example-jenkins-plugin/pom.properties",
|
||||
Name: "",
|
||||
GroupID: "io.jenkins.plugins",
|
||||
ArtifactID: "example-jenkins-plugin",
|
||||
Version: "1.0-SNAPSHOT",
|
||||
},
|
||||
PomProject: &pkg.JavaPomProject{
|
||||
Path: "META-INF/maven/io.jenkins.plugins/example-jenkins-plugin/pom.xml",
|
||||
Name: "Example Jenkins Plugin",
|
||||
GroupID: "io.jenkins.plugins",
|
||||
ArtifactID: "example-jenkins-plugin",
|
||||
Version: "1.0-SNAPSHOT",
|
||||
Parent: &pkg.JavaPomParent{
|
||||
GroupID: "org.jenkins-ci.plugins",
|
||||
ArtifactID: "plugin",
|
||||
Version: "4.46",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -189,6 +205,14 @@ func TestParseJar(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
// 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": {
|
||||
@@ -286,6 +310,12 @@ func TestParseJar(t *testing.T) {
|
||||
ArtifactID: "example-java-app-maven",
|
||||
Version: "0.1.0",
|
||||
},
|
||||
PomProject: &pkg.JavaPomProject{
|
||||
Path: "META-INF/maven/org.anchore/example-java-app-maven/pom.xml",
|
||||
GroupID: "org.anchore",
|
||||
ArtifactID: "example-java-app-maven",
|
||||
Version: "0.1.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"joda-time": {
|
||||
@@ -1127,6 +1157,13 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
GroupID: "org.apache.directory.api",
|
||||
ArtifactID: "api-all",
|
||||
Version: "2.0.0",
|
||||
}, PomProject: &pkg.JavaPomProject{
|
||||
Path: "META-INF/maven/org.apache.directory.api/api-all/pom.xml",
|
||||
ArtifactID: "api-all",
|
||||
GroupID: "org.apache.directory.api",
|
||||
Version: "2.0.0",
|
||||
Name: "Apache Directory API All",
|
||||
Parent: &pkg.JavaPomParent{GroupID: "org.apache.directory.api", ArtifactID: "api-parent", Version: "2.0.0"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -1163,6 +1200,46 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
micronautAop := pkg.Package{
|
||||
Name: "micronaut-aop",
|
||||
Version: "4.9.11",
|
||||
PURL: "pkg:maven/io.micronaut/micronaut-aop@4.9.11",
|
||||
Locations: file.NewLocationSet(file.NewLocation("test-fixtures/jar-metadata/cache/micronaut-aop-4.9.11.jar")),
|
||||
Type: pkg.JavaPkg,
|
||||
Language: pkg.Java,
|
||||
Metadata: pkg.JavaArchive{
|
||||
VirtualPath: "test-fixtures/jar-metadata/cache/micronaut-aop-4.9.11.jar",
|
||||
Manifest: &pkg.JavaManifest{
|
||||
Main: []pkg.KeyValue{
|
||||
{
|
||||
Key: "Manifest-Version",
|
||||
Value: "1.0",
|
||||
},
|
||||
{
|
||||
Key: "Automatic-Module-Name",
|
||||
Value: "io.micronaut.micronaut_aop",
|
||||
},
|
||||
{
|
||||
Key: "Implementation-Version",
|
||||
Value: "4.9.11",
|
||||
},
|
||||
{
|
||||
Key: "Implementation-Title",
|
||||
Value: "Micronaut Core",
|
||||
},
|
||||
},
|
||||
}, PomProject: &pkg.JavaPomProject{
|
||||
Path: "META-INF/maven/io.micronaut/micronaut-aop/pom.xml",
|
||||
ArtifactID: "micronaut-aop",
|
||||
GroupID: "io.micronaut",
|
||||
Version: "4.9.11",
|
||||
Name: "Micronaut Core",
|
||||
Description: "Core components supporting the Micronaut Framework",
|
||||
URL: "https://micronaut.io",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fixtureName string
|
||||
@@ -1220,6 +1297,16 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
{Key: "Specification-Version", Value: "2.15.2"},
|
||||
},
|
||||
},
|
||||
PomProject: &pkg.JavaPomProject{
|
||||
Path: "META-INF/maven/com.fasterxml.jackson.core/jackson-core/pom.xml",
|
||||
ArtifactID: "jackson-core",
|
||||
GroupID: "com.fasterxml.jackson.core",
|
||||
Version: "2.15.2",
|
||||
Name: "Jackson-core",
|
||||
Description: "Core Jackson processing abstractions (aka Streaming API), implementation for JSON",
|
||||
URL: "https://github.com/FasterXML/jackson-core",
|
||||
Parent: &pkg.JavaPomParent{GroupID: "com.fasterxml.jackson", ArtifactID: "jackson-base", Version: "2.15.2"},
|
||||
},
|
||||
// not under test
|
||||
//ArchiveDigests: []file.Digest{{Algorithm: "sha1", Value: "d8bc1d9c428c96fe447e2c429fc4304d141024df"}},
|
||||
},
|
||||
@@ -1275,6 +1362,16 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
{Key: "Specification-Version", Value: "2.15.2"},
|
||||
},
|
||||
},
|
||||
PomProject: &pkg.JavaPomProject{
|
||||
Path: "META-INF/maven/com.fasterxml.jackson.core/jackson-core/pom.xml",
|
||||
ArtifactID: "jackson-core",
|
||||
GroupID: "com.fasterxml.jackson.core",
|
||||
Version: "2.15.2",
|
||||
Name: "Jackson-core",
|
||||
Description: "Core Jackson processing abstractions (aka Streaming API), implementation for JSON",
|
||||
URL: "https://github.com/FasterXML/jackson-core",
|
||||
Parent: &pkg.JavaPomParent{GroupID: "com.fasterxml.jackson", ArtifactID: "jackson-base", Version: "2.15.2"},
|
||||
},
|
||||
// not under test
|
||||
//ArchiveDigests: []file.Digest{{Algorithm: "sha1", Value: "abd3e329270fc54a2acaceb45420fd5710ecefd5"}},
|
||||
},
|
||||
@@ -1341,6 +1438,14 @@ func Test_parseJavaArchive_regressions(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "micronaut-aop",
|
||||
fixtureName: "micronaut-aop-4.9.11",
|
||||
fileExtension: "jar",
|
||||
expectedPkgs: []pkg.Package{
|
||||
micronautAop,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -41,11 +41,14 @@ func NewPomCataloger(cfg ArchiveCatalogerConfig) pkg.Cataloger {
|
||||
// Note: Older versions of lockfiles aren't supported yet
|
||||
func NewGradleLockfileCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("java-gradle-lockfile-cataloger").
|
||||
WithParserByGlobs(parseGradleLockfile, gradleLockfileGlob)
|
||||
WithParserByGlobs(parseGradleLockfile, "**/gradle.lockfile*")
|
||||
}
|
||||
|
||||
// NewJvmDistributionCataloger returns packages representing JDK/JRE installations (of multiple distribution types).
|
||||
func NewJvmDistributionCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("java-jvm-cataloger").
|
||||
WithParserByGlobs(parseJVMRelease, jvmReleaseGlob)
|
||||
// this is a very permissive glob that will match more than just the JVM release file.
|
||||
// we started with "**/{java,jvm}/*/release", but this prevents scanning JVM archive contents (e.g. jdk8u402.zip).
|
||||
// this approach lets us check more files for JVM release info, but be rather silent about errors.
|
||||
WithParserByGlobs(parseJVMRelease, "**/release")
|
||||
}
|
||||
|
||||
@@ -9,12 +9,30 @@ import (
|
||||
|
||||
type ArchiveCatalogerConfig struct {
|
||||
cataloging.ArchiveSearchConfig `yaml:",inline" json:"" mapstructure:",squash"`
|
||||
UseNetwork bool `yaml:"use-network" json:"use-network" mapstructure:"use-network"`
|
||||
UseMavenLocalRepository bool `yaml:"use-maven-localrepository" json:"use-maven-localrepository" mapstructure:"use-maven-localrepository"`
|
||||
MavenLocalRepositoryDir string `yaml:"maven-localrepository-dir" json:"maven-localrepository-dir" mapstructure:"maven-localrepository-dir"`
|
||||
MavenBaseURL string `yaml:"maven-base-url" json:"maven-base-url" mapstructure:"maven-base-url"`
|
||||
MaxParentRecursiveDepth int `yaml:"max-parent-recursive-depth" json:"max-parent-recursive-depth" mapstructure:"max-parent-recursive-depth"`
|
||||
ResolveTransitiveDependencies bool `yaml:"resolve-transitive-dependencies" json:"resolve-transitive-dependencies" mapstructure:"resolve-transitive-dependencies"`
|
||||
|
||||
// UseNetwork enables network operations for java package metadata enrichment, such as fetching parent POMs and license information.
|
||||
// app-config: java.use-network
|
||||
UseNetwork bool `yaml:"use-network" json:"use-network" mapstructure:"use-network"`
|
||||
|
||||
// UseMavenLocalRepository enables searching the local maven repository (~/.m2/repository by default) for parent POMs and other metadata.
|
||||
// app-config: java.use-maven-local-repository
|
||||
UseMavenLocalRepository bool `yaml:"use-maven-localrepository" json:"use-maven-localrepository" mapstructure:"use-maven-localrepository"`
|
||||
|
||||
// MavenLocalRepositoryDir specifies the location of the local maven repository. When not set, defaults to ~/.m2/repository.
|
||||
// app-config: java.maven-local-repository-dir
|
||||
MavenLocalRepositoryDir string `yaml:"maven-localrepository-dir" json:"maven-localrepository-dir" mapstructure:"maven-localrepository-dir"`
|
||||
|
||||
// MavenBaseURL specifies the base URL(s) to use for fetching POMs and metadata from maven central or other repositories. When not set, defaults to https://repo1.maven.org/maven2.
|
||||
// app-config: java.maven-url
|
||||
MavenBaseURL string `yaml:"maven-base-url" json:"maven-base-url" mapstructure:"maven-base-url"`
|
||||
|
||||
// MaxParentRecursiveDepth limits how many parent POMs will be fetched recursively before stopping. This prevents infinite loops or excessively deep parent chains.
|
||||
// app-config: java.max-parent-recursive-depth
|
||||
MaxParentRecursiveDepth int `yaml:"max-parent-recursive-depth" json:"max-parent-recursive-depth" mapstructure:"max-parent-recursive-depth"`
|
||||
|
||||
// ResolveTransitiveDependencies enables resolving transitive dependencies for java packages found within archives.
|
||||
// app-config: java.resolve-transitive-dependencies
|
||||
ResolveTransitiveDependencies bool `yaml:"resolve-transitive-dependencies" json:"resolve-transitive-dependencies" mapstructure:"resolve-transitive-dependencies"`
|
||||
}
|
||||
|
||||
func DefaultArchiveCatalogerConfig() ArchiveCatalogerConfig {
|
||||
|
||||
@@ -11,8 +11,6 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
const gradleLockfileGlob = "**/gradle.lockfile*"
|
||||
|
||||
// lockfileDependency represents a single dependency in the gradle.lockfile file
|
||||
type lockfileDependency struct {
|
||||
Group string
|
||||
|
||||
@@ -22,10 +22,6 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
// this is a very permissive glob that will match more than just the JVM release file.
|
||||
// we started with "**/{java,jvm}/*/release", but this prevents scanning JVM archive contents (e.g. jdk8u402.zip).
|
||||
// this approach lets us check more files for JVM release info, but be rather silent about errors.
|
||||
jvmReleaseGlob = "**/release"
|
||||
oracleVendor = "oracle"
|
||||
openJdkProduct = "openjdk"
|
||||
jre = "jre"
|
||||
|
||||
@@ -13,6 +13,7 @@ API_ALL_SOURCES = api-all-2.0.0-sources
|
||||
SPRING_INSTRUMENTATION = spring-instrumentation-4.3.0-1.0
|
||||
MULTIPLE_MATCHING = multiple-matching-2.11.5
|
||||
ORG_MULTIPLE_THENAME = org.multiple-thename
|
||||
MICRONAUT_AOP = micronaut-aop-4.9.11
|
||||
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
@@ -23,7 +24,7 @@ fixtures: $(CACHE_DIR)
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
$(CACHE_DIR): $(CACHE_DIR)/$(JACKSON_CORE).jar $(CACHE_DIR)/$(SBT_JACKSON_CORE).jar $(CACHE_DIR)/$(OPENSAML_CORE).jar $(CACHE_DIR)/$(API_ALL_SOURCES).jar $(CACHE_DIR)/$(SPRING_INSTRUMENTATION).jar $(CACHE_DIR)/$(MULTIPLE_MATCHING).jar
|
||||
$(CACHE_DIR): $(CACHE_DIR)/$(JACKSON_CORE).jar $(CACHE_DIR)/$(SBT_JACKSON_CORE).jar $(CACHE_DIR)/$(OPENSAML_CORE).jar $(CACHE_DIR)/$(API_ALL_SOURCES).jar $(CACHE_DIR)/$(SPRING_INSTRUMENTATION).jar $(CACHE_DIR)/$(MULTIPLE_MATCHING).jar $(CACHE_DIR)/$(MICRONAUT_AOP).jar
|
||||
|
||||
$(CACHE_DIR)/$(JACKSON_CORE).jar:
|
||||
mkdir -p $(CACHE_DIR)
|
||||
@@ -53,6 +54,10 @@ $(CACHE_DIR)/$(ORG_MULTIPLE_THENAME).jar:
|
||||
mkdir -p $(CACHE_DIR)
|
||||
cd $(ORG_MULTIPLE_THENAME) && zip -r $(CACHE_PATH)/$(ORG_MULTIPLE_THENAME).jar .
|
||||
|
||||
$(CACHE_DIR)/$(MICRONAUT_AOP).jar:
|
||||
mkdir -p $(CACHE_DIR)
|
||||
cd $(MICRONAUT_AOP) && zip -r $(CACHE_PATH)/$(MICRONAUT_AOP).jar .
|
||||
|
||||
# Jenkins plugins typically do not have the version included in the archive name,
|
||||
# so it is important to not include it in the generated test fixture
|
||||
$(CACHE_DIR)/gradle.hpi:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Jar-Metadata test fixtures
|
||||
|
||||
Each directory is the name of a jar to be created (simply a zip) based on the contents of the directory.
|
||||
This prevents us from having to create real jars by hand or keep binaries in the repo. This also means we dont need the
|
||||
This prevents us from having to create real jars by hand or keep binaries in the repo. This also means we don't need the
|
||||
entire jar, only the necessary metadata for testing.
|
||||
|
||||
### api-all-2.0.0-sources
|
||||
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
Manifest-Version: 1.0
|
||||
Automatic-Module-Name: io.micronaut.micronaut_aop
|
||||
Implementation-Version: 4.9.11
|
||||
Implementation-Title: Micronaut Core
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<!-- This module was also published with a richer model, Gradle metadata, -->
|
||||
<!-- which should be used instead. Do not delete the following line which -->
|
||||
<!-- is to indicate to Gradle or any Gradle module metadata file consumer -->
|
||||
<!-- that they should prefer consuming it instead. -->
|
||||
<!-- do_not_remove: published-with-gradle-metadata -->
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>io.micronaut</groupId>
|
||||
<artifactId>micronaut-aop</artifactId>
|
||||
<version>4.9.11</version>
|
||||
<name>Micronaut Core</name>
|
||||
<description>Core components supporting the Micronaut Framework</description>
|
||||
<url>https://micronaut.io</url>
|
||||
</project>
|
||||
@@ -3,9 +3,15 @@ package javascript
|
||||
const npmBaseURL = "https://registry.npmjs.org"
|
||||
|
||||
type CatalogerConfig struct {
|
||||
SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
NPMBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"`
|
||||
IncludeDevDependencies bool `json:"include-dev-dependencies" yaml:"include-dev-dependencies" mapstructure:"include-dev-dependencies"`
|
||||
// SearchRemoteLicenses enables querying the NPM registry API to retrieve license information for packages that are missing license data in their local metadata.
|
||||
// app-config: javascript.search-remote-licenses
|
||||
SearchRemoteLicenses bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
// NPMBaseURL specifies the base URL for the NPM registry API used when searching for remote license information.
|
||||
// app-config: javascript.npm-base-url
|
||||
NPMBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"`
|
||||
// IncludeDevDependencies controls whether development dependencies should be included in the catalog results, in addition to production dependencies.
|
||||
// app-config: javascript.include-dev-dependencies
|
||||
IncludeDevDependencies bool `json:"include-dev-dependencies" yaml:"include-dev-dependencies" mapstructure:"include-dev-dependencies"`
|
||||
}
|
||||
|
||||
func DefaultCatalogerConfig() CatalogerConfig {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -21,92 +21,186 @@ import (
|
||||
// integrity check
|
||||
var _ generic.Parser = parsePnpmLock
|
||||
|
||||
type pnpmLockYaml struct {
|
||||
Version string `json:"lockfileVersion" yaml:"lockfileVersion"`
|
||||
Dependencies map[string]interface{} `json:"dependencies" yaml:"dependencies"`
|
||||
Packages map[string]interface{} `json:"packages" yaml:"packages"`
|
||||
// pnpmPackage holds the raw name and version extracted from the lockfile.
|
||||
type pnpmPackage struct {
|
||||
Name string
|
||||
Version string
|
||||
}
|
||||
|
||||
// pnpmLockfileParser defines the interface for parsing different versions of pnpm lockfiles.
|
||||
type pnpmLockfileParser interface {
|
||||
Parse(version float64, data []byte) ([]pnpmPackage, error)
|
||||
}
|
||||
|
||||
// pnpmV6LockYaml represents the structure of pnpm lockfiles for versions < 9.0.
|
||||
type pnpmV6LockYaml struct {
|
||||
Dependencies map[string]interface{} `yaml:"dependencies"`
|
||||
Packages map[string]interface{} `yaml:"packages"`
|
||||
}
|
||||
|
||||
// pnpmV9LockYaml represents the structure of pnpm lockfiles for versions >= 9.0.
|
||||
type pnpmV9LockYaml struct {
|
||||
LockfileVersion string `yaml:"lockfileVersion"`
|
||||
Importers map[string]interface{} `yaml:"importers"` // Using interface{} for forward compatibility
|
||||
Packages map[string]interface{} `yaml:"packages"`
|
||||
}
|
||||
|
||||
// Parse implements the pnpmLockfileParser interface for v6-v8 lockfiles.
|
||||
func (p *pnpmV6LockYaml) Parse(version float64, data []byte) ([]pnpmPackage, error) {
|
||||
if err := yaml.Unmarshal(data, p); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal pnpm v6 lockfile: %w", err)
|
||||
}
|
||||
|
||||
packages := make(map[string]pnpmPackage)
|
||||
|
||||
// Direct dependencies
|
||||
for name, info := range p.Dependencies {
|
||||
ver, err := parseVersionField(name, info)
|
||||
if err != nil {
|
||||
log.WithFields("package", name, "error", err).Trace("unable to parse pnpm dependency")
|
||||
continue
|
||||
}
|
||||
key := name + "@" + ver
|
||||
packages[key] = pnpmPackage{Name: name, Version: ver}
|
||||
}
|
||||
|
||||
splitChar := "/"
|
||||
if version >= 6.0 {
|
||||
splitChar = "@"
|
||||
}
|
||||
|
||||
// All transitive dependencies
|
||||
for key := range p.Packages {
|
||||
name, ver, ok := parsePnpmPackageKey(key, splitChar)
|
||||
if !ok {
|
||||
log.WithFields("key", key).Trace("unable to parse pnpm package key")
|
||||
continue
|
||||
}
|
||||
pkgKey := name + "@" + ver
|
||||
packages[pkgKey] = pnpmPackage{Name: name, Version: ver}
|
||||
}
|
||||
|
||||
return toSortedSlice(packages), nil
|
||||
}
|
||||
|
||||
// Parse implements the PnpmLockfileParser interface for v9+ lockfiles.
|
||||
func (p *pnpmV9LockYaml) Parse(_ float64, data []byte) ([]pnpmPackage, error) {
|
||||
if err := yaml.Unmarshal(data, p); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal pnpm v9 lockfile: %w", err)
|
||||
}
|
||||
|
||||
packages := make(map[string]pnpmPackage)
|
||||
|
||||
// In v9, all resolved dependencies are listed in the top-level "packages" field.
|
||||
// The key format is like /<name>@<version> or /<name>@<version>(<peer-deps>).
|
||||
for key := range p.Packages {
|
||||
// The separator for name and version is consistently '@' in v9+ keys.
|
||||
name, ver, ok := parsePnpmPackageKey(key, "@")
|
||||
if !ok {
|
||||
log.WithFields("key", key).Trace("unable to parse pnpm v9 package key")
|
||||
continue
|
||||
}
|
||||
pkgKey := name + "@" + ver
|
||||
packages[pkgKey] = pnpmPackage{Name: name, Version: ver}
|
||||
}
|
||||
|
||||
return toSortedSlice(packages), nil
|
||||
}
|
||||
|
||||
// newPnpmLockfileParser is a factory function that returns the correct parser for the given lockfile version.
|
||||
func newPnpmLockfileParser(version float64) pnpmLockfileParser {
|
||||
if version >= 9.0 {
|
||||
return &pnpmV9LockYaml{}
|
||||
}
|
||||
return &pnpmV6LockYaml{}
|
||||
}
|
||||
|
||||
// parsePnpmLock is the main parser function for pnpm-lock.yaml files.
|
||||
func parsePnpmLock(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
bytes, err := io.ReadAll(reader)
|
||||
data, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to load pnpm-lock.yaml file: %w", err)
|
||||
}
|
||||
|
||||
var pkgs []pkg.Package
|
||||
var lockFile pnpmLockYaml
|
||||
var lockfile struct {
|
||||
Version string `yaml:"lockfileVersion"`
|
||||
}
|
||||
if err := yaml.Unmarshal(data, &lockfile); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse pnpm-lock.yaml version: %w", err)
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(bytes, &lockFile); err != nil {
|
||||
version, err := strconv.ParseFloat(lockfile.Version, 64)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid lockfile version %q: %w", lockfile.Version, err)
|
||||
}
|
||||
|
||||
parser := newPnpmLockfileParser(version)
|
||||
pnpmPkgs, err := parser.Parse(version, data)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to parse pnpm-lock.yaml file: %w", err)
|
||||
}
|
||||
|
||||
lockVersion, _ := strconv.ParseFloat(lockFile.Version, 64)
|
||||
|
||||
for name, info := range lockFile.Dependencies {
|
||||
version := ""
|
||||
|
||||
switch info := info.(type) {
|
||||
case string:
|
||||
version = info
|
||||
case map[string]interface{}:
|
||||
v, ok := info["version"]
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
ver, ok := v.(string)
|
||||
if ok {
|
||||
version = parseVersion(ver)
|
||||
}
|
||||
default:
|
||||
log.Tracef("unsupported pnpm dependency type: %+v", info)
|
||||
continue
|
||||
}
|
||||
|
||||
if hasPkg(pkgs, name, version) {
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPnpmPackage(ctx, resolver, reader.Location, name, version))
|
||||
packages := make([]pkg.Package, len(pnpmPkgs))
|
||||
for i, p := range pnpmPkgs {
|
||||
packages[i] = newPnpmPackage(ctx, resolver, reader.Location, p.Name, p.Version)
|
||||
}
|
||||
|
||||
packageNameRegex := regexp.MustCompile(`^/?([^(]*)(?:\(.*\))*$`)
|
||||
splitChar := "/"
|
||||
if lockVersion >= 6.0 {
|
||||
splitChar = "@"
|
||||
}
|
||||
|
||||
// parse packages from packages section of pnpm-lock.yaml
|
||||
for nameVersion := range lockFile.Packages {
|
||||
nameVersion = packageNameRegex.ReplaceAllString(nameVersion, "$1")
|
||||
nameVersionSplit := strings.Split(strings.TrimPrefix(nameVersion, "/"), splitChar)
|
||||
|
||||
// last element in split array is version
|
||||
version := nameVersionSplit[len(nameVersionSplit)-1]
|
||||
|
||||
// construct name from all array items other than last item (version)
|
||||
name := strings.Join(nameVersionSplit[:len(nameVersionSplit)-1], splitChar)
|
||||
|
||||
if hasPkg(pkgs, name, version) {
|
||||
continue
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, newPnpmPackage(ctx, resolver, reader.Location, name, version))
|
||||
}
|
||||
|
||||
pkg.Sort(pkgs)
|
||||
|
||||
return pkgs, nil, unknown.IfEmptyf(pkgs, "unable to determine packages")
|
||||
return packages, nil, unknown.IfEmptyf(packages, "unable to determine packages")
|
||||
}
|
||||
|
||||
func hasPkg(pkgs []pkg.Package, name, version string) bool {
|
||||
for _, p := range pkgs {
|
||||
if p.Name == name && p.Version == version {
|
||||
return true
|
||||
// parseVersionField extracts the version string from a dependency entry.
|
||||
func parseVersionField(name string, info interface{}) (string, error) {
|
||||
switch v := info.(type) {
|
||||
case string:
|
||||
return v, nil
|
||||
case map[string]interface{}:
|
||||
if ver, ok := v["version"].(string); ok {
|
||||
// e.g., "1.2.3(react@17.0.0)" -> "1.2.3"
|
||||
return strings.SplitN(ver, "(", 2)[0], nil
|
||||
}
|
||||
return "", fmt.Errorf("version field is not a string for %q", name)
|
||||
default:
|
||||
return "", fmt.Errorf("unsupported dependency type %T for %q", info, name)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parseVersion(version string) string {
|
||||
return strings.SplitN(version, "(", 2)[0]
|
||||
// parsePnpmPackageKey extracts the package name and version from a lockfile package key.
|
||||
// Handles formats like:
|
||||
// - /@babel/runtime/7.16.7
|
||||
// - /@types/node@14.18.12
|
||||
// - /is-glob@4.0.3
|
||||
// - /@babel/helper-plugin-utils@7.24.7(@babel/core@7.24.7)
|
||||
func parsePnpmPackageKey(key, separator string) (name, version string, ok bool) {
|
||||
// Strip peer dependency information, e.g., (...)
|
||||
key = strings.SplitN(key, "(", 2)[0]
|
||||
|
||||
// Strip leading slash
|
||||
key = strings.TrimPrefix(key, "/")
|
||||
|
||||
parts := strings.Split(key, separator)
|
||||
if len(parts) < 2 {
|
||||
return "", "", false
|
||||
}
|
||||
|
||||
version = parts[len(parts)-1]
|
||||
name = strings.Join(parts[:len(parts)-1], separator)
|
||||
|
||||
return name, version, true
|
||||
}
|
||||
|
||||
// toSortedSlice converts the map of packages to a sorted slice for deterministic output.
|
||||
func toSortedSlice(packages map[string]pnpmPackage) []pnpmPackage {
|
||||
pkgs := make([]pnpmPackage, 0, len(packages))
|
||||
for _, p := range packages {
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
||||
|
||||
sort.Slice(pkgs, func(i, j int) bool {
|
||||
if pkgs[i].Name == pkgs[j].Name {
|
||||
return pkgs[i].Version < pkgs[j].Version
|
||||
}
|
||||
return pkgs[i].Name < pkgs[j].Name
|
||||
})
|
||||
|
||||
return pkgs
|
||||
}
|
||||
|
||||
@@ -145,6 +145,50 @@ func TestParsePnpmV6Lock(t *testing.T) {
|
||||
pkgtest.TestFileParser(t, fixture, parsePnpmLock, expectedPkgs, expectedRelationships)
|
||||
}
|
||||
|
||||
func TestParsePnpmLockV9(t *testing.T) {
|
||||
var expectedRelationships []artifact.Relationship
|
||||
fixture := "test-fixtures/pnpm-v9/pnpm-lock.yaml"
|
||||
locationSet := file.NewLocationSet(file.NewLocation(fixture))
|
||||
|
||||
expected := []pkg.Package{
|
||||
{
|
||||
Name: "@babel/core",
|
||||
Version: "7.24.7",
|
||||
PURL: "pkg:npm/%40babel/core@7.24.7",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "@babel/helper-plugin-utils",
|
||||
Version: "7.24.7",
|
||||
PURL: "pkg:npm/%40babel/helper-plugin-utils@7.24.7",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "is-positive",
|
||||
Version: "3.1.0",
|
||||
PURL: "pkg:npm/is-positive@3.1.0",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
{
|
||||
Name: "rollup",
|
||||
Version: "4.18.0",
|
||||
PURL: "pkg:npm/rollup@4.18.0",
|
||||
Locations: locationSet,
|
||||
Language: pkg.JavaScript,
|
||||
Type: pkg.NpmPkg,
|
||||
},
|
||||
}
|
||||
|
||||
// TODO: no relationships are under test
|
||||
pkgtest.TestFileParser(t, fixture, parsePnpmLock, expected, expectedRelationships)
|
||||
}
|
||||
|
||||
func Test_corruptPnpmLock(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromFile(t, "test-fixtures/corrupt/pnpm-lock.yaml").
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
lockfileVersion: '9.0'
|
||||
|
||||
importers:
|
||||
.:
|
||||
dependencies:
|
||||
is-positive:
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0
|
||||
rollup:
|
||||
specifier: ^4.18.0
|
||||
version: 4.18.0
|
||||
|
||||
packages:
|
||||
/@babel/core@7.24.7:
|
||||
resolution: {integrity: sha512-4RjkiFFI42+268iBv2nC+iMLTJGQW3u9P7YvA3x/6MDrJ9IYZ8I/xx5a2GIhY5gBTOcI4iC5S5in2fGjE+P4Yw==}
|
||||
dev: false
|
||||
|
||||
/@babel/helper-plugin-utils@7.24.7(@babel/core@7.24.7):
|
||||
resolution: {integrity: sha512-8A2+zKm53/3w4rwbX11FMW/yFS6c5Vam02P/dw01aK6KbwkKqBaIt3eEATiKtn9I2uS1itk8/aZ2yZ/kURee4Q==}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
|
||||
dev: false
|
||||
|
||||
/is-positive@3.1.0:
|
||||
resolution: {integrity: sha512-9ffLCf_f5sopimAhg2g91a7b9Rw5A1aA9eI6S391S3VEzYw99I3iKjcZGxLp25s0cRxNBV5aL2mhn7421SSlA==}
|
||||
dev: false
|
||||
|
||||
/rollup@4.18.0:
|
||||
resolution: {integrity: sha512-QpQY2Q5i0y0Q3RoAvoChE/R5iN2k05N//bNvQbC2XvRjHFT1qWJ2r3n1bNqE+gGRJaeuQf0BxE42D7CyuLh3ZQ==}
|
||||
hasBin: true
|
||||
dev: false
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
var _ pkg.Cataloger = (*linuxKernelCataloger)(nil)
|
||||
|
||||
type LinuxKernelCatalogerConfig struct {
|
||||
// CatalogModules enables cataloging linux kernel modules (*.ko files) in addition to the kernel itself.
|
||||
// app-config: linux-kernel.catalog-modules
|
||||
CatalogModules bool `yaml:"catalog-modules" json:"catalog-modules" mapstructure:"catalog-modules"`
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
// CaptureOwnedFiles determines whether to record the list of files owned by each Nix package discovered in the store. Recording owned files provides more detailed information but increases processing time and memory usage.
|
||||
// app-config: nix.capture-owned-files
|
||||
CaptureOwnedFiles bool `json:"capture-owned-files" yaml:"capture-owned-files" mapstructure:"capture-owned-files"`
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user