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