mirror of
https://github.com/anchore/syft.git
synced 2026-10-12 14:17:20 +02:00
Compare commits
29
Commits
+1
-1
@@ -24,7 +24,7 @@ tools:
|
||||
# used to parse JSON/YAML annotations on the fixture cache image
|
||||
- name: yq
|
||||
version:
|
||||
want: v4.53.6
|
||||
want: v4.54.1
|
||||
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@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1
|
||||
uses: anchore/go-make/.github/actions/setup@27a9f35f41b5fe86d68815844efd2ba816e9ba6b # v0.9.1
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
cache-key-prefix: ${{ inputs.cache-key-prefix }}
|
||||
|
||||
@@ -21,6 +21,9 @@ updates:
|
||||
- dependency-name: "github.com/aquasecurity/go-version"
|
||||
- dependency-name: "github.com/knqyf263/go-apk-version"
|
||||
- dependency-name: "github.com/knqyf263/go-deb-version"
|
||||
# v1 changed SlicesAsSets hashing, which changes every artifact ID syft produces
|
||||
- dependency-name: "github.com/gohugoio/hashstructure"
|
||||
update-types: ["version-update:semver-major"]
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directories:
|
||||
|
||||
@@ -46,7 +46,7 @@ jobs:
|
||||
# these are checks that should be run on pull-request and merges to main.
|
||||
# we do NOT want to kick off a release if these have not been verified on main.
|
||||
# Please see the validations.yaml workflow for the names that should be used here.
|
||||
checks: '["Acceptance tests (Linux)", "Acceptance tests (Mac)", "Build snapshot artifacts", "CLI tests (Linux)", "Integration tests", "Static analysis", "Unit tests"]'
|
||||
checks: '["Acceptance tests (Linux)", "Acceptance tests (Mac)", "Build snapshot artifacts", "CLI tests (Linux)", "Containers-storage tests (Podman)", "Containers-storage tests (Buildah)", "Integration tests", "Static analysis", "Unit tests"]'
|
||||
|
||||
release:
|
||||
needs: [check-gate, version-available]
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
# for updating brew formula in anchore/homebrew-syft
|
||||
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
|
||||
|
||||
- uses: anchore/sbom-action@3ad7283483fc7af8ff2b4ea19663c2d5ca935e26 #v0.24.2
|
||||
- uses: anchore/sbom-action@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c #v0.24.3
|
||||
continue-on-error: true
|
||||
with:
|
||||
file: go.mod
|
||||
|
||||
@@ -51,8 +51,19 @@ jobs:
|
||||
name: "Unit tests"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [Unit-Test-Linux, Unit-Test-Windows]
|
||||
# a skipped job satisfies a required check, so this job must always run and fail explicitly
|
||||
# unless every dependency succeeded (otherwise a failing PR can still be auto-merged)
|
||||
if: ${{ always() }}
|
||||
steps:
|
||||
- run: echo All unit tests passed!
|
||||
- name: Require all unit test jobs to succeed
|
||||
env:
|
||||
NEEDS: ${{ toJSON(needs) }}
|
||||
run: |
|
||||
echo "$NEEDS" | jq -e 'length > 0 and all(.[]; .result == "success")' > /dev/null || {
|
||||
echo "$NEEDS" | jq -r 'to_entries[] | "\(.key): \(.value.result)"'
|
||||
exit 1
|
||||
}
|
||||
echo All unit tests passed!
|
||||
|
||||
Unit-Test-Linux:
|
||||
name: "Unit tests (Linux)"
|
||||
@@ -85,16 +96,24 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# go-make's setup only (not ./.github/actions/bootstrap): no apt, binny tools, or fixture cache are needed
|
||||
# go-make's setup only (not ./.github/actions/bootstrap): binny ships no windows release, so binny tools
|
||||
# are unavailable here (`make unit` skips the fixture refresh on windows)
|
||||
- name: Setup go + go-make tooling
|
||||
uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1
|
||||
uses: anchore/go-make/.github/actions/setup@27a9f35f41b5fe86d68815844efd2ba816e9ba6b # v0.9.1
|
||||
with:
|
||||
# keep in sync with the go-version default in ./.github/actions/bootstrap
|
||||
go-version: "1.26.2"
|
||||
|
||||
# the full unit suite leans on linux docker fixtures, so this runs the windows-relevant subset
|
||||
# the binny-free version of `make download-test-fixture-cache` (keep the oras version in sync with .binny.yaml)
|
||||
- name: Download test fixture cache
|
||||
shell: bash
|
||||
run: |
|
||||
curl -sSfL https://github.com/oras-project/oras/releases/download/v1.3.4/oras_1.3.4_windows_amd64.zip -o "$RUNNER_TEMP/oras.zip"
|
||||
unzip -q "$RUNNER_TEMP/oras.zip" oras.exe -d "$RUNNER_TEMP"
|
||||
"$RUNNER_TEMP/oras.exe" pull ghcr.io/anchore/oss-cache/syft-test-fixture-cache:v2
|
||||
|
||||
- name: Run unit tests
|
||||
run: make unit:windows
|
||||
run: make unit
|
||||
|
||||
Integration-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
@@ -158,6 +177,72 @@ jobs:
|
||||
path: snapshot/
|
||||
retention-days: 30
|
||||
|
||||
Containers-Storage-Podman:
|
||||
name: "Containers-storage tests (Podman)"
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
|
||||
with:
|
||||
name: snapshot
|
||||
path: snapshot
|
||||
|
||||
# go-make's setup only (not ./.github/actions/bootstrap): the test only needs make + the snapshot binary
|
||||
- name: Setup go + go-make tooling
|
||||
uses: anchore/go-make/.github/actions/setup@27a9f35f41b5fe86d68815844efd2ba816e9ba6b # v0.9.1
|
||||
with:
|
||||
# keep in sync with the go-version default in ./.github/actions/bootstrap
|
||||
go-version: "1.26.2"
|
||||
|
||||
- name: Install Podman
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y jq podman uidmap
|
||||
|
||||
- name: Scan image from Podman's local store
|
||||
run: make containers-storage-test
|
||||
env:
|
||||
BUILDER: podman
|
||||
|
||||
Containers-Storage-Buildah:
|
||||
name: "Containers-storage tests (Buildah)"
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c #v8.0.1
|
||||
with:
|
||||
name: snapshot
|
||||
path: snapshot
|
||||
|
||||
# go-make's setup only (not ./.github/actions/bootstrap): the test only needs make + the snapshot binary
|
||||
- name: Setup go + go-make tooling
|
||||
uses: anchore/go-make/.github/actions/setup@27a9f35f41b5fe86d68815844efd2ba816e9ba6b # v0.9.1
|
||||
with:
|
||||
# keep in sync with the go-version default in ./.github/actions/bootstrap
|
||||
go-version: "1.26.2"
|
||||
|
||||
- name: Install Buildah
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install --no-install-recommends -y buildah jq uidmap
|
||||
|
||||
- name: Scan image from Buildah's local store
|
||||
run: make containers-storage-test
|
||||
env:
|
||||
BUILDER: buildah
|
||||
|
||||
Acceptance-Linux:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Acceptance tests (Linux)"
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
version: "2"
|
||||
run:
|
||||
tests: false
|
||||
# keep in sync with buildTags in .make/main.go
|
||||
build-tags:
|
||||
- containers_image_openpgp
|
||||
- exclude_graphdriver_btrfs
|
||||
linters:
|
||||
default: none
|
||||
enable:
|
||||
|
||||
+4
-6
@@ -15,6 +15,7 @@ builds:
|
||||
binary: syft
|
||||
goos: [linux]
|
||||
goarch: [amd64, arm64, ppc64le, riscv64, s390x]
|
||||
tags: [containers_image_openpgp, exclude_graphdriver_btrfs]
|
||||
mod_timestamp: &build-timestamp '{{ .CommitTimestamp }}'
|
||||
ldflags: &build-ldflags |
|
||||
-w
|
||||
@@ -167,16 +168,13 @@ sboms:
|
||||
- "--output"
|
||||
- "json=$document"
|
||||
|
||||
# the signature, certificate, and transparency log proof all land in a single sigstore bundle.
|
||||
signs:
|
||||
- cmd: .tool/cosign
|
||||
signature: "${artifact}.sig"
|
||||
certificate: "${artifact}.pem"
|
||||
signature: "${artifact}.sigstore.json"
|
||||
args:
|
||||
- "sign-blob"
|
||||
- "--use-signing-config=false"
|
||||
- "--oidc-issuer=https://token.actions.githubusercontent.com"
|
||||
- "--output-certificate=${certificate}"
|
||||
- "--output-signature=${signature}"
|
||||
- "--bundle=${signature}"
|
||||
- "${artifact}"
|
||||
- "--yes"
|
||||
artifacts: checksum
|
||||
|
||||
+4
-4
@@ -1,12 +1,12 @@
|
||||
module github.com/anchore/syft/.make
|
||||
|
||||
go 1.25.8
|
||||
go 1.26.0
|
||||
|
||||
require github.com/anchore/go-make v0.8.1
|
||||
require github.com/anchore/go-make v0.9.1
|
||||
|
||||
require (
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
golang.org/x/mod v0.40.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/mod v0.41.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
)
|
||||
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
github.com/anchore/go-make v0.8.1 h1:GAhPXR+9qVQhFB1pfVlHFOzxAt3dTDTUIA7Tp7nx9tg=
|
||||
github.com/anchore/go-make v0.8.1/go.mod h1:xALbVsPsDoJnJxdkjPlxOLZV7GbAdT5Pv3gd0kP1Lz0=
|
||||
github.com/anchore/go-make v0.9.1 h1:OnC830XJEhdHUPhR8jLpnjmLTco9UThNZfHpLefqd94=
|
||||
github.com/anchore/go-make v0.9.1/go.mod h1:1KhqPt0STAHLVEngwrXxYsuFftD62Nx497rNujwo8IY=
|
||||
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.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
|
||||
golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
|
||||
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
|
||||
+31
-15
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -17,6 +16,12 @@ import (
|
||||
"github.com/anchore/go-make/tasks/gotest"
|
||||
)
|
||||
|
||||
// buildTags must match the linux build in .goreleaser.yaml so tests and lint compile the same code that ships
|
||||
// (containers_image_openpgp compiles in stereoscope's real containers-storage provider instead of its stub).
|
||||
// exclude_graphdriver_btrfs drops the cgo-only btrfs driver, which needs libbtrfs headers whenever cgo is on (e.g.
|
||||
// under -race); release builds are CGO_ENABLED=0 and never include it anyway.
|
||||
const buildTags = "containers_image_openpgp,exclude_graphdriver_btrfs"
|
||||
|
||||
func main() {
|
||||
Makefile(
|
||||
// shared anchore tasks
|
||||
@@ -30,6 +35,7 @@ func main() {
|
||||
gotest.Name("unit"),
|
||||
gotest.ExcludeGlob("**/test/**"),
|
||||
gotest.CoverageThreshold(62),
|
||||
gotest.Tags(buildTags),
|
||||
race(),
|
||||
),
|
||||
|
||||
@@ -53,7 +59,7 @@ func main() {
|
||||
raceFlag = " -race"
|
||||
}
|
||||
Run(
|
||||
"go test -count=1 -timeout=30m"+raceFlag+" ./cmd/syft/internal/test/integration/...",
|
||||
"go test -count=1 -timeout=30m -tags="+buildTags+raceFlag+" ./cmd/syft/internal/test/integration/...",
|
||||
run.Env("GODEBUG", "dontfreezetheworld=1"),
|
||||
)
|
||||
},
|
||||
@@ -67,7 +73,7 @@ func main() {
|
||||
Log("race detector disabled (RACE=false); skipping race smoke")
|
||||
return
|
||||
}
|
||||
Run("go run -race cmd/syft/main.go anchore/test_images:grype-quality-dotnet-69f15d2")
|
||||
Run("go run -race -tags="+buildTags+" cmd/syft/main.go anchore/test_images:grype-quality-dotnet-69f15d2")
|
||||
},
|
||||
},
|
||||
|
||||
@@ -97,19 +103,20 @@ func main() {
|
||||
},
|
||||
},
|
||||
|
||||
// windows unit tests: deliberately not hooked into "test" (no RunsOn). The full unit suite leans on
|
||||
// linux docker fixture images and posix-only assumptions, so this covers the packages that own
|
||||
// host path handling (where windows bugs live) and skips the tests that need docker image fixtures.
|
||||
// ponytail: curated package list, widen it as more of the suite is made windows-safe.
|
||||
// containers-storage tests: build a fixture image with BUILDER (podman or buildah) into an isolated local store
|
||||
// and scan it with the snapshot binary. Linux only, and not hooked into "test" since it needs the builder installed.
|
||||
Task{
|
||||
Name: "unit:windows",
|
||||
Description: "run the windows-relevant subset of unit tests",
|
||||
Name: "containers-storage-test",
|
||||
Description: "Run containers-storage tests (BUILDER=podman|buildah)",
|
||||
Run: func() {
|
||||
Run("go test -count=1"+
|
||||
` -skip "Image|Squash|AllLayers|MixFileTypes|GivenCoordinates"`+
|
||||
" ./syft/internal/windows/... ./syft/internal/fileresolver/... ./syft/source/directorysource/... ./syft/source/filesource/..."+
|
||||
" ./syft/file ./syft/file/cataloger/filedigest/... ./syft/file/cataloger/filecontent/...",
|
||||
run.Stdout(os.Stderr),
|
||||
bin := snapshotBinPath()
|
||||
if !file.Exists(bin) {
|
||||
Log("snapshot binary not found at %s; building single-target snapshot", bin)
|
||||
Run("make snapshot:single-target")
|
||||
}
|
||||
Run(
|
||||
"bash test/containers-storage/source-test.sh "+config.Env("BUILDER", "podman"),
|
||||
run.Env("SYFT_BINARY_LOCATION", bin),
|
||||
)
|
||||
},
|
||||
},
|
||||
@@ -157,7 +164,7 @@ func main() {
|
||||
Task{
|
||||
Name: "unit:syft",
|
||||
RunsOn: lang.List("unit"),
|
||||
Dependencies: Deps("refresh-fixtures"),
|
||||
Dependencies: unitFixtureDeps(),
|
||||
},
|
||||
Task{
|
||||
Name: "clean:syft",
|
||||
@@ -185,6 +192,15 @@ func raceEnabled() bool {
|
||||
return config.CI && !config.Windows
|
||||
}
|
||||
|
||||
// unitFixtureDeps skips the fixture cache refresh on windows: it needs binny-managed tools (binny ships
|
||||
// no windows release) and linux docker, so fixture-backed tests there have to skip themselves instead.
|
||||
func unitFixtureDeps() []string {
|
||||
if config.Windows {
|
||||
return nil
|
||||
}
|
||||
return Deps("refresh-fixtures")
|
||||
}
|
||||
|
||||
// race applies raceEnabled() to a gotest suite. gotest exposes no functional option
|
||||
// for the race detector, but gotest.Config.Race is exported.
|
||||
func race() gotest.Option {
|
||||
|
||||
@@ -61,6 +61,18 @@ syft <image> -o cyclonedx-json
|
||||
syft <image> -o spdx-json=./spdx.json -o cyclonedx-json=./cdx.json
|
||||
```
|
||||
|
||||
### Local containers-storage images
|
||||
|
||||
On Linux, syft can scan images from a local containers-storage store (e.g. built with Buildah or Podman) when asked explicitly; plain image references never look there:
|
||||
|
||||
```bash
|
||||
syft --from containers-storage localhost/myimage:latest
|
||||
|
||||
# rootless stores must be read from inside the builder's user namespace
|
||||
podman unshare syft --from containers-storage localhost/myimage:latest
|
||||
```
|
||||
|
||||
This is included in the Linux release binaries. When building from source, add `-tags containers_image_openpgp,exclude_graphdriver_btrfs`.
|
||||
|
||||
> [!TIP]
|
||||
> **Check out the [Getting Started guide](https://oss.anchore.com/docs/guides/sbom/getting-started/)** to explore all of the capabilities and features.
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/syft/internal/redact"
|
||||
)
|
||||
|
||||
func TestCommandCanRunMultipleTimes(t *testing.T) {
|
||||
// https://github.com/anchore/syft/issues/2285
|
||||
t.Cleanup(redact.Reset)
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
_, cmd := create(clio.Identification{Name: "syft"}, io.Discard)
|
||||
cmd.SetArgs([]string{"cataloger", "list", "-o", "json"})
|
||||
require.NoError(t, cmd.Execute())
|
||||
|
||||
// the store is released at the end of each run so the next one can set its own
|
||||
require.Nil(t, redact.Get())
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,8 @@ func AppClioSetupConfig(id clio.Identification, out io.Writer) *clio.SetupConfig
|
||||
stereoscope.SetBus(state.Bus)
|
||||
bus.Set(state.Bus)
|
||||
|
||||
// the redact store is process-global and only cleared when a run ends (see the post-run below), so
|
||||
// setting it while another run is still in flight panics rather than splitting secrets across stores.
|
||||
redact.Set(state.RedactStore)
|
||||
|
||||
log.Set(state.Logger)
|
||||
@@ -48,8 +50,14 @@ func AppClioSetupConfig(id clio.Identification, out io.Writer) *clio.SetupConfig
|
||||
return nil
|
||||
},
|
||||
).
|
||||
WithPostRuns(func(_ *clio.State, _ error) {
|
||||
WithPostRuns(func(state *clio.State, _ error) {
|
||||
stereoscope.Cleanup() //nolint:staticcheck // we don't have access to the image object here
|
||||
|
||||
// release the redact store now that nothing in this run can report anymore, so a later run in the same
|
||||
// process (e.g. syft embedded as a library) can set its own. Only release the store this run set.
|
||||
if redact.Get() == state.RedactStore {
|
||||
redact.Reset()
|
||||
}
|
||||
})
|
||||
return clioCfg
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/go-logger/adapter/discard"
|
||||
gologgerredact "github.com/anchore/go-logger/adapter/redact"
|
||||
"github.com/anchore/syft/internal/redact"
|
||||
)
|
||||
|
||||
func TestAppClioSetupConfigInitializerPanicsWhileRunInFlight(t *testing.T) {
|
||||
t.Cleanup(redact.Reset)
|
||||
|
||||
cfg := AppClioSetupConfig(clio.Identification{Name: "syft"}, io.Discard)
|
||||
require.Len(t, cfg.Initializers, 1)
|
||||
|
||||
newState := func() *clio.State {
|
||||
return &clio.State{Logger: discard.New(), RedactStore: gologgerredact.NewStore()}
|
||||
}
|
||||
|
||||
require.NoError(t, cfg.Initializers[0](newState()))
|
||||
|
||||
// a second run starting before the first has finished must not silently replace the store, otherwise
|
||||
// secrets added to the first store would no longer be redacted
|
||||
require.PanicsWithValue(t, "replace existing redaction store (probably unintentional)", func() {
|
||||
_ = cfg.Initializers[0](newState())
|
||||
})
|
||||
}
|
||||
@@ -114,6 +114,10 @@ func RunConvert(ctx context.Context, opts *ConvertOptions, userInput string) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// output files are opened up front; release them if we bail out before writing
|
||||
if c, ok := writer.(io.Closer); ok {
|
||||
defer func() { _ = c.Close() }()
|
||||
}
|
||||
}
|
||||
|
||||
for _, output := range unchanged {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -181,6 +182,10 @@ func runScan(ctx context.Context, id clio.Identification, opts *scanOptions, use
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// output files are opened up front; release them if we bail out before writing
|
||||
if c, ok := writer.(io.Closer); ok {
|
||||
defer func() { _ = c.Close() }()
|
||||
}
|
||||
|
||||
sources := opts.From
|
||||
if len(sources) == 0 {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -22,9 +23,10 @@ func Test_defaultDir(t *testing.T) {
|
||||
homeDir := filepath.Join(tmpDir, "fake-home")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
env map[string]string
|
||||
expected string
|
||||
name string
|
||||
env map[string]string
|
||||
expected string
|
||||
skipOnWindows bool
|
||||
}{
|
||||
{
|
||||
name: "no-xdg",
|
||||
@@ -32,6 +34,8 @@ func Test_defaultDir(t *testing.T) {
|
||||
"HOME": homeDir,
|
||||
},
|
||||
expected: homeDir,
|
||||
// xdg falls back to LOCALAPPDATA on windows, not HOME
|
||||
skipOnWindows: true,
|
||||
},
|
||||
{
|
||||
name: "xdg-cache",
|
||||
@@ -59,6 +63,9 @@ func Test_defaultDir(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if test.skipOnWindows && runtime.GOOS == "windows" {
|
||||
t.Skip("posix-only cache dir fallback")
|
||||
}
|
||||
restoreCache(t)
|
||||
|
||||
for k, v := range test.env {
|
||||
|
||||
@@ -36,7 +36,7 @@ var _ clio.PostLoader = (*imageSource)(nil)
|
||||
func (o *sourceConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&o.File.Digests, `the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")`)
|
||||
descriptions.Add(&o.Image.DefaultPullSource, `allows users to specify which image source should be used to generate the sbom
|
||||
valid values are: registry, docker, podman`)
|
||||
valid values are: registry, docker, podman, containers-storage`)
|
||||
}
|
||||
|
||||
type imageSource struct {
|
||||
@@ -74,7 +74,7 @@ func (c *imageSource) PostLoad() error {
|
||||
return checkDefaultSourceValues(c.DefaultPullSource)
|
||||
}
|
||||
|
||||
var validDefaultSourceValues = []string{"registry", "docker", "podman", ""}
|
||||
var validDefaultSourceValues = []string{"registry", "docker", "podman", "containers-storage", ""}
|
||||
|
||||
func checkDefaultSourceValues(source string) error {
|
||||
validValues := strset.New(validDefaultSourceValues...)
|
||||
|
||||
@@ -2,10 +2,11 @@ package options
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -20,7 +21,10 @@ import (
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
var _ sbom.Writer = (*sbomMultiWriter)(nil)
|
||||
var _ interface {
|
||||
io.Closer
|
||||
sbom.Writer
|
||||
} = (*sbomMultiWriter)(nil)
|
||||
|
||||
var _ interface {
|
||||
io.Closer
|
||||
@@ -164,6 +168,12 @@ func newSBOMMultiWriter(options ...sbomWriterDescription) (_ *sbomMultiWriter, e
|
||||
}
|
||||
|
||||
out := &sbomMultiWriter{}
|
||||
defer func() {
|
||||
// don't leak the files already opened when a later output fails
|
||||
if err != nil {
|
||||
_ = out.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for _, option := range options {
|
||||
switch len(option.Path) {
|
||||
@@ -172,8 +182,8 @@ func newSBOMMultiWriter(options ...sbomWriterDescription) (_ *sbomMultiWriter, e
|
||||
format: option.Format,
|
||||
})
|
||||
default:
|
||||
// create any missing subdirectories
|
||||
dir := path.Dir(option.Path)
|
||||
// create any missing subdirectories (option.Path is a host path, so this must be filepath)
|
||||
dir := filepath.Dir(option.Path)
|
||||
if dir != "" {
|
||||
s, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
@@ -210,6 +220,20 @@ func (m *sbomMultiWriter) Write(s sbom.SBOM) (errs error) {
|
||||
return errs
|
||||
}
|
||||
|
||||
// Close releases any files held by the writers. Write already closes each file it writes to, so this matters
|
||||
// when the SBOM is never written (e.g. an error after the writer was created).
|
||||
func (m *sbomMultiWriter) Close() error {
|
||||
var errs error
|
||||
for _, w := range m.writers {
|
||||
if c, ok := w.(io.Closer); ok {
|
||||
if err := c.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// sbomStreamWriter implements sbom.Writer for a given format and io.Writer, also providing a close function for cleanup
|
||||
type sbomStreamWriter struct {
|
||||
format sbom.FormatEncoder
|
||||
|
||||
@@ -202,9 +202,9 @@ func Test_newSBOMMultiWriter(t *testing.T) {
|
||||
if test.err {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = mw.Close() })
|
||||
|
||||
assert.Len(t, mw.writers, len(test.expected))
|
||||
|
||||
|
||||
@@ -524,4 +524,12 @@ var commonTestCases = []testCase{
|
||||
"kong": "3.7.0-0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find cpan distribution",
|
||||
pkgType: pkg.CpanPkg,
|
||||
pkgLanguage: pkg.Perl,
|
||||
pkgInfo: map[string]string{
|
||||
"URI": "5.35",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"abstract": "Uniform Resource Identifiers (absolute and relative)",
|
||||
"license": ["perl_5"],
|
||||
"meta-spec": {"version": 2},
|
||||
"name": "URI",
|
||||
"prereqs": {"runtime": {"requires": {"Carp": "0", "perl": "5.008001"}}},
|
||||
"release_status": "stable",
|
||||
"version": "5.35"
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"dist": "URI-5.35",
|
||||
"name": "URI",
|
||||
"pathname": "O/OA/OALDERS/URI-5.35.tar.gz",
|
||||
"provides": {
|
||||
"URI": {"file": "lib/URI.pm", "version": "5.35"},
|
||||
"URI::Escape": {"file": "lib/URI/Escape.pm", "version": "5.35"}
|
||||
},
|
||||
"target": "URI",
|
||||
"version": "5.35"
|
||||
}
|
||||
@@ -18,16 +18,16 @@ require (
|
||||
github.com/anchore/go-homedir v0.1.1
|
||||
github.com/anchore/go-logger v0.2.0
|
||||
github.com/anchore/go-macholibre v0.1.1
|
||||
github.com/anchore/go-rpmdb v0.2.0
|
||||
github.com/anchore/go-rpmdb v0.2.2
|
||||
github.com/anchore/go-sync v0.1.2
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/packageurl-go v0.2.0
|
||||
github.com/anchore/stereoscope v0.3.2
|
||||
github.com/anchore/stereoscope v0.3.4
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1
|
||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.2
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/charmbracelet/bubbles v1.0.0
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
@@ -41,7 +41,7 @@ require (
|
||||
github.com/facebookincubator/nvdtools v0.1.5
|
||||
github.com/github/go-spdx/v2 v2.7.0
|
||||
github.com/gkampitakis/go-snaps v0.5.23
|
||||
github.com/go-git/go-billy/v5 v5.9.1
|
||||
github.com/go-git/go-billy/v5 v5.9.2
|
||||
github.com/go-git/go-git/v5 v5.19.2
|
||||
github.com/go-test/deep v1.1.1
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||
@@ -52,7 +52,7 @@ require (
|
||||
github.com/google/licensecheck v0.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gookit/color v1.6.1
|
||||
github.com/gpustack/gguf-parser-go v0.26.3
|
||||
github.com/gpustack/gguf-parser-go v0.26.4
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2
|
||||
github.com/hashicorp/go-getter v1.8.9
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
@@ -62,7 +62,7 @@ require (
|
||||
github.com/jedib0t/go-pretty/v6 v6.8.3
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||
github.com/klauspost/compress v1.20.0
|
||||
github.com/klauspost/compress v1.20.1
|
||||
github.com/magiconair/properties v1.18.12
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/moby/sys/mountinfo v0.7.2
|
||||
@@ -83,7 +83,7 @@ require (
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/ulikunitz/xz v0.5.16
|
||||
github.com/ulikunitz/xz v0.5.17
|
||||
github.com/vbatts/go-mtree v0.7.0
|
||||
github.com/vifraa/gopom v1.0.0
|
||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
|
||||
@@ -97,11 +97,11 @@ require (
|
||||
golang.org/x/net v0.59.0
|
||||
golang.org/x/time v0.16.0
|
||||
golang.org/x/tools v0.50.0
|
||||
modernc.org/sqlite v1.59.0
|
||||
modernc.org/sqlite v1.60.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1
|
||||
github.com/pb33f/ordered-map/v2 v2.3.2
|
||||
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd
|
||||
howett.net/plist v1.0.1
|
||||
)
|
||||
@@ -115,6 +115,7 @@ require (
|
||||
cloud.google.com/go/iam v1.12.0 // indirect
|
||||
cloud.google.com/go/monitoring v1.30.0 // indirect
|
||||
cloud.google.com/go/storage v1.65.1 // indirect
|
||||
cyphar.com/go-pathrs v0.2.5 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 // indirect
|
||||
@@ -122,9 +123,10 @@ require (
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 // indirect
|
||||
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/Microsoft/hcsshim v0.15.0-rc.4 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.1 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/agext/levenshtein v1.2.1 // indirect
|
||||
github.com/anchore/go-lzo v0.1.1 // indirect
|
||||
github.com/anchore/go-struct-converter v0.2.0-rc2 // indirect
|
||||
@@ -169,20 +171,25 @@ require (
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // 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.6 // indirect
|
||||
github.com/containerd/containerd/api v1.12.0 // indirect
|
||||
github.com/containerd/containerd/v2 v2.4.1 // indirect
|
||||
github.com/containerd/continuity v0.5.0 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/log v0.2.0 // indirect
|
||||
github.com/containerd/log/otel v0.1.0 // indirect
|
||||
github.com/containerd/platforms v1.0.0-rc.5 // indirect
|
||||
github.com/containerd/plugin v1.1.0 // 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/docker/cli v29.8.0+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.5 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
|
||||
github.com/containerd/ttrpc v1.2.9 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.3.0 // indirect
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 // indirect
|
||||
github.com/containers/ocicrypt v1.3.2 // indirect
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.7.0 // indirect
|
||||
github.com/docker/cli v29.8.2+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.8 // indirect
|
||||
github.com/docker/go-connections v0.8.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
@@ -192,17 +199,17 @@ require (
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/fatih/color v1.19.0 // indirect
|
||||
github.com/felixge/fgprof v0.9.5 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.1.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.15 // 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-jose/go-jose/v4 v4.1.5 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-restruct/restruct v1.2.0-alpha // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/google/go-intervals v0.0.2 // indirect
|
||||
github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.20 // indirect
|
||||
@@ -226,10 +233,13 @@ require (
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.24 // indirect
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.27 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.48 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/miekg/pkcs11 v1.1.2 // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/minlz v1.0.1 // indirect
|
||||
github.com/mistifyio/go-zfs/v4 v4.0.0 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
@@ -238,10 +248,10 @@ require (
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/moby/api v1.56.0 // indirect
|
||||
github.com/moby/moby/client v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/capability v0.4.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.1 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/sys/user v0.4.1 // indirect
|
||||
github.com/moby/sys/userns v0.2.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
@@ -254,6 +264,8 @@ require (
|
||||
github.com/olekukonko/ll v0.1.6 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.3.0 // indirect
|
||||
github.com/opencontainers/selinux v1.15.1 // indirect
|
||||
github.com/pb33f/go-yaml v0.1.0 // indirect
|
||||
github.com/pborman/indent v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.26 // indirect
|
||||
@@ -264,47 +276,59 @@ require (
|
||||
github.com/pkg/xattr v0.4.12 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/proglottis/gpgme v0.1.6 // 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
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sahilm/fuzzy v0.1.1 // indirect
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.11.1 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/sigstore/fulcio v1.8.7 // indirect
|
||||
github.com/sigstore/protobuf-specs v0.5.2 // indirect
|
||||
github.com/sigstore/sigstore v1.10.11 // indirect
|
||||
github.com/sirupsen/logrus v1.10.2 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d // indirect
|
||||
github.com/smallstep/pkcs7 v0.2.1 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
|
||||
github.com/spf13/cast v1.10.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/spf13/viper v1.21.0 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.7.0 // indirect
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 // indirect
|
||||
github.com/stretchr/objx v0.5.3 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/sylabs/sif/v2 v2.24.1 // indirect
|
||||
github.com/sylabs/sif/v2 v2.24.2 // indirect
|
||||
github.com/sylabs/squashfs v1.0.6 // indirect
|
||||
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // 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.3 // indirect
|
||||
github.com/vbauerster/cupwriter v0.0.4 // indirect
|
||||
github.com/vbauerster/mpb/v8 v8.14.0 // 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
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/zclconf/go-cty v1.19.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
|
||||
go.opentelemetry.io/otel v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.45.0 // indirect
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 // indirect
|
||||
go.opentelemetry.io/otel v1.46.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.46.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.46.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.46.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.46.0 // indirect
|
||||
go.podman.io/image/v5 v5.41.2 // indirect
|
||||
go.podman.io/storage v1.64.1 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.57.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
@@ -316,12 +340,12 @@ require (
|
||||
gonum.org/v1/gonum v0.17.0 // indirect
|
||||
google.golang.org/api v0.294.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect
|
||||
google.golang.org/grpc v1.83.2 // indirect
|
||||
google.golang.org/protobuf v1.36.12 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
modernc.org/libc v1.75.7 // indirect
|
||||
modernc.org/libc v1.77.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.12.1 // indirect
|
||||
)
|
||||
|
||||
@@ -67,6 +67,8 @@ cloud.google.com/go/storage v1.65.1 h1:LRRpBJUTf+OXDPX9jZUKZ3mSLIsz3htG+qUpeNZov
|
||||
cloud.google.com/go/storage v1.65.1/go.mod h1:UsS9OgFg/XHOSYakQ8ZtLWWeyGkk1WnmD/GsGfN0BHM=
|
||||
cloud.google.com/go/trace v1.16.0 h1:GmQovzFc5F0CNfl0VLgL64aoTtu7xsM0YajW2GlG9+E=
|
||||
cloud.google.com/go/trace v1.16.0/go.mod h1:r+bdAn16dKLSV1G2D5v3e58IlQlizfxWrUfjx7kM7X0=
|
||||
cyphar.com/go-pathrs v0.2.5 h1:SnX9FBvnoyn3lUs1dkMgZ52bAETpirNu3FTRh5HlRik=
|
||||
cyphar.com/go-pathrs v0.2.5/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
@@ -99,8 +101,8 @@ github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSC
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 h1:0kQAzHq8vLs7Pptv+7TxjdETLf/nIqJpIB4oC6Ba4vY=
|
||||
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
|
||||
github.com/Microsoft/hcsshim v0.15.0-rc.1 h1:FbbwtQmiD+BVHynGkx5S65JkLyhkEiiTP8nrpmg2SZw=
|
||||
github.com/Microsoft/hcsshim v0.15.0-rc.1/go.mod h1:HWvvUPIy9HF6LotILj1G4VyS065rcLQ6tqj6tMUdOfI=
|
||||
github.com/Microsoft/hcsshim v0.15.0-rc.4 h1:aZFX4LH0S20Lgjq0wG61StIClj7im4yzrxIClkaR8Z8=
|
||||
github.com/Microsoft/hcsshim v0.15.0-rc.4/go.mod h1:BA9CBztgu4h/6Jsvo1O1M4qjWw09PoYpaEYgexPE578=
|
||||
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=
|
||||
@@ -108,6 +110,8 @@ github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67
|
||||
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/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
|
||||
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
|
||||
@@ -136,8 +140,8 @@ 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.2.0 h1:ixcyMg/guhBL9XjBVwhTv9CG48xO07Qkg2uDX2+zx/c=
|
||||
github.com/anchore/go-rpmdb v0.2.0/go.mod h1:ATsRlpCXstnoYfzqBfhwGw0U2dolx1BBQ9rAU8jWBAw=
|
||||
github.com/anchore/go-rpmdb v0.2.2 h1:44VC7mvFAxV9t5JUZYg3S9sqG+E94iZvyWACMDq9hSk=
|
||||
github.com/anchore/go-rpmdb v0.2.2/go.mod h1:Y64EbetdqhryMjtk8IzXkdCQw/aNDx+feZCT9aj6rjs=
|
||||
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.2 h1:RDD5RCanrWqhbtBulqPSZpERadUlgcYjpybYzYd3Uk8=
|
||||
@@ -146,8 +150,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T/EAErk=
|
||||
github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE=
|
||||
github.com/anchore/stereoscope v0.3.2 h1:A2FDMSU56m3rErO1e0CtWAVcDS8tc2iX4UXTw1hyPnI=
|
||||
github.com/anchore/stereoscope v0.3.2/go.mod h1:/IUDVDka+4gufc8kDw+JO3gFl9PkpCjJ2r3bd9UWouM=
|
||||
github.com/anchore/stereoscope v0.3.4 h1:dT5AfjCTXUJowMiRIc+VBQVwvwnIEwnxxwA1GwAkMxw=
|
||||
github.com/anchore/stereoscope v0.3.4/go.mod h1:+HxqHTt7dvH8mea0mf3ulqkf084VvHiT1grpAQf5uEs=
|
||||
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=
|
||||
@@ -225,8 +229,8 @@ github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef h1:TSFnfbbu2oAO
|
||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb h1:m935MPodAbYS46DG4pJSv7WO+VECIWUQ7OJYSoTrMh4=
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb/go.mod h1:PkYb9DJNAwrSvRx5DYA+gUcOIgTGVMNkfSCbZM8cWpI=
|
||||
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/bmatcuk/doublestar/v4 v4.10.2 h1:eF7W7HWKg3z9NrWV9pTLnNeoXaqq3Tq9DNKXVMfoCnw=
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.2/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
|
||||
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
|
||||
github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4=
|
||||
@@ -294,10 +298,10 @@ github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
|
||||
github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ=
|
||||
github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
|
||||
github.com/containerd/containerd/api v1.11.1 h1:h8nfoDW9+fNsC/9TwiAHj8B1GzXKtR4eFtkhi/X5RLU=
|
||||
github.com/containerd/containerd/api v1.11.1/go.mod h1:CaQFRu+N1MtbgL6JDOJLUB1hCKESU1lD6MuTJhgtdlw=
|
||||
github.com/containerd/containerd/v2 v2.3.6 h1:huoqZXaDW1x1kgPvFC4rMwxv92vUJnqgq7I5b1rSddM=
|
||||
github.com/containerd/containerd/v2 v2.3.6/go.mod h1:qaUSWKk9EchqbudSbhv1MxI8dXKGYHLpZtMQdkEkVZ4=
|
||||
github.com/containerd/containerd/api v1.12.0 h1:kuQm82SbDrCuO4n7hf2L8zsBtZLuympyq5X/VotfX2A=
|
||||
github.com/containerd/containerd/api v1.12.0/go.mod h1:EBcSzoi9Vl18cdODaXUCskf3D2NT8lsSXeZJnU5jIUc=
|
||||
github.com/containerd/containerd/v2 v2.4.1 h1:DUx/ZJN7cEu0WuzHClDB+68H/bqMEH5pWoEjf0ae4hc=
|
||||
github.com/containerd/containerd/v2 v2.4.1/go.mod h1:vgLdtvl3prFk1d3ZVqQcsDD5Q9IKM+vAIdU54U85w+I=
|
||||
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=
|
||||
@@ -306,23 +310,33 @@ github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151X
|
||||
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
|
||||
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
|
||||
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/log v0.2.0 h1:BewD/umNgVnoczglOpX8eRMyEy5t5iPlu5AIpnWDONc=
|
||||
github.com/containerd/log v0.2.0/go.mod h1:/M7L7CXKcPTfNC74XzaK+5H5KbO5+4lJVpuVI6vRLoM=
|
||||
github.com/containerd/log/otel v0.1.0 h1:Az5rFFo0+c4v2yC8ROR63sWsZpKl/vhtUnUhqLSpE6U=
|
||||
github.com/containerd/log/otel v0.1.0/go.mod h1:65C5iYF2xIQByCyxzoO2KKKK1+/tGxD4XqhdlrTtvzE=
|
||||
github.com/containerd/platforms v1.0.0-rc.5 h1:vXd569rDrz8LeMXzAnBsy6LADV5YtsD8oyaRarxdmSU=
|
||||
github.com/containerd/platforms v1.0.0-rc.5/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/ttrpc v1.2.8 h1:xbVu6D4qF2jihdh9rDVOKqUMiFBQk6YctTdo1zk087Y=
|
||||
github.com/containerd/ttrpc v1.2.8/go.mod h1:wyZW2K79t4Hfcxl+GUvkZqRBzJlqFFvgEeeWXa42tyE=
|
||||
github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40=
|
||||
github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk=
|
||||
github.com/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.9 h1:ha0ak962T0s3CA/RoZ6S6xiWZQF24GrBaEpiGX1uihg=
|
||||
github.com/containerd/ttrpc v1.2.9/go.mod h1:jjtQRwXm4DL3KsHKW8vDiUOV6wO0hi6IPhmJhxU7aEs=
|
||||
github.com/containerd/typeurl/v2 v2.3.0 h1:HZHPhRWo5XMy3QGQoPrUzbW/2ckwjfweHmOwlkIrPAQ=
|
||||
github.com/containerd/typeurl/v2 v2.3.0/go.mod h1:Qk+PAdUYArVj41TnGi6rJ+48RF0PkcTc4i/taoBcK0w=
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
|
||||
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
|
||||
github.com/containers/ocicrypt v1.3.2 h1:MuqHSfiPpGzoAQdgDSX85FgixSgIm9mSDXTLTgugY5E=
|
||||
github.com/containers/ocicrypt v1.3.2/go.mod h1:ntBZabYG0rlvstB/1rK/ba2laaU5EHE0pD5ioHoPTq4=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q=
|
||||
github.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=
|
||||
github.com/cyphar/filepath-securejoin v0.7.0 h1:s0Y3ITPy6sQn5xt54DuYvTF8hu134ooYLUb58DX/HjE=
|
||||
github.com/cyphar/filepath-securejoin v0.7.0/go.mod h1:ymLGms/u3BYaviIiuKFnUx8EkQEZeK6cInNoAPJA3o4=
|
||||
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
|
||||
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -339,10 +353,12 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||
github.com/docker/cli v29.8.0+incompatible h1:ih0c2jq/nN7QfES8zIfwSzIhRScjs4ehER+kZ60aeSk=
|
||||
github.com/docker/cli v29.8.0+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/cli v29.8.2+incompatible h1:2zgdFuoFst2T80oS42vhKGxkd0JRo305eIEKTsxR7pQ=
|
||||
github.com/docker/cli v29.8.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker-credential-helpers v0.9.8 h1:bIREROb7So6PRlq6KTtdS9MPEjC29OQRkFNlvK2OX8Q=
|
||||
github.com/docker/docker-credential-helpers v0.9.8/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go-connections v0.8.1 h1:JibmG5hULs5qXSr/cp/w3Pw5fZuStt4MOHMUExb29/M=
|
||||
github.com/docker/go-connections v0.8.1/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@@ -381,8 +397,8 @@ github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMD
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/erofs/go-erofs v0.3.0 h1:o/W5ABAA3sHYl97WL93dacKEfeDpJhdFf3c2snAti7I=
|
||||
github.com/erofs/go-erofs v0.3.0/go.mod h1:XkSeN9MHszGd4+3gcEjadJLYHCQpWzJ7/8yznzMuzJs=
|
||||
github.com/erofs/go-erofs v0.3.1 h1:Sux82Jq9yvyYhIoLgSHDp741p/+370HsOj9dAh1+VVs=
|
||||
github.com/erofs/go-erofs v0.3.1/go.mod h1:XkSeN9MHszGd4+3gcEjadJLYHCQpWzJ7/8yznzMuzJs=
|
||||
github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk=
|
||||
github.com/facebookincubator/nvdtools v0.1.5 h1:jbmDT1nd6+k+rlvKhnkgMokrCAzHoASWE5LtHbX2qFQ=
|
||||
github.com/facebookincubator/nvdtools v0.1.5/go.mod h1:Kh55SAWnjckS96TBSrXI99KrEKH4iB0OJby3N8GRJO4=
|
||||
@@ -396,13 +412,13 @@ github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0C
|
||||
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
||||
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
|
||||
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
|
||||
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
|
||||
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@@ -412,14 +428,14 @@ github.com/gkampitakis/ciinfo v0.3.4 h1:5eBSibVuSMbb/H6Elc0IIEFbkzCJi3lm94n0+U7Z
|
||||
github.com/gkampitakis/ciinfo v0.3.4/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-snaps v0.5.23 h1:okh5QR48zpUjpWtu65AtqxdCY8huJq+dEDuUzd1PuKg=
|
||||
github.com/gkampitakis/go-snaps v0.5.23/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/glebarez/go-sqlite v1.23.0 h1:FyhIq4jqmgphQAUlY79zPldYGwISEZikaDfhiGWkkaI=
|
||||
github.com/glebarez/go-sqlite v1.23.0/go.mod h1:IIYrOH3L0rHY3jb4IXOHoWdklNajSGUN2eJcvK8WrnI=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.9.1 h1:8U73XiOTfINdItHVa6z4Gv7ToObcZ6grkqQbLryLCdA=
|
||||
github.com/go-git/go-billy/v5 v5.9.1/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
|
||||
github.com/go-git/go-billy/v5 v5.9.2 h1:OXFSRyz4g20upsGDJgQG9Bak1l/ZEv8GHVYB52O71sE=
|
||||
github.com/go-git/go-billy/v5 v5.9.2/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.19.2 h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY=
|
||||
@@ -427,8 +443,8 @@ github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuw
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-jose/go-jose/v4 v4.1.5 h1:RjgjO2LOtWOJKUC5wpwY9LR3B3vwVAz6JS2YHfYU6eA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.5/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
@@ -453,7 +469,6 @@ github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/gohugoio/hashstructure v0.6.0 h1:7wMB/2CfXoThFYhdWRGv3u3rUM761Cq29CxUW+NltUg=
|
||||
github.com/gohugoio/hashstructure v0.6.0/go.mod h1:lapVLk9XidheHG1IQ4ZSbyYrXcaILU1ZEP/+vno5rBQ=
|
||||
@@ -507,10 +522,13 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
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.22.1 h1:RZuuSYhTvlDvtsK+NkutoCZ//C0X2ebLK8X8l3ULs84=
|
||||
github.com/google/go-containerregistry v0.22.1/go.mod h1:bJR35SK8XgisYmhg/FMQ/5RK0S/XrOAqLBV5/LR2XE0=
|
||||
github.com/google/go-intervals v0.0.2 h1:FGrVEiUnTRKR8yE04qzXYaJMtnIYqobR5QbblK3ixcM=
|
||||
github.com/google/go-intervals v0.0.2/go.mod h1:MkaR3LNRfeKLPmqgJYs4E66z5InYjmCjbbr4TQlcT6Y=
|
||||
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=
|
||||
@@ -543,6 +561,7 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
||||
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
||||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.20 h1:t/xL64VUoN69MuMRQuJETqYGOw4Z9mSRJK9epIEtwFk=
|
||||
@@ -558,8 +577,10 @@ github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj
|
||||
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
||||
github.com/gookit/color v1.6.1 h1:KoTnDxJPRgrL0SoX0f8rCFg2zI0t4E3GZZBMo2nN8LU=
|
||||
github.com/gookit/color v1.6.1/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
|
||||
github.com/gpustack/gguf-parser-go v0.26.3 h1:F63PlUPIW56HGU5k4Crv0JAG9ojtz/TuwzgMYlb82Ec=
|
||||
github.com/gpustack/gguf-parser-go v0.26.3/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gpustack/gguf-parser-go v0.26.4 h1:B9CRrQBcIB7I/MJFZvzPZ+e+cbc1xBzQJcmpi9l/mKU=
|
||||
github.com/gpustack/gguf-parser-go v0.26.4/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.74 h1:mymLUKThnV9wFvogOK8NnsMP9/vlhnjXY98gr2QIGW8=
|
||||
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.74/go.mod h1:Bh9qYL8ehmDxSg14Tk8oxFCP90XHfs6NxV1D84884xA=
|
||||
@@ -645,8 +666,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.20.0 h1:a3C1ke2ohxFymNlb2HWAHjDeKCI90scRskErZkR0ezA=
|
||||
github.com/klauspost/compress v1.20.0/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
github.com/klauspost/compress v1.20.1 h1:T7kKElXUMXrUJ2E9QhQhxFtcK5rPyLdsGZvdbLMPdiQ=
|
||||
github.com/klauspost/compress v1.20.1/go.mod h1:LUdAzn7YLVvxLpc7y3V1m40wESHTgc1422pwwBSKYuI=
|
||||
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=
|
||||
@@ -692,8 +713,10 @@ github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsRe
|
||||
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
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.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
|
||||
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-runewidth v0.0.27 h1:Feg/Oou5zI/wnpgDF6omIU0OokC9GxLC/WRknhVlIR0=
|
||||
github.com/mattn/go-runewidth v0.0.27/go.mod h1:3qAiGCV4Koz/yuveO58qUefmUTRm8r0IGEXZ9jeHp/8=
|
||||
github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs=
|
||||
github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
|
||||
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=
|
||||
@@ -702,10 +725,14 @@ github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPp
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/miekg/pkcs11 v1.1.2 h1:/VxmeAX5qU6Q3EwafypogwWbYryHFmF2RpkJmw3m4MQ=
|
||||
github.com/miekg/pkcs11 v1.1.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
|
||||
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
|
||||
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
|
||||
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
github.com/mistifyio/go-zfs/v4 v4.0.0 h1:sU0+5dX45tdDK5xNZ3HBi95nxUc48FS92qbIZEvpAg4=
|
||||
github.com/mistifyio/go-zfs/v4 v4.0.0/go.mod h1:weotFtXTHvBwhr9Mv96KYnDkTPBOHFUbm9cBmQpesL0=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
|
||||
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
|
||||
@@ -727,16 +754,16 @@ github.com/moby/moby/api v1.56.0 h1:GQzua3NA599ASSIICx0iFgiJeO9YkdDARvQsm23ZZuQ=
|
||||
github.com/moby/moby/api v1.56.0/go.mod h1:sZ+THbVWkjOmBPPfbnzdD/G1LuIexWhqlSHHPTDQ1Uk=
|
||||
github.com/moby/moby/client v0.6.0 h1:AJjEB21QPbXSXjDsZorFBoDZPhMrfbpaPLgSMAW9Bgs=
|
||||
github.com/moby/moby/client v0.6.0/go.mod h1:OCo00wNRyA3m4lmJ228W3JbyCN4ZNNYjpOXiJydBdcQ=
|
||||
github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk=
|
||||
github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I=
|
||||
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=
|
||||
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
|
||||
github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0=
|
||||
github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8=
|
||||
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
|
||||
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/sys/user v0.4.1 h1:RgjRlaDKi/Xmyrz4t8lyzXT6v2ooFeO/7xtchmhVWE0=
|
||||
github.com/moby/sys/user v0.4.1/go.mod h1:E9QsW5WRe1kUAf7kW8hXKwu1uhsZEAdPLYHYSDudF4Y=
|
||||
github.com/moby/sys/userns v0.2.1 h1:4OvdM7BcPkASbuouHsbW3aeMJSFlYDldBRnXVZhaRk8=
|
||||
github.com/moby/sys/userns v0.2.1/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -776,11 +803,15 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=
|
||||
github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.15.1 h1:ERxeh5caJvCzNAKdI8WQbJmB1LDTn4BuaAg8wihLBpA=
|
||||
github.com/opencontainers/selinux v1.15.1/go.mod h1:LenyElirjUHszfxrjuFqC85HIeXZKumHcKMQtnaDlQQ=
|
||||
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/pb33f/go-yaml v0.1.0 h1:zZ6Gg+ATbumXlXZNLMHIe7+zTkVqe20lx+TSYRrXEuY=
|
||||
github.com/pb33f/go-yaml v0.1.0/go.mod h1:QvWdkHP5VjFYAeHMQUkaPQqREaQnBNOXmWzAjNrnSdo=
|
||||
github.com/pb33f/ordered-map/v2 v2.3.2 h1:wDyaZ2Pv9QLh64X4utCeD5Zoi9nIv2aW3lwv172O5PQ=
|
||||
github.com/pb33f/ordered-map/v2 v2.3.2/go.mod h1:1OhFrXu3OYw3kM+FXF+Ug3ugmmZ9LE8z0JfQMLvtV0w=
|
||||
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=
|
||||
@@ -814,6 +845,8 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
|
||||
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/proglottis/gpgme v0.1.6 h1:8WpQ8VWggLdxkuTnW+sZ1r1t92XBNd8GZNDhQ4Rz+98=
|
||||
github.com/proglottis/gpgme v0.1.6/go.mod h1:5LoXMgpE4bttgwwdv9bLs/vwqv3qV7F4glEEZ7mRKrM=
|
||||
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=
|
||||
@@ -826,8 +859,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
|
||||
github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
@@ -853,6 +886,8 @@ github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg=
|
||||
github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
|
||||
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU=
|
||||
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
|
||||
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
|
||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
|
||||
@@ -860,21 +895,31 @@ github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd7
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sebdah/goldie/v2 v2.8.0 h1:dZb9wR8q5++oplmEiJT+U/5KyotVD+HNGCAc5gNr8rc=
|
||||
github.com/sebdah/goldie/v2 v2.8.0/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.11.1 h1:ayahDPjfSIqKegyt5YVGEvQ7SAi72GHXTy+b2YjUd5w=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.11.1/go.mod h1:UyOjhoZLi76ir63u1ptubEevVe6dlNPmNxwgVvKqFFY=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/sigstore/fulcio v1.8.7 h1:d7QJQxukgXarqPCT9uDNeyzt1Bbt/biw58/VfOnwI/o=
|
||||
github.com/sigstore/fulcio v1.8.7/go.mod h1:7fX+QyigZxRGKTgHvTfOfxwwniCDxK02PVloHy5/vg4=
|
||||
github.com/sigstore/protobuf-specs v0.5.2 h1:RSWWUY8QrVTxbYH00jY/jg2e7YnjzrpwP+PeHTMll0E=
|
||||
github.com/sigstore/protobuf-specs v0.5.2/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVLGEQ6o2FovN2Rats=
|
||||
github.com/sigstore/sigstore v1.10.11 h1:fjrwDfR0AWmRONYV/hKGJyqbT0osMtaBz9FTZ6MH9XI=
|
||||
github.com/sigstore/sigstore v1.10.11/go.mod h1:ecPk+ftLqP8/TmzQFJmn0+QrL2yUQIJvyzVKNmDiw6U=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo=
|
||||
github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d h1:3VwvTjiRPA7cqtgOWddEL+JrcijMlXUmj99c/6YyZoY=
|
||||
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d/go.mod h1:tAG61zBM1DYRaGIPloumExGvScf08oHuo0kFoOqdbT0=
|
||||
github.com/smallstep/pkcs7 v0.2.1 h1:6Kfzr/QizdIuB6LSv8y1LJdZ3aPSfTNhTLqAx9CTLfA=
|
||||
github.com/smallstep/pkcs7 v0.2.1/go.mod h1:RcXHsMfL+BzH8tRhmrF1NkkpebKpq3JEM66cOFxanf0=
|
||||
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
|
||||
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
|
||||
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 h1:+jumHNA0Wrelhe64i8F6HNlS8pkoyMv5sreGx2Ry5Rw=
|
||||
@@ -904,6 +949,8 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
|
||||
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
|
||||
github.com/spiffe/go-spiffe/v2 v2.7.0 h1:uXe1MflJoHw58wAUvxVlcM7WpKtijWG7I1UidcGh6g4=
|
||||
github.com/spiffe/go-spiffe/v2 v2.7.0/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6 h1:pnnLyeX7o/5aX8qUQ69P/mLojDqwda8hFOCBTmP/6hw=
|
||||
github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
@@ -925,12 +972,14 @@ github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUm
|
||||
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.1 h1:OhTOfTwBaGXfbWYWXK9C6Pojma3A1bTaNJ6CEhyuKok=
|
||||
github.com/sylabs/sif/v2 v2.24.1/go.mod h1:PRq9MoP0g+p0qE5ZRGrOhyVAUdHrPsAVmXHgoiW6VU0=
|
||||
github.com/sylabs/sif/v2 v2.24.2 h1:rdkFf6z3xLC6yKweU7S9marlKfkS91Ht0ruIri6YWFY=
|
||||
github.com/sylabs/sif/v2 v2.24.2/go.mod h1:bbrkk0JhTX+xNJcnUtmDkivoO2r4DGWM3LxNR0U2ooA=
|
||||
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/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
|
||||
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.8 h1:UVrTCy1j3DhwaYTTUF+QYO/Nan13S0tf+Jwi+p45Bf0=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.8/go.mod h1:1kb47fi2c6onlf+B7UrrQ9VYraOhcYwFm3iG+J6F4Zo=
|
||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||
@@ -947,10 +996,16 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.16 h1:ld6NyySjx5lowVKwJvMRLnW5nxKX/xnpSiFYZ/Lxur0=
|
||||
github.com/ulikunitz/xz v0.5.16/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw=
|
||||
github.com/ulikunitz/xz v0.5.17 h1:flR0y/x1hgM8EGV1AW3Xll6T413G0glV8UfBwR617V4=
|
||||
github.com/ulikunitz/xz v0.5.17/go.mod h1:H9Rt/W6/Qj27PGauhQc6nfCDy7vHpzsOThBSaYDoEhw=
|
||||
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.3 h1:Cd46rkGXI3Td4yrVNwU8ripbxFaQbmesqhjBUUYAJSw=
|
||||
github.com/vbatts/tar-split v0.12.3/go.mod h1:sQOc6OlqGCr7HkGx/IDBeKiTIvqhmj8KffNhEXG4Nq0=
|
||||
github.com/vbauerster/cupwriter v0.0.4 h1:9sBPe0uXWLZuWQU5lqVbhyFlxX6c09asST/YfatFAys=
|
||||
github.com/vbauerster/cupwriter v0.0.4/go.mod h1:IFyzS6Xis5dnBH/rdAhrnuzg3c+KkUqEN6yE8lhJlDw=
|
||||
github.com/vbauerster/mpb/v8 v8.14.0 h1:55SR80dptMfASxIG/oCEkBXgBhxeSu4GrVsjl16oKmA=
|
||||
github.com/vbauerster/mpb/v8 v8.14.0/go.mod h1:HgpQPKfcWe3kbuGGPmi+jatHreMase5C3Fp5dpdAy0Q=
|
||||
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=
|
||||
@@ -972,6 +1027,8 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@@ -994,31 +1051,33 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.44.0 h1:NmLfL734pJhM0JKaYd2Y28+nY9dPRWYAAbxhRCrKXPw=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.44.0/go.mod h1:tNAsgd8avTGke1+MndXlU5Cru4PQ9Ai/cCNWQv/ZJ/s=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
|
||||
go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
|
||||
go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0 h1:B2h3uqicet1CT2N5TOFhS+Gq++9i0/CLmaxvhmhtP5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0/go.mod h1:dylvB+ZiiwMvsDij9O84Uy7SijLgHMX4mbkncds+4Sw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU=
|
||||
go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc=
|
||||
go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 h1:hqxVTu/GtBF+vJ8d1fzW7fRxZFvgoDjWcxwwCaFDYpU=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0/go.mod h1:z5fVEF4X5v0ESvlJqBrrFlBVoj5EQuefZpzsu7R+x5Q=
|
||||
go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
|
||||
go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
|
||||
go.opentelemetry.io/otel/metric/x v0.67.0 h1:PcicCNZFkZ4bXfSooXdo3WN7RBOVOtjVdo1wD358Uns=
|
||||
go.opentelemetry.io/otel/metric/x v0.67.0/go.mod h1:FBjCWZe6wgcqxcMtjdGiClDKXb2YxxXii0CXftE4QtI=
|
||||
go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
|
||||
go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
|
||||
go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
|
||||
go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
|
||||
go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8=
|
||||
go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o=
|
||||
go.opentelemetry.io/otel/metric/x v0.68.0 h1:TA/cBT23D3MnxYPwHL7YFOdYGdx0A0v+s7Mzotpd1dU=
|
||||
go.opentelemetry.io/otel/metric/x v0.68.0/go.mod h1:agudOmvWhwUTjgibWDzxD2PoWYnpw5Ht5jISYOD2Hd4=
|
||||
go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI=
|
||||
go.opentelemetry.io/otel/sdk v1.46.0/go.mod h1:GAERFXFt5SYCEB+YiKUbMBeza6UaDH7GmGOZEfh2gSM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.46.0/go.mod h1:I1PbKrdVc8Qu8HYVDNtqVIwLwjNrhsV/uFuxfwg8mO4=
|
||||
go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c=
|
||||
go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.podman.io/image/v5 v5.41.2 h1:YnVaFrGLPWSBmipKfNvlOsGcBtk5bUxlPxQ+A6CS/uo=
|
||||
go.podman.io/image/v5 v5.41.2/go.mod h1:T/bzxEFpdpAzyHabPmcq5K+my3wBsht22sFkFjJSKT4=
|
||||
go.podman.io/storage v1.64.1 h1:DqbG8scW/bD3xHiSRzV7O5fzn46qVC+SwmUN6C6Fr5w=
|
||||
go.podman.io/storage v1.64.1/go.mod h1:ft0DzCRMZs1yn0rszmLYXEEwgd7IIc66JRwSRPUDwwc=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
@@ -1027,8 +1086,6 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
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=
|
||||
@@ -1043,6 +1100,10 @@ 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.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
golang.org/x/crypto v0.57.0 h1:3ZVCjf8Ggz7zneR/EHRVx68Ctf+2pmIMP2UFhh9cC6M=
|
||||
golang.org/x/crypto v0.57.0/go.mod h1:Fdz0i5U6CoizGwLda9DttjSk6qlZo25zYNtR+ycvuZA=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
@@ -1084,6 +1145,10 @@ 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.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.41.0 h1:qJmnOUb4YB+FsEuM3HcWucdZASCPGhsX6uljO6pog0c=
|
||||
golang.org/x/mod v0.41.0/go.mod h1:Ek9pY8RKWXwsWvd3rQiHYtMqkjSUV+s1Rj7j4H5Ur6o=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1129,7 +1194,12 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
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.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.59.0 h1:5zfYln+w5XCxwrnMMJPufRgNoXEaGxl0wo5GqPXyues=
|
||||
golang.org/x/net v0.59.0/go.mod h1:2DA/G1UfVbCpQPeWTmMPGY7Cs2PkBkwu743bVX5PIVg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@@ -1163,6 +1233,11 @@ 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.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.23.0 h1:KameEIfc1IkluZyXWLn39Wd4tURc6GbCiISGiZm2bQk=
|
||||
golang.org/x/sync v0.23.0/go.mod h1:sUUOizhqBxiL6pEWpqNLUiaJn1ShEbZ6BBqskPbjZm0=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1239,11 +1314,22 @@ 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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.48.0 h1:bbX/i/6MgT9BVLM9RT1thmxL04yeTAhbEz4SyadbXoo=
|
||||
golang.org/x/sys v0.48.0/go.mod h1:hNLxWAXmnKAxqDtdwIYC4bM9oQPEecfsnNMuSxOs3og=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
golang.org/x/term v0.46.0 h1:3+OXuTbaKDgwk8jTi3aSLHRlmWqHEUDUtxnbFigO4YE=
|
||||
golang.org/x/term v0.46.0/go.mod h1:+K02xbkittuwc0Am4abfA3Fc+XRGXkvBXNO88NCXPoc=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1256,6 +1342,11 @@ 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.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
golang.org/x/text v0.42.0 h1:JbOZXgfeCPU9gacVtYliJqOhD+zhrEqK4LfdpmlUZqI=
|
||||
golang.org/x/text v0.42.0/go.mod h1:ojzP1Z+2QtioaF8DTtO8K5q7JWVVYwZKenzujK0Zd0E=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1318,6 +1409,9 @@ 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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.50.0 h1:c2ifzfcuY7L90lZ2aKd8S4K2NpASF08SZx9ZuJkHmSU=
|
||||
golang.org/x/tools v0.50.0/go.mod h1:7ulVMw3831Mwi5EZD6RomGyffr4VFjuNYXf2BbCEAV0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1434,10 +1528,10 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d h1:C9v1o0/4quuhOAfmRXA2j+we0PqZIp8traLdeogF3Ms=
|
||||
google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d/go.mod h1:Wz2wFJntZFmLGo7pLDXZ3wYk5hyc0Mb+SkHhDDXT+lU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d h1:QwnJwPte4XXAkhPu26LTDIahnsMSUV0kK8HkxbC+Pc4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d/go.mod h1:WRrQ7/7N19PypuT0fxLOL5Lq0waoiRri4FbtHDEKrGE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688 h1:cYNAzI2sUwhmCcoj9TxvihSrqsxt6uIkj3rDRhSDmW4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260819154853-08b0e4226688/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 h1:izFU9hz7aeLI/Mi1J0991ae+xcwRLr7hTqWnB/9aIIU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5/go.mod h1:3LhxRw4YYkf+ylAfgaY9JlVLFKhokkCV8duhLLe7+t0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 h1:1VUiZAXyC+zmiFYi+WLtBzr68Cj8wOofHjjrA/kkizc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -1516,10 +1610,10 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
howett.net/plist v1.0.1 h1:37GdZ8tP09Q35o9ych3ehygcsL+HqKSwzctveSlarvM=
|
||||
howett.net/plist v1.0.1/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
||||
modernc.org/cc/v4 v4.29.2 h1:h6+9ciCnPKutf4I03CvheAvDLX7+IHlqR6Iy6J+cgd8=
|
||||
modernc.org/cc/v4 v4.29.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.35.0 h1:F+TUsmw09QxLzmi3aeYYGxjAXarmZaKgj3mKQHNaA8w=
|
||||
modernc.org/ccgo/v4 v4.35.0/go.mod h1:qrVGs9S3Sr2Ztcg9ve+kTAYMp5a3YvWjo+SoN06kJ5I=
|
||||
modernc.org/cc/v4 v4.29.7 h1:q+NXGJ0bK3b4TXFYQQVr9pYETGnmwFWkrUzJnMya/Tg=
|
||||
modernc.org/cc/v4 v4.29.7/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.36.1 h1:ZNIUZAryN0UgnJwtyxrdEzcFc3yD4Cu4AzjfPXsLsIE=
|
||||
modernc.org/ccgo/v4 v4.36.1/go.mod h1:rrtGc2QkS239nYb/mQNuBMyjq3/y3ZXWbBjPoV3wqzA=
|
||||
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=
|
||||
@@ -1528,8 +1622,8 @@ modernc.org/gc/v3 v3.1.5 h1:21ldfPfRYE31Tb7B3mwAK8gy1AxP4+dKjrOQPfqakoc=
|
||||
modernc.org/gc/v3 v3.1.5/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.75.7 h1:o3DTP9/0p9pKmY2WCKQaySW6wIiZhNM7wc2lUoyhfew=
|
||||
modernc.org/libc v1.75.7/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
|
||||
modernc.org/libc v1.77.1 h1:Ct8j47QtiZ1Enj2DtFXQtUqrPCAjdCmPjtCuvrYQ0Hs=
|
||||
modernc.org/libc v1.77.1/go.mod h1:87/pZ4L6nD1zqW4nItuS12YO7hN1igAah34xjnQo/W0=
|
||||
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.12.1 h1:nFMiWrpStgZczNl6XI9GnIk/rWhYIyHGUaR04pGbp9g=
|
||||
@@ -1538,8 +1632,8 @@ 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.59.0 h1:X1es1GpqBlS/5T+vbM4HLUdaa8OtQx468DF2vrx+38A=
|
||||
modernc.org/sqlite v1.59.0/go.mod h1:+paeT2A3iPRHkQDwG7oA6Tk0zQd5woMEI8q7orfry8k=
|
||||
modernc.org/sqlite v1.60.1 h1:/blz53O951KWFOso4QQvEs/Fq6cDBKLtMVrYNSeJVKw=
|
||||
modernc.org/sqlite v1.60.1/go.mod h1:1dIoEagfDE72QytD5scH1lxARtaUgKgHC/NuApA27r0=
|
||||
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=
|
||||
|
||||
+49
-20
@@ -8,7 +8,6 @@ REPO="${PROJECT_NAME}"
|
||||
GITHUB_DOWNLOAD_PREFIX=https://github.com/${OWNER}/${REPO}/releases/download
|
||||
INSTALL_SH_BASE_URL=https://get.anchore.io/${PROJECT_NAME}
|
||||
LEGACY_INSTALL_SH_BASE_URL=https://raw.githubusercontent.com/${OWNER}/${PROJECT_NAME}
|
||||
PROGRAM_ARGS=$@
|
||||
|
||||
# signature verification options
|
||||
|
||||
@@ -19,6 +18,8 @@ VERIFY_SIGN=false
|
||||
VERIFY_SIGN_SUPPORTED_VERSION=v0.104.0
|
||||
# this is the earliest tag in the repo where the -v flag was introduced to this install.sh script
|
||||
VERIFY_SIGN_FLAG_VERSION=v1.6.0
|
||||
# releases at or after this version are signed with a sigstore bundle (.sigstore.json); earlier releases ship a separate .sig and .pem
|
||||
VERIFY_SIGN_BUNDLE_VERSION=v1.53.0
|
||||
|
||||
# do not change the name of this parameter (this must always be backwards compatible)
|
||||
DOWNLOAD_TAG_INSTALL_SCRIPT=${DOWNLOAD_TAG_INSTALL_SCRIPT:-true}
|
||||
@@ -386,8 +387,8 @@ download_github_release_checksums_files() (
|
||||
complete_url=$(github_release_asset_url "${download_url}" "${name}" "${version}" "${filename}")
|
||||
output_path="${output_dir}/${complete_filename}"
|
||||
|
||||
http_download "${output_path}" "${complete_url}" ""
|
||||
asset_file_exists "${output_path}"
|
||||
http_download "${output_path}" "${complete_url}" "" || return 1
|
||||
asset_file_exists "${output_path}" || return 1
|
||||
|
||||
log_trace "download_github_release_checksums_files() returned '${output_path}' for file '${complete_filename}'"
|
||||
|
||||
@@ -585,28 +586,31 @@ download_and_install_asset() (
|
||||
install_asset "${asset_filepath}" "${install_path}" "${binary}"
|
||||
)
|
||||
|
||||
# verify_sign [checksums-file-path] [certificate-reference] [signature-reference] [version]
|
||||
# verify_sign [checksums-file-path] [verification-material-flags...]
|
||||
#
|
||||
# attempts verify the signature of the checksums file from the release workflow in Github Actions run against the main branch.
|
||||
# the verification material is either "--bundle <path>" or (for older releases) "--certificate <ref> --signature <ref>".
|
||||
#
|
||||
verify_sign() {
|
||||
checksums_file=$1
|
||||
cert_reference=$2
|
||||
sig_reference=$3
|
||||
shift
|
||||
|
||||
log_trace "verifying artifact $1"
|
||||
log_trace "verifying artifact ${checksums_file}"
|
||||
|
||||
log_file=$(mktemp)
|
||||
|
||||
${COSIGN_BINARY} \
|
||||
verify-blob "$checksums_file" \
|
||||
--certificate "$cert_reference" \
|
||||
--signature "$sig_reference" \
|
||||
"$@" \
|
||||
--certificate-identity "https://github.com/${OWNER}/${REPO}/.github/workflows/release.yaml@refs/heads/main" \
|
||||
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" > "${log_file}" 2>&1
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
log_err "$(cat "${log_file}")"
|
||||
# cosign older than v2.4.2 can't read the bundle's verification material and reports a missing cert
|
||||
if [ "$1" = "--bundle" ] && grep -q "bundle does not contain cert" "${log_file}"; then
|
||||
log_err "note: releases >= ${VERIFY_SIGN_BUNDLE_VERSION} are verified with a sigstore bundle, which requires cosign v2.5.0 or newer (check '${COSIGN_BINARY} version')"
|
||||
fi
|
||||
rm -f "${log_file}"
|
||||
return 1
|
||||
fi
|
||||
@@ -630,7 +634,10 @@ download_asset() (
|
||||
|
||||
log_trace "download_asset(url=${download_url}, destination=${destination}, name=${name}, os=${os}, arch=${arch}, version=${version}, format=${format})"
|
||||
|
||||
checksums_filepath=$(download_github_release_checksums "${download_url}" "${name}" "${version}" "${destination}")
|
||||
if ! checksums_filepath=$(download_github_release_checksums "${download_url}" "${name}" "${version}" "${destination}"); then
|
||||
log_err "unable to download checksums file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_trace "checksums content:\n$(cat ${checksums_filepath})"
|
||||
|
||||
@@ -642,13 +649,25 @@ download_asset() (
|
||||
fi
|
||||
|
||||
if [ "$VERIFY_SIGN" = true ]; then
|
||||
checksum_sig_file_url=$(github_release_checksums_sig_url "${download_url}" "${name}" "${version}")
|
||||
log_trace "checksums signature url: ${checksum_sig_file_url}"
|
||||
if compare_semver "${version}" "${VERIFY_SIGN_BUNDLE_VERSION}"; then
|
||||
if ! checksums_bundle_filepath=$(download_github_release_checksums_files "${download_url}" "${name}" "${version}" "${destination}" "checksums.txt.sigstore.json"); then
|
||||
log_err "unable to download checksums signature bundle"
|
||||
return 1
|
||||
fi
|
||||
log_trace "checksums bundle: ${checksums_bundle_filepath}"
|
||||
|
||||
checksums_cert_file_url=$(github_release_checksums_cert_url "${download_url}" "${name}" "${version}")
|
||||
log_trace "checksums certificate url: ${checksums_cert_file_url}"
|
||||
set -- --bundle "${checksums_bundle_filepath}"
|
||||
else
|
||||
checksum_sig_file_url=$(github_release_checksums_sig_url "${download_url}" "${name}" "${version}")
|
||||
log_trace "checksums signature url: ${checksum_sig_file_url}"
|
||||
|
||||
if ! verify_sign "${checksums_filepath}" "${checksums_cert_file_url}" "${checksum_sig_file_url}"; then
|
||||
checksums_cert_file_url=$(github_release_checksums_cert_url "${download_url}" "${name}" "${version}")
|
||||
log_trace "checksums certificate url: ${checksums_cert_file_url}"
|
||||
|
||||
set -- --certificate "${checksums_cert_file_url}" --signature "${checksum_sig_file_url}"
|
||||
fi
|
||||
|
||||
if ! verify_sign "${checksums_filepath}" "$@"; then
|
||||
log_err "signature verification failed"
|
||||
return 1
|
||||
fi
|
||||
@@ -657,9 +676,15 @@ download_asset() (
|
||||
|
||||
asset_url="${download_url}/${asset_filename}"
|
||||
asset_filepath="${destination}/${asset_filename}"
|
||||
http_download "${asset_filepath}" "${asset_url}" ""
|
||||
if ! http_download "${asset_filepath}" "${asset_url}" ""; then
|
||||
log_err "unable to download asset '${asset_url}'"
|
||||
return 1
|
||||
fi
|
||||
|
||||
hash_sha256_verify "${asset_filepath}" "${checksums_filepath}"
|
||||
# this is what ties the asset to the (optionally signature verified) checksums file, so a mismatch must stop the install
|
||||
if ! hash_sha256_verify "${asset_filepath}" "${checksums_filepath}"; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
log_trace "download_asset_by_checksums_file() returned '${asset_filepath}'"
|
||||
|
||||
@@ -765,7 +790,7 @@ prep_signature_verification() {
|
||||
fi
|
||||
}
|
||||
|
||||
main() (
|
||||
parse_install_args() {
|
||||
# parse arguments
|
||||
|
||||
# note: never change default install directory (this must always be backwards compatible)
|
||||
@@ -805,6 +830,11 @@ EOF
|
||||
|
||||
set +u
|
||||
tag=$1
|
||||
set -u
|
||||
}
|
||||
|
||||
main() (
|
||||
parse_install_args "$@"
|
||||
|
||||
if [ -z "${tag}" ]; then
|
||||
log_info "checking github for the current release tag"
|
||||
@@ -812,7 +842,6 @@ EOF
|
||||
else
|
||||
log_info "checking github for release tag='${tag}'"
|
||||
fi
|
||||
set -u
|
||||
|
||||
if ! tag=$(get_release_tag "${OWNER}" "${REPO}" "${tag}"); then
|
||||
log_err "unable to find tag='${tag}'"
|
||||
@@ -843,7 +872,7 @@ EOF
|
||||
log_warn "failed to fetch from ${INSTALL_SH_BASE_URL}, trying fallback URL"
|
||||
install_script=$(http_copy "${LEGACY_INSTALL_SH_BASE_URL}/${tag}/install.sh" "")
|
||||
fi
|
||||
echo "${install_script}" | sh -s -- ${PROGRAM_ARGS}
|
||||
echo "${install_script}" | sh -s -- "$@"
|
||||
exit $?
|
||||
fi
|
||||
|
||||
|
||||
Vendored
+4
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -95,6 +96,9 @@ func Test_makeDiskKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_errors(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("windows ignores unix permission bits on directories")
|
||||
}
|
||||
tmp := t.TempDir()
|
||||
cache := filepath.Join(tmp, "cache")
|
||||
// make a non-writable directory
|
||||
|
||||
@@ -212,7 +212,7 @@ func parseGenericCatalogerFunction(funcDecl *ast.FuncDecl, filePath, repoRoot st
|
||||
// extractPackageNameFromPath extracts the package name from a cataloger source file path
|
||||
// e.g., "syft/pkg/cataloger/swift/cataloger.go" -> "swift"
|
||||
func extractPackageNameFromPath(filePath string) string {
|
||||
parts := strings.Split(filePath, string(filepath.Separator))
|
||||
parts := strings.Split(filepath.ToSlash(filePath), "/")
|
||||
|
||||
// find the LAST occurrence of "cataloger" in the path
|
||||
// (to handle test fixtures with multiple "cataloger" segments)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -337,7 +338,7 @@ func TestResolveImportPath(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := resolveImportPath(tt.importPath, testRepoRoot)
|
||||
require.Equal(t, tt.want, got)
|
||||
require.Equal(t, filepath.FromSlash(tt.want), got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,6 +502,19 @@ func convertToJSONSchemaTypesFromMetadata(metadataTypes []string) []string {
|
||||
return result
|
||||
}
|
||||
|
||||
// packageTypeConstName renders a package type using its go constant name, which is what the detector
|
||||
// "type" field reports (e.g. "BinaryPkg").
|
||||
// a switch rather than a derivation, since pkg.Type values don't reliably map back to their constant
|
||||
// names (e.g. "java-archive" is JavaPkg); add an arm when a classifier declares a new type.
|
||||
func packageTypeConstName(t pkg.Type) string {
|
||||
switch t {
|
||||
case pkg.CpanPkg:
|
||||
return "CpanPkg"
|
||||
default:
|
||||
return "BinaryPkg"
|
||||
}
|
||||
}
|
||||
|
||||
// EnrichWithBinaryClassifier enriches an entry with binary classifier detectors if it's the binary-classifier-cataloger
|
||||
func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry *capabilities.CatalogerEntry) {
|
||||
// special handling for binary-classifier-cataloger: auto-generate one detector per classifier
|
||||
@@ -523,7 +536,7 @@ func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry
|
||||
Name: classifier.Package,
|
||||
PURL: purlStr,
|
||||
CPEs: cpeStrings,
|
||||
Type: "BinaryPkg",
|
||||
Type: packageTypeConstName(classifier.PackageType()),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ 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.10"
|
||||
JSONSchemaVersion = "16.1.11"
|
||||
|
||||
// Changelog
|
||||
// 16.1.0 - reformulated the python pdm fields (added "URL" and removed the unused "path" field).
|
||||
@@ -17,4 +17,5 @@ const (
|
||||
// 16.1.8 - add VcpkgManifest metadata type for vcpkg manifest support
|
||||
// 16.1.9 - add Symbols (grouped by owning package import path) to GolangBinaryBuildinfoEntry metadata
|
||||
// 16.1.10 - add SafeTensorsModelInfo metadata type for the safetensors AI model cataloger
|
||||
// 16.1.11 - add CpanDistribution and CpanUnpackedRelease metadata types for the perl catalogers
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/diskfs/go-diskfs/filesystem"
|
||||
@@ -25,7 +25,8 @@ func WalkDiskDir(fsys filesystem.FileSystem, root string, fn WalkDiskDirFunc) er
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
p := filepath.Join(root, info.Name())
|
||||
// squashfs paths are posix regardless of host os
|
||||
p := path.Join(root, info.Name())
|
||||
err = walkDiskDir(fsys, p, info, fn)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.SkipDir) {
|
||||
@@ -74,7 +75,7 @@ func walkDiskDir(fsys filesystem.FileSystem, name string, d os.FileInfo, walkDir
|
||||
}
|
||||
|
||||
for _, d1 := range dirs {
|
||||
name1 := filepath.Join(name, d1.Name())
|
||||
name1 := path.Join(name, d1.Name())
|
||||
if err := walkDiskDir(fsys, name1, d1, walkDirFn); err != nil {
|
||||
if errors.Is(err, fs.SkipDir) {
|
||||
break
|
||||
|
||||
@@ -19,6 +19,8 @@ func createTestFS(t *testing.T) filesystem.FileSystem {
|
||||
filename := "test.squashfs"
|
||||
f, err := os.Create(filepath.Join(dir, filename))
|
||||
require.NoError(t, err)
|
||||
// windows can't remove the temp dir while the backing file is open
|
||||
t.Cleanup(func() { _ = f.Close() })
|
||||
|
||||
b := file.New(f, false)
|
||||
fsys, err := squashfs.Create(b, 0, 0, 4096)
|
||||
|
||||
@@ -84,7 +84,8 @@ func ExtractGlobsFromTarToUniqueTempFile(ctx context.Context, archivePath, dir s
|
||||
|
||||
func matchesAnyGlob(name string, globs ...string) bool {
|
||||
for _, glob := range globs {
|
||||
if matches, err := doublestar.PathMatch(glob, name); err == nil && matches {
|
||||
// tar entry names are always posix, so don't match with the host separator (PathMatch)
|
||||
if matches, err := doublestar.Match(glob, name); err == nil && matches {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/anchore/syft/internal/os"
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/internal/task"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
@@ -84,6 +85,7 @@ func Test_EnvironmentTask(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
tarPath := imagetest.GetFixtureImageTarPath(t, test.name)
|
||||
|
||||
// get the source
|
||||
|
||||
@@ -19,6 +19,8 @@ func AllTypes() []any {
|
||||
pkg.ConanfileEntry{},
|
||||
pkg.ConaninfoEntry{},
|
||||
pkg.CondaMetaPackage{},
|
||||
pkg.CpanDistribution{},
|
||||
pkg.CpanUnpackedRelease{},
|
||||
pkg.DartPubspec{},
|
||||
pkg.DartPubspecLockEntry{},
|
||||
pkg.DenoLockEntry{},
|
||||
|
||||
@@ -71,6 +71,8 @@ var jsonTypes = makeJSONTypes(
|
||||
jsonNames(pkg.ConanV2LockEntry{}, "c-conan-lock-v2-entry"),
|
||||
jsonNames(pkg.ConanfileEntry{}, "c-conan-file-entry", "ConanMetadataType"),
|
||||
jsonNames(pkg.ConaninfoEntry{}, "c-conan-info-entry"),
|
||||
jsonNames(pkg.CpanDistribution{}, "cpan-distribution"),
|
||||
jsonNames(pkg.CpanUnpackedRelease{}, "cpan-unpacked-release"),
|
||||
jsonNames(pkg.DartPubspecLockEntry{}, "dart-pubspec-lock-entry", "DartPubMetadata"),
|
||||
jsonNames(pkg.DartPubspec{}, "dart-pubspec"),
|
||||
jsonNames(pkg.DenoLockEntry{}, "deno-lock-entry"),
|
||||
|
||||
@@ -13,6 +13,14 @@ func Set(s redact.Store) {
|
||||
store = s
|
||||
}
|
||||
|
||||
// Reset clears the global redaction store. It must only be called once a command
|
||||
// run has fully finished (nothing left to Add or Apply), so that a later run can
|
||||
// Set its own store. Calling it while a run is in flight would let a second Set
|
||||
// split secrets across two stores and leak the ones in the dropped store.
|
||||
func Reset() {
|
||||
store = nil
|
||||
}
|
||||
|
||||
func Get() redact.Store {
|
||||
return store
|
||||
}
|
||||
|
||||
@@ -279,6 +279,46 @@ func TestOwnershipByFilesRelationship(t *testing.T) {
|
||||
return []pkg.Package{parent, child}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
// a CPAN distribution owns whatever its .packlist recorded, which is how a distribution that
|
||||
// installed a binary or a shared object is related to the package cataloged from it
|
||||
name: "cpan-distribution-owns-packlist-paths",
|
||||
setup: func(t testing.TB) ([]pkg.Package, []artifact.Relationship) {
|
||||
parent := pkg.Package{
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("/usr/local/lib/perl5/site_perl/5.40.4/x86_64-linux/auto/Net/SSLeay/.packlist"),
|
||||
),
|
||||
Type: pkg.CpanPkg,
|
||||
Metadata: pkg.CpanDistribution{
|
||||
MainModule: "Net::SSLeay",
|
||||
Files: []string{
|
||||
"/usr/local/lib/perl5/site_perl/5.40.4/x86_64-linux/Net/SSLeay.pm",
|
||||
"/usr/local/lib/perl5/site_perl/5.40.4/x86_64-linux/auto/Net/SSLeay/SSLeay.so",
|
||||
},
|
||||
},
|
||||
}
|
||||
parent.SetID()
|
||||
|
||||
child := pkg.Package{
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("/usr/local/lib/perl5/site_perl/5.40.4/x86_64-linux/auto/Net/SSLeay/SSLeay.so"),
|
||||
),
|
||||
Type: pkg.BinaryPkg,
|
||||
}
|
||||
child.SetID()
|
||||
|
||||
return []pkg.Package{parent, child}, []artifact.Relationship{
|
||||
{
|
||||
From: parent,
|
||||
To: child,
|
||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
||||
Data: ownershipByFilesMetadata{
|
||||
Files: []string{"/usr/local/lib/perl5/site_perl/5.40.4/x86_64-linux/auto/Net/SSLeay/SSLeay.so"},
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/lua"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/nix"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/ocaml"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/perl"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/php"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/python"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/r"
|
||||
@@ -132,6 +133,10 @@ func DefaultPackageTaskFactories() Factories {
|
||||
newSimplePackageTaskFactory(swift.NewSwiftPackageManagerCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swift", "spm"),
|
||||
newSimplePackageTaskFactory(swipl.NewSwiplPackCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swipl", "pack"),
|
||||
newSimplePackageTaskFactory(ocaml.NewOpamPackageManagerCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "ocaml", "opam"),
|
||||
// note: no image tag -- an unpacked release's own META.json describes a distribution present on
|
||||
// disk but not installed into the interpreter's @INC, and declared information is not cataloged
|
||||
// inside container images
|
||||
newSimplePackageTaskFactory(perl.NewCpanMetaCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "perl", "cpan"),
|
||||
|
||||
// language-specific package for both image and directory scans (but not necessarily declared) ////////////////////////////////////////
|
||||
newPackageTaskFactory(
|
||||
@@ -162,6 +167,7 @@ func DefaultPackageTaskFactories() Factories {
|
||||
pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "nix",
|
||||
),
|
||||
newSimplePackageTaskFactory(lua.NewPackageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "lua"),
|
||||
newSimplePackageTaskFactory(perl.NewCpanInstalledCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "perl", "cpan"),
|
||||
|
||||
// other package catalogers ///////////////////////////////////////////////////////////////////////////
|
||||
newPackageTaskFactory(
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var dockerServerOS = sync.OnceValue(func() string {
|
||||
out, err := exec.Command("docker", "version", "--format", "{{.Server.Os}}").Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
})
|
||||
|
||||
// SkipWithoutLinuxContainers skips tests whose fixtures are linux container images (built or run via docker).
|
||||
// This only kicks in on windows, where the docker daemon is often running windows containers (or is absent),
|
||||
// so the linux fixture images can't be built. Everywhere else the fixture tooling is left to fail loudly.
|
||||
func SkipWithoutLinuxContainers(t testing.TB) {
|
||||
t.Helper()
|
||||
if runtime.GOOS != "windows" {
|
||||
return
|
||||
}
|
||||
if os := dockerServerOS(); os != "linux" {
|
||||
t.Skipf("linux container fixtures unavailable (docker server os=%q)", os)
|
||||
}
|
||||
}
|
||||
@@ -265,8 +265,9 @@ func TestLazyCreation(t *testing.T) {
|
||||
// root dir is not created until needed
|
||||
assert.Equal(t, "", td.root)
|
||||
|
||||
_, _, err := td.NewFile("trigger-*")
|
||||
f, _, err := td.NewFile("trigger-*")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
assert.NotEmpty(t, td.root)
|
||||
|
||||
|
||||
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.10/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.1.11/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@@ -729,6 +729,72 @@
|
||||
],
|
||||
"description": "Coordinates contains the minimal information needed to describe how to find a file within any possible source object (e.g."
|
||||
},
|
||||
"CpanDistribution": {
|
||||
"properties": {
|
||||
"dist": {
|
||||
"type": "string",
|
||||
"description": "Dist is the distvname install.json recorded (e.g. URI-5.35), the raw value the package name and\nversion were recovered from"
|
||||
},
|
||||
"mainModule": {
|
||||
"type": "string",
|
||||
"description": "MainModule is the module name recovered from the .packlist path (e.g. LWP for libwww-perl).\nEmpty when the name came from install.json, which is authoritative. Set, it means the name is the\ninstaller's NAME and may not be the distribution name."
|
||||
},
|
||||
"author": {
|
||||
"type": "string",
|
||||
"description": "Author is the PAUSE ID of the distribution author (e.g. OALDERS), parsed out of Path"
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Path is the raw PAUSE path of the release archive (e.g. O/OA/OALDERS/URI-5.35.tar.gz)"
|
||||
},
|
||||
"modules": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/CpanModule"
|
||||
},
|
||||
"type": "array",
|
||||
"description": "Modules are the modules this distribution provides, sorted by name"
|
||||
},
|
||||
"files": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array",
|
||||
"description": "Files are the paths the .packlist recorded as installed by this distribution, kept in the\npacklist's own order and unfiltered: a path the packlist claims and the filesystem lacks means the\nfile was removed or overwritten out from under the installer, which is signal rather than noise"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"description": "CpanDistribution describes a CPAN distribution installed on disk, as recorded by a CPAN client's install.json and by the .packlist an installer wrote."
|
||||
},
|
||||
"CpanModule": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name is the module name as it would be used in a perl `use` statement (e.g. URI::Escape)"
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"description": "Version is the module version, which may differ from the distribution version"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name"
|
||||
],
|
||||
"description": "CpanModule is a single perl module provided by a CPAN distribution."
|
||||
},
|
||||
"CpanUnpackedRelease": {
|
||||
"properties": {
|
||||
"modules": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/CpanModule"
|
||||
},
|
||||
"type": "array",
|
||||
"description": "Modules are the modules the release declares it provides, sorted by name"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"description": "CpanUnpackedRelease describes an unpacked CPAN release found on disk, read from the release's own META.json or META.yml."
|
||||
},
|
||||
"DartPubspec": {
|
||||
"properties": {
|
||||
"homepage": {
|
||||
@@ -2772,6 +2838,12 @@
|
||||
{
|
||||
"$ref": "#/$defs/CondaMetadataEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/CpanDistribution"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/CpanUnpackedRelease"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DartPubspec"
|
||||
},
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
intFile "github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
@@ -92,6 +93,7 @@ func TestDigestsCataloger(t *testing.T) {
|
||||
func TestDigestsCataloger_MixFileTypes(t *testing.T) {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
@@ -162,6 +164,7 @@ func TestDigestsCataloger_MixFileTypes(t *testing.T) {
|
||||
func TestFileDigestCataloger_GivenCoordinates(t *testing.T) {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
c := NewCataloger([]crypto.Hash{crypto.SHA256})
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
func TestFileMetadataCataloger(t *testing.T) {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
c := NewCataloger()
|
||||
@@ -157,6 +159,7 @@ func TestFileMetadataCataloger(t *testing.T) {
|
||||
func TestFileMetadataCataloger_GivenCoordinates(t *testing.T) {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
c := NewCataloger()
|
||||
|
||||
@@ -2,6 +2,7 @@ package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
@@ -19,15 +21,16 @@ import (
|
||||
func Test_allRegularFiles(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func() file.Resolver
|
||||
setup func(t *testing.T) file.Resolver
|
||||
wantRealPaths *strset.Set
|
||||
wantAccessPaths *strset.Set
|
||||
}{
|
||||
{
|
||||
name: "image",
|
||||
setup: func() file.Resolver {
|
||||
setup: func(t *testing.T) file.Resolver {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
s := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
@@ -44,7 +47,12 @@ func Test_allRegularFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "directory",
|
||||
setup: func() file.Resolver {
|
||||
setup: func(t *testing.T) file.Resolver {
|
||||
if runtime.GOOS == "windows" {
|
||||
// file symlinks from a git checkout don't index as access paths on windows yet (same gap the
|
||||
// fileresolver symlink fixtures skip for)
|
||||
t.Skip("fixture relies on symlinks")
|
||||
}
|
||||
s, err := directorysource.NewFromPath("testdata/symlinked-root/nested/link-root")
|
||||
require.NoError(t, err)
|
||||
r, err := s.FileResolver(source.SquashedScope)
|
||||
@@ -57,7 +65,7 @@ func Test_allRegularFiles(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resolver := tt.setup()
|
||||
resolver := tt.setup(t)
|
||||
locations := AllRegularFiles(context.Background(), resolver)
|
||||
realLocations := strset.New()
|
||||
virtualLocations := strset.New()
|
||||
|
||||
@@ -114,45 +114,6 @@ func TestCycloneDxImageEncoder(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestCycloneDxSourcePackageEncoder(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||
testutil.EncoderSnapshotTestConfig{
|
||||
Subject: testutil.SourcePackagesInput(t, dir),
|
||||
Format: getEncoder(t),
|
||||
UpdateSnapshot: *updateSnapshot,
|
||||
PersistRedactionsInSnapshot: true,
|
||||
IsJSON: true,
|
||||
Redactor: redactor(dir),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestCycloneDxSourcePackageEncoder_SpecVersions(t *testing.T) {
|
||||
s := testutil.SourcePackagesInput(t, t.TempDir())
|
||||
tests := []struct {
|
||||
version string
|
||||
want bool
|
||||
}{
|
||||
{version: "1.4", want: false},
|
||||
{version: "1.5", want: false},
|
||||
{version: "1.6", want: true},
|
||||
{version: cyclonedxutil.DefaultVersion, want: true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.version, func(t *testing.T) {
|
||||
enc, err := NewFormatEncoderWithConfig(EncoderConfig{Version: tt.version})
|
||||
require.NoError(t, err)
|
||||
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, enc.Encode(&buf, s))
|
||||
|
||||
// the source-distribution reference type only exists from spec 1.6
|
||||
assert.Equal(t, tt.want, strings.Contains(buf.String(), `"source-distribution"`))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func redactor(values ...string) testutil.Redactor {
|
||||
return testutil.NewRedactions().
|
||||
WithValuesRedacted(values...).
|
||||
|
||||
-275
@@ -1,275 +0,0 @@
|
||||
{
|
||||
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.7",
|
||||
"serialNumber": "urn:uuid:redacted",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"timestamp": "timestamp:redacted",
|
||||
"tools": {
|
||||
"components": [
|
||||
{
|
||||
"type": "application",
|
||||
"author": "anchore",
|
||||
"name": "syft",
|
||||
"version": "v0.42.0-bogus"
|
||||
}
|
||||
]
|
||||
},
|
||||
"component": {
|
||||
"bom-ref": "redacted",
|
||||
"type": "file",
|
||||
"name": "some/path"
|
||||
}
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"bom-ref": "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&package-id=5cc0e8979d7f5115&upstream=attr%401%3A2.4.47-2",
|
||||
"type": "library",
|
||||
"name": "attr",
|
||||
"version": "1:2.4.47-2+b1",
|
||||
"purl": "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "pkg:deb/debian/attr@1%3A2.4.47-2?arch=source&distro=debian-12",
|
||||
"type": "source-distribution"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "dpkg-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "deb"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "dpkg-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/var/lib/dpkg/status"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:source",
|
||||
"value": "attr"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:sourceVersion",
|
||||
"value": "1:2.4.47-2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&package-id=6008fca05ca7b418&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
"type": "library",
|
||||
"name": "bind-export-libs",
|
||||
"version": "32:9.11.13-3.el8",
|
||||
"purl": "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "pkg:rpm/centos/bind@9.11.13-3.el8?arch=src&distro=centos-8&epoch=32",
|
||||
"type": "source-distribution"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "rpm-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "rpm"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "rpm-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/var/lib/rpm/Packages"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:epoch",
|
||||
"value": "32"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:release",
|
||||
"value": "3.el8"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:size",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:sourceRpm",
|
||||
"value": "bind-9.11.13-3.el8.src.rpm"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3&package-id=aa74af1190de1c40",
|
||||
"type": "library",
|
||||
"name": "busybox",
|
||||
"version": "1.35.0-r17",
|
||||
"purl": "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3",
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "apk-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "apk"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "apk-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/lib/apk/db/installed"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:originPackage",
|
||||
"value": "busybox"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:size",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&package-id=58a2fcf749a9a3cc&upstream=gcc",
|
||||
"type": "library",
|
||||
"name": "gcc-libs",
|
||||
"version": "13.2.1-3",
|
||||
"purl": "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc",
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "alpm-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "alpm"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "alpm-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/var/lib/pacman/local/gcc-libs-13.2.1-3/desc"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:architecture",
|
||||
"value": "x86_64"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:basepackage",
|
||||
"value": "gcc"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:package",
|
||||
"value": "gcc-libs"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:size",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:version",
|
||||
"value": "13.2.1-3"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&package-id=16a8c7b19fde6f4f&upstream=libc-dev",
|
||||
"type": "library",
|
||||
"name": "libc-utils",
|
||||
"version": "0.7.2-r3",
|
||||
"purl": "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev",
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "apk-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "apk"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "apk-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/lib/apk/db/installed"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:originPackage",
|
||||
"value": "libc-dev"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:size",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"bom-ref": "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&package-id=7b4ddc97df57aec4&upstream=pam",
|
||||
"type": "library",
|
||||
"name": "libpam-runtime",
|
||||
"version": "1.5.2-6+deb12u1",
|
||||
"purl": "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam",
|
||||
"externalReferences": [
|
||||
{
|
||||
"url": "pkg:deb/debian/pam@1.5.2-6%2Bdeb12u1?arch=source&distro=debian-12",
|
||||
"type": "source-distribution"
|
||||
}
|
||||
],
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "dpkg-db-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "deb"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "dpkg-db-entry"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/var/lib/dpkg/status"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:installedSize",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"name": "syft:metadata:source",
|
||||
"value": "pam"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -87,20 +87,6 @@ func TestCycloneDxImageEncoder(t *testing.T) {
|
||||
)
|
||||
}
|
||||
|
||||
func TestCycloneDxSourcePackageEncoder(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||
testutil.EncoderSnapshotTestConfig{
|
||||
Subject: testutil.SourcePackagesInput(t, dir),
|
||||
Format: getEncoder(t),
|
||||
UpdateSnapshot: *updateSnapshot,
|
||||
PersistRedactionsInSnapshot: true,
|
||||
IsJSON: false,
|
||||
Redactor: redactor(dir),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func redactor(values ...string) testutil.Redactor {
|
||||
return testutil.NewRedactions().
|
||||
WithValuesRedacted(values...).
|
||||
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="redacted" version="1">
|
||||
<metadata>
|
||||
<timestamp>redacted</timestamp>
|
||||
<tools>
|
||||
<components>
|
||||
<component type="application">
|
||||
<author>anchore</author>
|
||||
<name>syft</name>
|
||||
<version>v0.42.0-bogus</version>
|
||||
</component>
|
||||
</components>
|
||||
</tools>
|
||||
<component bom-ref="redacted" type="file">
|
||||
<name>some/path</name>
|
||||
</component>
|
||||
</metadata>
|
||||
<components>
|
||||
<component bom-ref="pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&package-id=5cc0e8979d7f5115&upstream=attr%401%3A2.4.47-2" type="library">
|
||||
<name>attr</name>
|
||||
<version>1:2.4.47-2+b1</version>
|
||||
<purl>pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2</purl>
|
||||
<externalReferences>
|
||||
<reference type="source-distribution">
|
||||
<url>pkg:deb/debian/attr@1%3A2.4.47-2?arch=source&distro=debian-12</url>
|
||||
</reference>
|
||||
</externalReferences>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">dpkg-db-cataloger</property>
|
||||
<property name="syft:package:type">deb</property>
|
||||
<property name="syft:package:metadataType">dpkg-db-entry</property>
|
||||
<property name="syft:location:0:path">/var/lib/dpkg/status</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
<property name="syft:metadata:source">attr</property>
|
||||
<property name="syft:metadata:sourceVersion">1:2.4.47-2</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&package-id=6008fca05ca7b418&upstream=bind-9.11.13-3.el8.src.rpm" type="library">
|
||||
<name>bind-export-libs</name>
|
||||
<version>32:9.11.13-3.el8</version>
|
||||
<purl>pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm</purl>
|
||||
<externalReferences>
|
||||
<reference type="source-distribution">
|
||||
<url>pkg:rpm/centos/bind@9.11.13-3.el8?arch=src&distro=centos-8&epoch=32</url>
|
||||
</reference>
|
||||
</externalReferences>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">rpm-db-cataloger</property>
|
||||
<property name="syft:package:type">rpm</property>
|
||||
<property name="syft:package:metadataType">rpm-db-entry</property>
|
||||
<property name="syft:location:0:path">/var/lib/rpm/Packages</property>
|
||||
<property name="syft:metadata:epoch">32</property>
|
||||
<property name="syft:metadata:release">3.el8</property>
|
||||
<property name="syft:metadata:size">0</property>
|
||||
<property name="syft:metadata:sourceRpm">bind-9.11.13-3.el8.src.rpm</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3&package-id=aa74af1190de1c40" type="library">
|
||||
<name>busybox</name>
|
||||
<version>1.35.0-r17</version>
|
||||
<purl>pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3</purl>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">apk-db-cataloger</property>
|
||||
<property name="syft:package:type">apk</property>
|
||||
<property name="syft:package:metadataType">apk-db-entry</property>
|
||||
<property name="syft:location:0:path">/lib/apk/db/installed</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
<property name="syft:metadata:originPackage">busybox</property>
|
||||
<property name="syft:metadata:size">0</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&package-id=58a2fcf749a9a3cc&upstream=gcc" type="library">
|
||||
<name>gcc-libs</name>
|
||||
<version>13.2.1-3</version>
|
||||
<purl>pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc</purl>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">alpm-db-cataloger</property>
|
||||
<property name="syft:package:type">alpm</property>
|
||||
<property name="syft:package:metadataType">alpm-db-entry</property>
|
||||
<property name="syft:location:0:path">/var/lib/pacman/local/gcc-libs-13.2.1-3/desc</property>
|
||||
<property name="syft:metadata:architecture">x86_64</property>
|
||||
<property name="syft:metadata:basepackage">gcc</property>
|
||||
<property name="syft:metadata:package">gcc-libs</property>
|
||||
<property name="syft:metadata:size">0</property>
|
||||
<property name="syft:metadata:version">13.2.1-3</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&package-id=16a8c7b19fde6f4f&upstream=libc-dev" type="library">
|
||||
<name>libc-utils</name>
|
||||
<version>0.7.2-r3</version>
|
||||
<purl>pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev</purl>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">apk-db-cataloger</property>
|
||||
<property name="syft:package:type">apk</property>
|
||||
<property name="syft:package:metadataType">apk-db-entry</property>
|
||||
<property name="syft:location:0:path">/lib/apk/db/installed</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
<property name="syft:metadata:originPackage">libc-dev</property>
|
||||
<property name="syft:metadata:size">0</property>
|
||||
</properties>
|
||||
</component>
|
||||
<component bom-ref="pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&package-id=7b4ddc97df57aec4&upstream=pam" type="library">
|
||||
<name>libpam-runtime</name>
|
||||
<version>1.5.2-6+deb12u1</version>
|
||||
<purl>pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam</purl>
|
||||
<externalReferences>
|
||||
<reference type="source-distribution">
|
||||
<url>pkg:deb/debian/pam@1.5.2-6%2Bdeb12u1?arch=source&distro=debian-12</url>
|
||||
</reference>
|
||||
</externalReferences>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">dpkg-db-cataloger</property>
|
||||
<property name="syft:package:type">deb</property>
|
||||
<property name="syft:package:metadataType">dpkg-db-entry</property>
|
||||
<property name="syft:location:0:path">/var/lib/dpkg/status</property>
|
||||
<property name="syft:metadata:installedSize">0</property>
|
||||
<property name="syft:metadata:source">pam</property>
|
||||
</properties>
|
||||
</component>
|
||||
</components>
|
||||
</bom>
|
||||
@@ -2,6 +2,7 @@ package github
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/format/internal/testutil"
|
||||
@@ -11,6 +12,10 @@ var updateSnapshot = flag.Bool("update-github", false, "update the *.golden file
|
||||
var updateImage = flag.Bool("update-image", false, "update the golden image used for image encoder testing")
|
||||
|
||||
func TestGithubDirectoryEncoder(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// the golden snapshot embeds the posix source path (and the source ID derived from it)
|
||||
t.Skip("directory snapshot is posix path specific")
|
||||
}
|
||||
dir := t.TempDir()
|
||||
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||
testutil.EncoderSnapshotTestConfig{
|
||||
|
||||
@@ -2,7 +2,6 @@ package cyclonedxutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
"slices"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
@@ -30,35 +29,9 @@ func NewEncoder(version string, format cyclonedx.BOMFileFormat, pretty bool) (En
|
||||
|
||||
func (e Encoder) Encode(writer io.Writer, s sbom.SBOM) error {
|
||||
bom := cyclonedxhelpers.ToFormatModel(s)
|
||||
if e.version < cyclonedx.SpecVersion1_6 {
|
||||
// cyclonedx-go does not downgrade this 1.6+ reference type for older spec versions
|
||||
// (see https://github.com/CycloneDX/cyclonedx-go/issues/291), so drop it rather than emit an invalid document
|
||||
dropExternalReferenceType(bom.Components, cyclonedx.ERTypeSourceDistribution)
|
||||
}
|
||||
enc := cyclonedx.NewBOMEncoder(writer, e.format)
|
||||
enc.SetPretty(e.pretty)
|
||||
enc.SetEscapeHTML(false)
|
||||
|
||||
return enc.EncodeVersion(bom, e.version)
|
||||
}
|
||||
|
||||
func dropExternalReferenceType(components *[]cyclonedx.Component, refType cyclonedx.ExternalReferenceType) {
|
||||
if components == nil {
|
||||
return
|
||||
}
|
||||
for i := range *components {
|
||||
c := &(*components)[i]
|
||||
dropExternalReferenceType(c.Components, refType)
|
||||
if c.ExternalReferences == nil {
|
||||
continue
|
||||
}
|
||||
refs := slices.DeleteFunc(*c.ExternalReferences, func(r cyclonedx.ExternalReference) bool {
|
||||
return r.Type == refType
|
||||
})
|
||||
if len(refs) == 0 {
|
||||
c.ExternalReferences = nil
|
||||
continue
|
||||
}
|
||||
c.ExternalReferences = &refs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/file"
|
||||
syftFile "github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -80,162 +79,12 @@ func encodeExternalReferences(p pkg.Package) *[]cyclonedx.ExternalReference {
|
||||
}
|
||||
}
|
||||
}
|
||||
if srcRef := encodeSourcePackageExternalReference(p); srcRef != nil {
|
||||
refs = append(refs, *srcRef)
|
||||
}
|
||||
if len(refs) > 0 {
|
||||
return &refs
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// encodeSourcePackageExternalReference returns a "source-distribution" reference holding the PURL of the distro
|
||||
// source package that a binary OS package was built from (e.g. libpam-runtime -> pkg:deb/debian/pam@...?arch=source).
|
||||
// It is only emitted when the source differs from the binary package (by name or version) and when the package PURL
|
||||
// is of the type implied by the metadata. The main consumer is vulnerability matching, since distros tend to report
|
||||
// vulnerabilities against the source package rather than each binary package built from it.
|
||||
//
|
||||
// why an external reference and not pedigree: `component.pedigree.ancestors` looks like the obvious spot for "this
|
||||
// binary came from that source", but pedigree describes code lineage (forks, patched variants, and the patches that
|
||||
// separate them). A binary built unmodified from its own distro source package is the same code in a different form,
|
||||
// not a fork, and CycloneDX components are deliberately agnostic to source vs binary form (see the comments from the
|
||||
// CycloneDX maintainers in https://github.com/CycloneDX/specification/issues/612#issuecomment-2958800363). The
|
||||
// ancestor slot is better left for the upstream project a distro patched (e.g. Linux-PAM for Debian's pam), which
|
||||
// syft does not know today.
|
||||
//
|
||||
// why "source-distribution": the spec defines it as "the location where the source code distributable can be
|
||||
// obtained", and a deb/rpm source package is exactly that distributable (a .dsc and tarballs in the Debian source
|
||||
// pool, a .src.rpm in an SRPMS repo). The value is a PURL (an identifier) rather than a download URL, which the
|
||||
// schema allows since external reference URLs are URIs of any scheme, and a CycloneDX maintainer confirmed this exact
|
||||
// shape is valid in https://github.com/CycloneDX/specification/issues/612#issuecomment-5428057874. A first-class
|
||||
// "source" component type was discussed in that issue and deferred to CycloneDX 2.0.
|
||||
func encodeSourcePackageExternalReference(p pkg.Package) *cyclonedx.ExternalReference {
|
||||
if p.PURL == "" {
|
||||
return nil
|
||||
}
|
||||
src := sourcePackageOf(p)
|
||||
if src == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// the binary PURL is only consulted for the namespace and a small set of qualifiers (e.g. distro), everything
|
||||
// else describes the binary package and must not be restated as a fact about the source package.
|
||||
binPurl, err := packageurl.FromString(p.PURL)
|
||||
if err != nil || binPurl.Type != src.purlType {
|
||||
return nil
|
||||
}
|
||||
|
||||
qualifiers := map[string]string{}
|
||||
for _, q := range binPurl.Qualifiers {
|
||||
if src.keepQualifier(q.Key) {
|
||||
qualifiers[q.Key] = q.Value
|
||||
}
|
||||
}
|
||||
if src.arch != "" {
|
||||
qualifiers[pkg.PURLQualifierArch] = src.arch
|
||||
}
|
||||
|
||||
srcPurl := packageurl.NewPackageURL(
|
||||
src.purlType,
|
||||
binPurl.Namespace,
|
||||
src.name,
|
||||
src.version,
|
||||
packageurl.QualifiersFromMap(qualifiers),
|
||||
"",
|
||||
)
|
||||
|
||||
return &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution,
|
||||
URL: srcPurl.ToString(),
|
||||
}
|
||||
}
|
||||
|
||||
// sourcePackage is the identity of the source package a binary OS package was built from.
|
||||
type sourcePackage struct {
|
||||
purlType string
|
||||
name string
|
||||
version string
|
||||
// arch is the ecosystem's source architecture marker ("source" for deb, "src" or "nosrc" for rpm)
|
||||
arch string
|
||||
}
|
||||
|
||||
func (s sourcePackage) keepQualifier(key string) bool {
|
||||
switch key {
|
||||
case pkg.PURLQualifierDistro:
|
||||
return true
|
||||
case pkg.PURLQualifierEpoch, pkg.PURLQualifierRpmModularity:
|
||||
return s.purlType == pkg.RpmPkg.PackageURLType()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// sourcePackageOf returns the source package for p, or nil when there is none or it is the same as the binary.
|
||||
//
|
||||
// only deb and rpm are supported, since those are the ecosystems that publish real source packages with their own
|
||||
// PURL identity (arch=source / arch=src). apk (origin) and alpm (pkgbase) are intentionally left out: there the
|
||||
// "source" is a build recipe rather than a published source distributable, and its name is usually also the name of
|
||||
// a real binary package (e.g. pkg:apk/alpine/libc-dev@0.7.2-r3 is an installable package, not a source archive), so
|
||||
// a "source-distribution" reference would point at a binary package. Whether those ecosystems have a meaningful
|
||||
// source PURL is still an open question, and until then the `upstream` PURL qualifier continues to carry that info.
|
||||
func sourcePackageOf(p pkg.Package) *sourcePackage {
|
||||
var src sourcePackage
|
||||
var binName, binVersion string
|
||||
switch m := p.Metadata.(type) {
|
||||
case pkg.DpkgDBEntry:
|
||||
src = dpkgSourcePackage(m)
|
||||
binName, binVersion = m.Package, m.Version
|
||||
case pkg.DpkgArchiveEntry:
|
||||
src = dpkgSourcePackage(pkg.DpkgDBEntry(m))
|
||||
binName, binVersion = m.Package, m.Version
|
||||
case pkg.RpmDBEntry:
|
||||
src = rpmSourcePackage(m.SourceRpm)
|
||||
binName, binVersion = m.Name, m.Version+"-"+m.Release
|
||||
case pkg.RpmArchive:
|
||||
src = rpmSourcePackage(m.SourceRpm)
|
||||
binName, binVersion = m.Name, m.Version+"-"+m.Release
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
if src.name == "" || src.version == "" {
|
||||
return nil
|
||||
}
|
||||
if src.name == binName && src.version == binVersion {
|
||||
return nil
|
||||
}
|
||||
return &src
|
||||
}
|
||||
|
||||
func dpkgSourcePackage(entry pkg.DpkgDBEntry) sourcePackage {
|
||||
version := entry.SourceVersion
|
||||
if version == "" {
|
||||
version = entry.Version
|
||||
}
|
||||
return sourcePackage{purlType: pkg.DebPkg.PackageURLType(), name: entry.Source, version: version, arch: "source"}
|
||||
}
|
||||
|
||||
// rpmSourcePackage parses a source RPM filename of the form <name>-<version>-<release>.(no)src.rpm.
|
||||
func rpmSourcePackage(sourceRpm string) sourcePackage {
|
||||
var arch string
|
||||
switch {
|
||||
case strings.HasSuffix(sourceRpm, ".src.rpm"):
|
||||
arch = "src"
|
||||
case strings.HasSuffix(sourceRpm, ".nosrc.rpm"):
|
||||
arch = "nosrc"
|
||||
default:
|
||||
return sourcePackage{}
|
||||
}
|
||||
nvr := strings.TrimSuffix(sourceRpm, "."+arch+".rpm")
|
||||
release := strings.LastIndex(nvr, "-")
|
||||
if release < 0 {
|
||||
return sourcePackage{}
|
||||
}
|
||||
ver := strings.LastIndex(nvr[:release], "-")
|
||||
if ver < 0 {
|
||||
return sourcePackage{}
|
||||
}
|
||||
return sourcePackage{purlType: pkg.RpmPkg.PackageURLType(), name: nvr[:ver], version: nvr[ver+1:], arch: arch}
|
||||
}
|
||||
|
||||
// supported algorithm in cycloneDX as of 1.4
|
||||
// "MD5", "SHA-1", "SHA-256", "SHA-384", "SHA-512",
|
||||
// "SHA3-256", "SHA3-384", "SHA3-512", "BLAKE2b-256", "BLAKE2b-384", "BLAKE2b-512", "BLAKE3"
|
||||
|
||||
@@ -144,342 +144,6 @@ func Test_encodeExternalReferences(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_encodeSourcePackage(t *testing.T) {
|
||||
rpmEpoch := 32
|
||||
tests := []struct {
|
||||
name string
|
||||
input pkg.Package
|
||||
expected *cyclonedx.ExternalReference
|
||||
}{
|
||||
{
|
||||
name: "no metadata",
|
||||
input: pkg.Package{},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "no PURL",
|
||||
input: pkg.Package{
|
||||
Name: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Type: pkg.DebPkg,
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Source: "pam",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
// apk origins are build recipes, not published source packages, so they are not supported
|
||||
name: "from apk is not supported",
|
||||
input: pkg.Package{
|
||||
Name: "libc-utils",
|
||||
Version: "0.7.2-r3",
|
||||
Type: pkg.ApkPkg,
|
||||
PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "libc-utils",
|
||||
OriginPackage: "libc-dev",
|
||||
Version: "0.7.2-r3",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from deb",
|
||||
input: pkg.Package{
|
||||
Name: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Source: "pam",
|
||||
Architecture: "all",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/debian/pam@1.5.2-6%2Bdeb12u1?arch=source&distro=debian-12",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from deb with source version",
|
||||
input: pkg.Package{
|
||||
Name: "attr",
|
||||
Version: "1:2.4.47-2+b1",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "attr",
|
||||
Version: "1:2.4.47-2+b1",
|
||||
Source: "attr",
|
||||
SourceVersion: "1:2.4.47-2",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/debian/attr@1%3A2.4.47-2?arch=source&distro=debian-12",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from deb with empty source",
|
||||
input: pkg.Package{
|
||||
Name: "some-package",
|
||||
Version: "1.0.0",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/some-package@1.0.0?arch=all&distro=debian-12",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "some-package",
|
||||
Version: "1.0.0",
|
||||
Architecture: "all",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from deb archive",
|
||||
input: pkg.Package{
|
||||
Name: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&upstream=pam",
|
||||
Metadata: pkg.DpkgArchiveEntry{
|
||||
Package: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Source: "pam",
|
||||
Architecture: "all",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/pam@1.5.2-6%2Bdeb12u1?arch=source",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm",
|
||||
input: pkg.Package{
|
||||
Name: "bind-export-libs",
|
||||
Version: "32:9.11.13-3.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "bind-export-libs",
|
||||
Version: "9.11.13",
|
||||
Release: "3.el8",
|
||||
Epoch: &rpmEpoch,
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "bind-9.11.13-3.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/centos/bind@9.11.13-3.el8?arch=src&distro=centos-8&epoch=32",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm with source = bin",
|
||||
input: pkg.Package{
|
||||
Name: "some-package",
|
||||
Version: "32:1.0.0-1.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/centos/some-package@1.0.0-1.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=some-package-1.0.0-1.el8.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "some-package",
|
||||
Version: "1.0.0",
|
||||
Release: "1.el8",
|
||||
Epoch: &rpmEpoch,
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "some-package-1.0.0-1.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from rpm archive",
|
||||
input: pkg.Package{
|
||||
Name: "bind-export-libs",
|
||||
Version: "32:9.11.13-3.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/bind-export-libs@9.11.13-3.el8?arch=x86_64&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
Metadata: pkg.RpmArchive{
|
||||
Name: "bind-export-libs",
|
||||
Version: "9.11.13",
|
||||
Release: "3.el8",
|
||||
Epoch: &rpmEpoch,
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "bind-9.11.13-3.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/bind@9.11.13-3.el8?arch=src&epoch=32",
|
||||
},
|
||||
},
|
||||
{
|
||||
// alpm pkgbases are build recipes, not published source packages, so they are not supported
|
||||
name: "from alpm is not supported",
|
||||
input: pkg.Package{
|
||||
Name: "gcc-libs",
|
||||
Version: "13.2.1-3",
|
||||
Type: pkg.AlpmPkg,
|
||||
PURL: "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "gcc-libs",
|
||||
Version: "13.2.1-3",
|
||||
BasePackage: "gcc",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from rpm with dashes in the source name",
|
||||
input: pkg.Package{
|
||||
Name: "perl-Foo-Bar-libs",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/perl-Foo-Bar-libs@1.0-1.el8?arch=x86_64&distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "perl-Foo-Bar-libs",
|
||||
Version: "1.0",
|
||||
Release: "1.el8",
|
||||
SourceRpm: "perl-Foo-Bar-1.0-1.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/redhat/perl-Foo-Bar@1.0-1.el8?arch=src&distro=rhel-8",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm nosrc",
|
||||
input: pkg.Package{
|
||||
Name: "some-firmware",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/some-firmware@1.0-1.el8?arch=noarch&distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "some-firmware",
|
||||
Version: "1.0",
|
||||
Release: "1.el8",
|
||||
SourceRpm: "firmware-1.0-1.el8.nosrc.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/redhat/firmware@1.0-1.el8?arch=nosrc&distro=rhel-8",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm keeps rpmmod",
|
||||
input: pkg.Package{
|
||||
Name: "nodejs-libs",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/nodejs-libs@18.0-1.el8?arch=x86_64&distro=rhel-8&rpmmod=nodejs%3A18",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "nodejs-libs",
|
||||
Version: "18.0",
|
||||
Release: "1.el8",
|
||||
SourceRpm: "nodejs-18.0-1.el8.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:rpm/redhat/nodejs@18.0-1.el8?arch=src&distro=rhel-8&rpmmod=nodejs%3A18",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "from rpm with malformed source rpm",
|
||||
input: pkg.Package{
|
||||
Name: "gpg-pubkey",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/gpg-pubkey@1-1?distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "gpg-pubkey",
|
||||
Version: "1",
|
||||
Release: "1",
|
||||
SourceRpm: "(none)",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from rpm with non-source rpm filename",
|
||||
input: pkg.Package{
|
||||
Name: "bash",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/bash@5.1-2?arch=x86_64&distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "bash",
|
||||
Version: "5.1",
|
||||
Release: "2",
|
||||
SourceRpm: "bash-5.1-2.x86_64.rpm",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "from rpm with too few dashes",
|
||||
input: pkg.Package{
|
||||
Name: "bash",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/bash@5.1-2?arch=x86_64&distro=rhel-8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "bash",
|
||||
Version: "5.1",
|
||||
Release: "2",
|
||||
SourceRpm: "bash-5.1.src.rpm",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "binary qualifiers and subpath are not carried over",
|
||||
input: pkg.Package{
|
||||
Name: "libssl3",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/libssl3@3.0.0-1?arch=amd64&distro=debian-12&checksum=sha256:dead&download_url=https://example.com/x.deb&cpes=cpe:2.3:a:x:x:1:*:*:*:*:*:*:*#sub/dir",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "libssl3",
|
||||
Version: "3.0.0-1",
|
||||
Source: "openssl",
|
||||
},
|
||||
},
|
||||
expected: &cyclonedx.ExternalReference{
|
||||
Type: cyclonedx.ERTypeSourceDistribution, URL: "pkg:deb/debian/openssl@3.0.0-1?arch=source&distro=debian-12",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "PURL type does not match metadata",
|
||||
input: pkg.Package{
|
||||
Name: "x",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:npm/x@1",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "x",
|
||||
Version: "1",
|
||||
Source: "lodash",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "invalid PURL",
|
||||
input: pkg.Package{
|
||||
Name: "x",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "not-a-purl",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "x",
|
||||
Version: "1",
|
||||
Source: "y",
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, encodeSourcePackageExternalReference(test.input))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_isValidExternalRef(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -20,6 +20,8 @@ func Test_OriginatorSupplier(t *testing.T) {
|
||||
pkg.ConanfileEntry{},
|
||||
pkg.ConaninfoEntry{},
|
||||
pkg.CondaMetaPackage{},
|
||||
pkg.CpanDistribution{}, // the author is a PAUSE ID, not a person or organization name
|
||||
pkg.CpanUnpackedRelease{},
|
||||
pkg.DartPubspecLockEntry{},
|
||||
pkg.DartPubspec{},
|
||||
pkg.DotnetDepsEntry{},
|
||||
|
||||
@@ -66,6 +66,8 @@ func SourceInfo(p pkg.Package) string {
|
||||
answer = "acquired package info from nix store path"
|
||||
case pkg.Rpkg:
|
||||
answer = "acquired package info from R-package DESCRIPTION file"
|
||||
case pkg.CpanPkg:
|
||||
answer = "acquired package info from CPAN distribution metadata"
|
||||
case pkg.LuaRocksPkg:
|
||||
answer = "acquired package info from Rockspec package file"
|
||||
case pkg.SwiftPkg:
|
||||
|
||||
@@ -287,6 +287,14 @@ func Test_SourceInfo(t *testing.T) {
|
||||
"acquired package info from R-package DESCRIPTION file",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: pkg.Package{
|
||||
Type: pkg.CpanPkg,
|
||||
},
|
||||
expected: []string{
|
||||
"acquired package info from CPAN distribution metadata",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: pkg.Package{
|
||||
Type: pkg.LuaRocksPkg,
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/anchore/stereoscope/pkg/filetree"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
@@ -35,6 +36,7 @@ func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBO
|
||||
case true:
|
||||
img = imagetest.GetGoldenFixtureImage(t, testImage)
|
||||
default:
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img = imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,146 +0,0 @@
|
||||
package testutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
)
|
||||
|
||||
func SourcePackagesInput(t testing.TB, dir string) sbom.SBOM {
|
||||
path := filepath.Join(dir, "some", "path")
|
||||
require.NoError(t, os.MkdirAll(path, 0755))
|
||||
|
||||
src, err := directorysource.New(
|
||||
directorysource.Config{
|
||||
Path: path,
|
||||
Base: dir,
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
return sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: newSourcePackageCatalog(),
|
||||
},
|
||||
Source: src.Describe(),
|
||||
Descriptor: sbom.Descriptor{
|
||||
Name: "syft",
|
||||
Version: "v0.42.0-bogus",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:funlen
|
||||
func newSourcePackageCatalog() *pkg.Collection {
|
||||
epoch := 32
|
||||
|
||||
catalog := pkg.NewCollection()
|
||||
|
||||
// apk: the origin differs from the package name, but apk is not supported so no source reference is expected
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "libc-utils",
|
||||
Version: "0.7.2-r3",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/lib/apk/db/installed")),
|
||||
PURL: "pkg:apk/alpine/libc-utils@0.7.2-r3?arch=x86_64&distro=alpine-3.16.3&upstream=libc-dev",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "libc-utils",
|
||||
OriginPackage: "libc-dev",
|
||||
Version: "0.7.2-r3",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
})
|
||||
|
||||
// apk: negative control -- the package is its own origin
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "busybox",
|
||||
Version: "1.35.0-r17",
|
||||
Type: pkg.ApkPkg,
|
||||
FoundBy: "apk-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/lib/apk/db/installed")),
|
||||
PURL: "pkg:apk/alpine/busybox@1.35.0-r17?arch=x86_64&distro=alpine-3.16.3",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "busybox",
|
||||
OriginPackage: "busybox",
|
||||
Version: "1.35.0-r17",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
})
|
||||
|
||||
// deb: source name differs, and the source version is implied by the binary version
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "dpkg-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/var/lib/dpkg/status")),
|
||||
PURL: "pkg:deb/debian/libpam-runtime@1.5.2-6%2Bdeb12u1?arch=all&distro=debian-12&upstream=pam",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "libpam-runtime",
|
||||
Version: "1.5.2-6+deb12u1",
|
||||
Source: "pam",
|
||||
Architecture: "all",
|
||||
},
|
||||
})
|
||||
|
||||
// deb: the source version differs from the binary version
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "attr",
|
||||
Version: "1:2.4.47-2+b1",
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "dpkg-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/var/lib/dpkg/status")),
|
||||
PURL: "pkg:deb/debian/attr@1%3A2.4.47-2%2Bb1?arch=amd64&distro=debian-12&upstream=attr%401%3A2.4.47-2",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "attr",
|
||||
Version: "1:2.4.47-2+b1",
|
||||
Source: "attr",
|
||||
SourceVersion: "1:2.4.47-2",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
})
|
||||
|
||||
// rpm: name and version both come out of the source RPM filename, and the epoch is carried over
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "bind-export-libs",
|
||||
Version: "32:9.11.13-3.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
FoundBy: "rpm-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/var/lib/rpm/Packages")),
|
||||
PURL: "pkg:rpm/centos/bind-export-libs@9.11.13-3.el8?arch=x86_64&distro=centos-8&epoch=32&upstream=bind-9.11.13-3.el8.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "bind-export-libs",
|
||||
Version: "9.11.13",
|
||||
Release: "3.el8",
|
||||
Epoch: &epoch,
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "bind-9.11.13-3.el8.src.rpm",
|
||||
},
|
||||
})
|
||||
|
||||
// alpm: the base package differs from the package name, but alpm is not supported so no source reference is expected
|
||||
catalog.Add(pkg.Package{
|
||||
Name: "gcc-libs",
|
||||
Version: "13.2.1-3",
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Locations: file.NewLocationSet(file.NewLocation("/var/lib/pacman/local/gcc-libs-13.2.1-3/desc")),
|
||||
PURL: "pkg:alpm/arch/gcc-libs@13.2.1-3?arch=x86_64&distro=arch-rolling&upstream=gcc",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "gcc-libs",
|
||||
Version: "13.2.1-3",
|
||||
BasePackage: "gcc",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
})
|
||||
|
||||
return catalog
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"flag"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -90,6 +91,10 @@ func TestEscapeHTML(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryEncoder(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// the golden snapshot embeds the posix source path (and the source ID derived from it)
|
||||
t.Skip("directory snapshot is posix path specific")
|
||||
}
|
||||
cfg := DefaultEncoderConfig()
|
||||
cfg.Pretty = true
|
||||
enc, err := NewFormatEncoderWithConfig(cfg)
|
||||
|
||||
@@ -2,6 +2,7 @@ package text
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/format/internal/testutil"
|
||||
@@ -11,6 +12,10 @@ var updateSnapshot = flag.Bool("update-text", false, "update the *.golden files
|
||||
var updateImage = flag.Bool("update-image", false, "update the golden image used for image encoder testing")
|
||||
|
||||
func TestTextDirectoryEncoder(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// the golden snapshot embeds the posix source path (and the source ID derived from it)
|
||||
t.Skip("directory snapshot is posix path specific")
|
||||
}
|
||||
dir := t.TempDir()
|
||||
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||
testutil.EncoderSnapshotTestConfig{
|
||||
|
||||
@@ -3,6 +3,7 @@ package syft
|
||||
import (
|
||||
"crypto"
|
||||
"fmt"
|
||||
"slices"
|
||||
|
||||
"github.com/anchore/go-collections"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
@@ -10,6 +11,11 @@ import (
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
// GetSourceConfig controls which source providers are tried, and in what order, when resolving user input to a source.
|
||||
//
|
||||
// The "containers-storage" provider (local Buildah / Podman store) is never part of automatic resolution: it is only
|
||||
// used when named in Sources or DefaultImagePullSource. It is also only functional when built with the
|
||||
// containers_image_openpgp build tag (as syft's Linux release binaries are); otherwise it always returns an error.
|
||||
type GetSourceConfig struct {
|
||||
// SourceProviderConfig may optionally be provided to be used when constructing the default set of source providers, unused if All specified
|
||||
SourceProviderConfig *sourceproviders.Config
|
||||
@@ -64,6 +70,12 @@ func (c *GetSourceConfig) WithDefaultImagePullSource(defaultImagePullSource stri
|
||||
func (c *GetSourceConfig) getProviders(userInput string) ([]source.Provider, error) {
|
||||
providers := collections.TaggedValueSet[source.Provider]{}.Join(sourceproviders.All(userInput, c.SourceProviderConfig)...)
|
||||
|
||||
// opening a containers-storage store runs full graph driver init (mounts, locks, possible store writes), which is
|
||||
// too invasive for an "is this image local?" probe on every plain image reference. Only use it when asked by name.
|
||||
if !c.requestsSource(image.ContainersStorageSource) {
|
||||
providers = providers.Remove(image.ContainersStorageSource)
|
||||
}
|
||||
|
||||
// if the "default image pull source" is set, we move this as the first pull source
|
||||
if c.DefaultImagePullSource != "" {
|
||||
base := providers.Remove(sourceproviders.PullTag)
|
||||
@@ -84,6 +96,10 @@ func (c *GetSourceConfig) getProviders(userInput string) ([]source.Provider, err
|
||||
return providers.Values(), nil
|
||||
}
|
||||
|
||||
func (c *GetSourceConfig) requestsSource(name string) bool {
|
||||
return c.DefaultImagePullSource == name || slices.Contains(c.Sources, name)
|
||||
}
|
||||
|
||||
func DefaultGetSourceConfig() *GetSourceConfig {
|
||||
return &GetSourceConfig{
|
||||
SourceProviderConfig: sourceproviders.DefaultConfig(),
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
package syft
|
||||
|
||||
import (
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
@@ -17,8 +23,9 @@ func TestGetProviders_DefaultImagePullSource(t *testing.T) {
|
||||
t.Errorf("Expected no error for DefaultImagePullSource parameter, got: %v", err)
|
||||
}
|
||||
|
||||
if len(providers) != len(allSourceProviders) {
|
||||
t.Errorf("Expected %d providers, got %d", len(allSourceProviders), len(providers))
|
||||
// everything except containers-storage, which is only used when requested by name
|
||||
if len(providers) != len(allSourceProviders)-1 {
|
||||
t.Errorf("Expected %d providers, got %d", len(allSourceProviders)-1, len(providers))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,3 +43,55 @@ func TestGetProviders_Sources(t *testing.T) {
|
||||
t.Errorf("Expected 2 providers, got %d", len(providers))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetProviders_ContainersStorageOnlyWhenRequested(t *testing.T) {
|
||||
const storage = image.ContainersStorageSource
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg *GetSourceConfig
|
||||
wantFound bool
|
||||
// if set, containers-storage must be ordered ahead of this provider
|
||||
wantBefore string
|
||||
}{
|
||||
{
|
||||
name: "excluded from automatic resolution",
|
||||
cfg: DefaultGetSourceConfig(),
|
||||
},
|
||||
{
|
||||
name: "excluded when selecting all pull sources",
|
||||
cfg: DefaultGetSourceConfig().WithSources(sourceproviders.PullTag),
|
||||
},
|
||||
{
|
||||
name: "excluded when a different default pull source is set",
|
||||
cfg: DefaultGetSourceConfig().WithDefaultImagePullSource(stereoscope.RegistryTag),
|
||||
},
|
||||
{
|
||||
name: "included when explicitly selected",
|
||||
cfg: DefaultGetSourceConfig().WithSources(storage),
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "included ahead of other pull sources when it is the default pull source",
|
||||
cfg: DefaultGetSourceConfig().WithDefaultImagePullSource(storage),
|
||||
wantFound: true,
|
||||
wantBefore: image.DockerDaemonSource,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
providers, err := tt.cfg.getProviders("localhost/myimage:latest")
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, providers)
|
||||
|
||||
assert.Equal(t, tt.wantFound, providerIndex(providers, storage) >= 0)
|
||||
if tt.wantBefore != "" {
|
||||
assert.Less(t, providerIndex(providers, storage), providerIndex(providers, tt.wantBefore))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func providerIndex(providers []source.Provider, name string) int {
|
||||
return slices.IndexFunc(providers, func(p source.Provider) bool { return p.Name() == name })
|
||||
}
|
||||
|
||||
@@ -210,8 +210,13 @@ func (r ChrootContext) ToChrootPath(nativePath string) string {
|
||||
prefix = r.base
|
||||
} else {
|
||||
// we need to account for the cwd relative to the running process and the given root for the directory resolver
|
||||
prefix = filepath.Clean(filepath.Join(r.cwd, r.cwdRelativeToRoot))
|
||||
prefix += string(filepath.Separator)
|
||||
prefix = r.cwdRelativeToRoot
|
||||
// on windows an absolute root stays absolute here (see ChangeDirectory), and joining it onto the cwd
|
||||
// would nest one volume inside another (D:\cwd\C:\root)
|
||||
if !filepath.IsAbs(prefix) {
|
||||
prefix = filepath.Join(r.cwd, prefix)
|
||||
}
|
||||
prefix = filepath.Clean(prefix) + string(filepath.Separator)
|
||||
}
|
||||
responsePath = strings.TrimPrefix(responsePath, prefix)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package fileresolver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// windows CI runs from D: while t.TempDir() lives on C:, so the root and cwd don't share a volume
|
||||
func TestChrootContext_ToChrootPath_rootOnAnotherVolume(t *testing.T) {
|
||||
ctx := ChrootContext{
|
||||
root: `C:\scan\root`,
|
||||
cwd: `D:\work`,
|
||||
cwdRelativeToRoot: `C:\scan\root`,
|
||||
}
|
||||
assert.Equal(t, "dir/file.txt", ctx.ToChrootPath("/c/scan/root/dir/file.txt"))
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
@@ -91,6 +92,7 @@ func TestAllLayersResolver_FilesByPath(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -201,6 +203,7 @@ func TestAllLayersResolver_FilesByGlob(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -249,6 +252,7 @@ func Test_imageAllLayersResolver_FilesByMIMEType(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixtureName, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName)
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -266,6 +270,7 @@ func Test_imageAllLayersResolver_FilesByMIMEType(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_imageAllLayersResolver_hasFilesystemIDInLocation(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -326,6 +331,7 @@ func TestAllLayersImageResolver_FilesContents(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -353,6 +359,7 @@ func TestAllLayersImageResolver_FilesContents(t *testing.T) {
|
||||
|
||||
func TestAllLayersImageResolver_FilesContents_errorOnDirRequest(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -500,6 +507,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -514,6 +522,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAllLayersResolver_AllLocations(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"go.uber.org/goleak"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
@@ -115,6 +116,7 @@ func Test_ContainerImageDeepSquash_FilesByPath(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -231,6 +233,7 @@ func Test_ContainerImageDeepSquash_FilesByGlob(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -288,6 +291,7 @@ func Test_ContainerImageDeepSquash_FilesByMIMEType(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixtureName, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName)
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -305,6 +309,7 @@ func Test_ContainerImageDeepSquash_FilesByMIMEType(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ContainerImageDeepSquash_hasFilesystemIDInLocation(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -365,6 +370,7 @@ func Test_ContainerImageDeepSquash_FilesContents(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -389,6 +395,7 @@ func Test_ContainerImageDeepSquash_FilesContents(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ContainerImageDeepSquash_FilesContents_errorOnDirRequest(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -557,6 +564,7 @@ func Test_ContainerImageDeepSquash_resolvesLinks(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -571,6 +579,7 @@ func Test_ContainerImageDeepSquash_resolvesLinks(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ContainerImageDeepSquash_AllLocations(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
@@ -75,6 +76,7 @@ func TestImageSquashResolver_FilesByPath(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -184,6 +186,7 @@ func TestImageSquashResolver_FilesByGlob(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -241,6 +244,7 @@ func Test_imageSquashResolver_FilesByMIMEType(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixtureName, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName)
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -258,6 +262,7 @@ func Test_imageSquashResolver_FilesByMIMEType(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_imageSquashResolver_hasFilesystemIDInLocation(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -316,6 +321,7 @@ func TestSquashImageResolver_FilesContents(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -341,6 +347,7 @@ func TestSquashImageResolver_FilesContents(t *testing.T) {
|
||||
|
||||
func TestSquashImageResolver_FilesContents_errorOnDirRequest(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -499,6 +506,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -545,6 +553,7 @@ func locationSorter(a, b file.Location) int {
|
||||
}
|
||||
|
||||
func TestSquashResolver_AllLocations(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -580,6 +589,7 @@ func TestSquashResolver_AllLocations(t *testing.T) {
|
||||
// content and metadata), so that image results are in parity with directory results (which cannot distinguish a
|
||||
// hardlink from a regular file). this must hold for both the squashed and all-layers resolvers.
|
||||
func TestImageResolvers_Hardlinks(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-hardlinks")
|
||||
|
||||
resolvers := map[string]file.Resolver{}
|
||||
|
||||
@@ -351,17 +351,12 @@ func (r directoryIndexer) addFileToIndex(p string, info os.FileInfo) error {
|
||||
func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string, error) {
|
||||
linkTarget, err := os.Readlink(p)
|
||||
if err != nil {
|
||||
isOnWindows := windows.HostRunningOnWindows()
|
||||
if isOnWindows {
|
||||
p = windows.FromPosix(p)
|
||||
}
|
||||
|
||||
linkTarget, err = filepath.EvalSymlinks(p)
|
||||
|
||||
if isOnWindows {
|
||||
p = windows.ToPosix(p)
|
||||
nativePath := p
|
||||
if windows.HostRunningOnWindows() {
|
||||
nativePath = windows.FromPosix(p)
|
||||
}
|
||||
|
||||
linkTarget, err = filepath.EvalSymlinks(nativePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to readlink for path=%q: %w", p, err)
|
||||
}
|
||||
@@ -414,16 +409,21 @@ func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string,
|
||||
}
|
||||
}
|
||||
|
||||
ref, err := r.tree.AddSymLink(file.Path(p), file.Path(linkTarget))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
targetAbsPath := linkTarget
|
||||
if !filepath.IsAbs(targetAbsPath) {
|
||||
targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget))
|
||||
}
|
||||
|
||||
// the tree is posix (like p), but the joins above produce native separators on windows
|
||||
if windows.HostRunningOnWindows() {
|
||||
linkTarget = windows.ToPosix(linkTarget)
|
||||
}
|
||||
|
||||
ref, err := r.tree.AddSymLink(file.Path(p), file.Path(linkTarget))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
metadata := NewMetadataFromPath(p, info)
|
||||
metadata.LinkDestination = linkTarget
|
||||
r.index.Add(*ref, metadata)
|
||||
|
||||
@@ -35,9 +35,19 @@ func nativeOSFileOpener(ref stereoscopeFile.Reference) (io.ReadCloser, error) {
|
||||
return stereoscopeFile.NewLazyReadCloser(filePath), nil
|
||||
}
|
||||
|
||||
// virtual reports whether the tree is not backed by the host filesystem (e.g. a squashfs), in which case there is
|
||||
// no chroot and request/response paths are already the posix tree paths. Mapping them through the host (as the
|
||||
// chroot does) would root them on the current volume on windows (/payload.txt -> /d/payload.txt).
|
||||
func (r FiletreeResolver) virtual() bool {
|
||||
return r.Chroot == ChrootContext{}
|
||||
}
|
||||
|
||||
// requestPath converts a user (chroot) path into the path the file tree is keyed on. The tree is always posix
|
||||
// (windows paths are volume-encoded, e.g. /c/some/path), while ToNativePath yields a native host path.
|
||||
func (r *FiletreeResolver) requestPath(userPath string) (string, error) {
|
||||
if r.virtual() {
|
||||
return userPath, nil
|
||||
}
|
||||
nativePath, err := r.Chroot.ToNativePath(userPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -50,6 +60,9 @@ func (r *FiletreeResolver) requestPath(userPath string) (string, error) {
|
||||
|
||||
// responsePath takes a path from the underlying fs domain and converts it to a path that is relative to the root of the file resolver.
|
||||
func (r FiletreeResolver) responsePath(path string) string {
|
||||
if r.virtual() {
|
||||
return path
|
||||
}
|
||||
return r.Chroot.ToChrootPath(path)
|
||||
}
|
||||
|
||||
@@ -111,6 +124,9 @@ func (r FiletreeResolver) FilesByPath(userPaths ...string) ([]file.Location, err
|
||||
|
||||
// requestGlob is the glob equivalent of requestPath.
|
||||
func (r FiletreeResolver) requestGlob(pattern string) (string, error) {
|
||||
if r.virtual() {
|
||||
return pattern, nil
|
||||
}
|
||||
glob, err := r.Chroot.ToNativeGlob(pattern)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -522,14 +523,15 @@ func TestSafeTensorsMergeProcessor(t *testing.T) {
|
||||
|
||||
t.Run("dir scan: config.json _name_or_path beats the parent directory fallback", func(t *testing.T) {
|
||||
// When a sibling config.json carries _name_or_path
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "config.json"),
|
||||
// (location paths are posix, so keep the temp dir slash-separated for windows)
|
||||
dir := filepath.ToSlash(t.TempDir())
|
||||
require.NoError(t, os.WriteFile(path.Join(dir, "config.json"),
|
||||
[]byte(`{"_name_or_path":"org/preferred-name"}`), 0o644))
|
||||
stPath := filepath.Join(dir, "weights.safetensors")
|
||||
stPath := path.Join(dir, "weights.safetensors")
|
||||
p := dirPkg(stPath, pkg.SafeTensorsModelInfo{
|
||||
Format: "safetensors", TensorCount: 1, MetadataHash: "abc",
|
||||
})
|
||||
resolver := file.NewMockResolverForPaths(filepath.Join(dir, "config.json"))
|
||||
resolver := file.NewMockResolverForPaths(path.Join(dir, "config.json"))
|
||||
out, _, err := safeTensorsMergeProcessor(context.Background(), resolver, []pkg.Package{p}, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, out, 1)
|
||||
|
||||
@@ -114,7 +114,7 @@ func fetchPkgFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, f
|
||||
|
||||
func fetchBackupFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, file.Location, error) {
|
||||
// We only really do this to get any backup database entries from the files database
|
||||
target := filepath.Join(base, "files")
|
||||
target := path.Join(base, "files")
|
||||
|
||||
loc, err := getLocation(target, resolver)
|
||||
if err != nil {
|
||||
@@ -395,7 +395,9 @@ func parseMtreeWithLimits(r io.Reader, maxSize int64, maxLines int) ([]pkg.AlpmF
|
||||
if ok := ignoredFiles[f.Name]; ok {
|
||||
continue
|
||||
}
|
||||
path := fmt.Sprintf("/%s", f.Name)
|
||||
// go-mtree runs filepath.Clean on entry names, which turns them into host paths on windows.
|
||||
// ponytail: a vis-encoded backslash (\134) in a name gets mangled on windows only, unvis first if that matters
|
||||
path := fmt.Sprintf("/%s", filepath.ToSlash(f.Name))
|
||||
fileFields["path"] = path
|
||||
for _, kv := range f.Keywords {
|
||||
kw := string(kv.Keyword())
|
||||
|
||||
@@ -155,10 +155,10 @@ catalogers:
|
||||
packages:
|
||||
- class: perl-binary
|
||||
name: perl
|
||||
purl: pkg:generic/perl
|
||||
purl: pkg:cpan/perl
|
||||
cpes:
|
||||
- cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
type: CpanPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/composer*'
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -460,8 +461,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
expected: pkg.Package{
|
||||
Name: "perl",
|
||||
Version: "5.12.5",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/perl@5.12.5",
|
||||
Type: "cpan",
|
||||
PURL: "pkg:cpan/perl@5.12.5",
|
||||
Locations: locations("perl"),
|
||||
Metadata: metadata("perl-binary"),
|
||||
},
|
||||
@@ -473,8 +474,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
expected: pkg.Package{
|
||||
Name: "perl",
|
||||
Version: "5.20.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/perl@5.20.0",
|
||||
Type: "cpan",
|
||||
PURL: "pkg:cpan/perl@5.20.0",
|
||||
Locations: locations("perl"),
|
||||
Metadata: metadata("perl-binary"),
|
||||
},
|
||||
@@ -486,8 +487,8 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
expected: pkg.Package{
|
||||
Name: "perl",
|
||||
Version: "5.37.8",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/perl@5.37.8",
|
||||
Type: "cpan",
|
||||
PURL: "pkg:cpan/perl@5.37.8",
|
||||
Locations: locations("perl"),
|
||||
Metadata: metadata("perl-binary"),
|
||||
},
|
||||
@@ -3169,6 +3170,7 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureImage)
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: test.fixtureImage,
|
||||
@@ -3187,6 +3189,58 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// Test_Cataloger_PackageTypes covers what assertPackagesAreEqual deliberately ignores: the package type and CPEs.
|
||||
// The perl interpreter is reported as cpan so it lines up with the rest of the CPAN ecosystem, everything else
|
||||
// keeps the default binary type.
|
||||
func Test_Cataloger_PackageTypes(t *testing.T) {
|
||||
tests := []struct {
|
||||
logicalFixture string
|
||||
wantType pkg.Type
|
||||
wantPURL string
|
||||
wantCPEs []string
|
||||
}{
|
||||
{
|
||||
logicalFixture: "perl/5.20.0/linux-amd64",
|
||||
wantType: pkg.CpanPkg,
|
||||
wantPURL: "pkg:cpan/perl@5.20.0",
|
||||
wantCPEs: []string{"cpe:2.3:a:perl:perl:5.20.0:*:*:*:*:*:*:*"},
|
||||
},
|
||||
{
|
||||
logicalFixture: "haproxy/1.5.14/linux-amd64",
|
||||
wantType: pkg.BinaryPkg,
|
||||
wantPURL: "pkg:generic/haproxy@1.5.14",
|
||||
wantCPEs: []string{"cpe:2.3:a:haproxy:haproxy:1.5.14:*:*:*:*:*:*:*"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.logicalFixture, func(t *testing.T) {
|
||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||
|
||||
path := testutil.SnippetOrBinary(t, test.logicalFixture, *mustUseOriginalBinaries)
|
||||
|
||||
src, err := directorysource.NewFromPath(path)
|
||||
require.NoError(t, err)
|
||||
|
||||
resolver, err := src.FileResolver(source.SquashedScope)
|
||||
require.NoError(t, err)
|
||||
|
||||
packages, _, err := c.Catalog(context.Background(), resolver)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, packages, 1)
|
||||
|
||||
var cpes []string
|
||||
for _, c := range packages[0].CPEs {
|
||||
cpes = append(cpes, c.Attributes.BindToFmtString())
|
||||
}
|
||||
|
||||
assert.Equal(t, test.wantType, packages[0].Type)
|
||||
assert.Equal(t, test.wantPURL, packages[0].PURL)
|
||||
assert.Equal(t, test.wantCPEs, cpes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
|
||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/binutils"
|
||||
)
|
||||
|
||||
@@ -251,8 +252,9 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)\/usr\/local\/lib\/perl\d\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
Package: "perl",
|
||||
PURL: mustPURL("pkg:generic/perl@version"),
|
||||
PURL: mustPURL("pkg:cpan/perl@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
Type: pkg.CpanPkg,
|
||||
},
|
||||
{
|
||||
Class: "php-composer-binary",
|
||||
|
||||
+5
-4
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -123,10 +124,10 @@ func TestAllStorePaths(t *testing.T) {
|
||||
},
|
||||
dest: "dest",
|
||||
expected: []string{
|
||||
"dest/test/1.0/platform1/test1",
|
||||
"dest/test/1.0/platform1/test2",
|
||||
"dest/test/1.0/platform2/test1",
|
||||
"dest/test/1.0/platform2/test2",
|
||||
filepath.FromSlash("dest/test/1.0/platform1/test1"),
|
||||
filepath.FromSlash("dest/test/1.0/platform1/test2"),
|
||||
filepath.FromSlash("dest/test/1.0/platform2/test1"),
|
||||
filepath.FromSlash("dest/test/1.0/platform2/test2"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
@@ -167,7 +168,7 @@ func getLogicalKey(managedBinaryPath string) (*LogicalEntryKey, error) {
|
||||
OrgName: items[0],
|
||||
Version: items[1],
|
||||
Platform: items[2],
|
||||
Filename: filepath.Join(items[3:]...),
|
||||
Filename: path.Join(items[3:]...), // posix, since this is part of the logical key
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ func TestListAllBinaries(t *testing.T) {
|
||||
|
||||
assert.Equal(t,
|
||||
Entries{
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: "testdata/bin/busybox/1.3.6/linux-amd64/busybox", SnippetPath: ""},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: "testdata/bin/busybox/1.3.6/linux-arm64/busybox", SnippetPath: ""},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-amd64/busybox"), SnippetPath: ""},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-arm64/busybox"), SnippetPath: ""},
|
||||
},
|
||||
entries,
|
||||
)
|
||||
@@ -105,10 +105,10 @@ func TestListAllEntries(t *testing.T) {
|
||||
|
||||
assert.Equal(t,
|
||||
Entries{
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: "testdata/bin/busybox/1.3.6/linux-amd64/busybox", SnippetPath: "testdata/snippets/busybox/1.3.6/linux-amd64/busybox"},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: "testdata/bin/busybox/1.3.6/linux-arm64/busybox", SnippetPath: "testdata/snippets/busybox/1.3.6/linux-arm64/busybox"},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-amd64/busybox"), SnippetPath: filepath.FromSlash("testdata/snippets/busybox/1.3.6/linux-amd64/busybox")},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-arm64/busybox"), SnippetPath: filepath.FromSlash("testdata/snippets/busybox/1.3.6/linux-arm64/busybox")},
|
||||
// note the standalone snippet!
|
||||
LogicalEntryKey{OrgName: "postgres", Version: "9.6.10", Platform: "linux-amd64", Filename: "postgres"}: EntryInfo{IsConfigured: false, BinaryPath: "", SnippetPath: "testdata/snippets/postgres/9.6.10/linux-amd64/postgres"},
|
||||
LogicalEntryKey{OrgName: "postgres", Version: "9.6.10", Platform: "linux-amd64", Filename: "postgres"}: EntryInfo{IsConfigured: false, BinaryPath: "", SnippetPath: filepath.FromSlash("testdata/snippets/postgres/9.6.10/linux-amd64/postgres")},
|
||||
},
|
||||
entries,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -13,7 +14,7 @@ func TestSplitFilepath(t *testing.T) {
|
||||
path string
|
||||
expected []string
|
||||
}{
|
||||
{path: "a/b/c", expected: []string{"a", "b", "c"}},
|
||||
{path: filepath.Join("a", "b", "c"), expected: []string{"a", "b", "c"}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -3,7 +3,9 @@ package testutil
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -18,10 +20,11 @@ import (
|
||||
// not specify the "bin" or "snippets" parent path... this is determined logically [snippets > binary unless told
|
||||
// otherwise]). Path should also be to the directory containing the binary or snippets of interest (not the binaries
|
||||
// or snippets itself).
|
||||
func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string {
|
||||
func SnippetOrBinary(t *testing.T, logicalPath string, requireBinary bool) string {
|
||||
t.Helper()
|
||||
|
||||
require.Len(t, internal.SplitFilepath(path), 3, "path must be a in the form <name>/<version>/<arch>")
|
||||
// logicalPath is a posix key, not a host path
|
||||
require.Len(t, strings.Split(logicalPath, "/"), 3, "path must be a in the form <name>/<version>/<arch>")
|
||||
|
||||
// cd to testdata directory and load the config
|
||||
|
||||
@@ -43,11 +46,11 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string {
|
||||
|
||||
var fixturePath string
|
||||
for k, v := range entries.Sorted() {
|
||||
if filepath.Dir(k.Path()) == path {
|
||||
if path.Dir(k.Path()) == logicalPath {
|
||||
// prefer the snippet over the binary
|
||||
if !requireBinary {
|
||||
if v.SnippetPath != "" {
|
||||
t.Logf("using snippet for %q", path)
|
||||
t.Logf("using snippet for %q", logicalPath)
|
||||
require.NoError(t, validateSnippet(v.BinaryPath, v.SnippetPath))
|
||||
fixturePath = v.SnippetPath
|
||||
break
|
||||
@@ -56,10 +59,10 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string {
|
||||
fixturePath = v.BinaryPath
|
||||
break
|
||||
}
|
||||
t.Fatalf("no binary or snippet found for %q", path)
|
||||
t.Fatalf("no binary or snippet found for %q", logicalPath)
|
||||
}
|
||||
if v.BinaryPath != "" {
|
||||
t.Logf("forcing the use of the original binary for %q", path)
|
||||
t.Logf("forcing the use of the original binary for %q", logicalPath)
|
||||
fixturePath = v.BinaryPath
|
||||
break
|
||||
}
|
||||
@@ -68,12 +71,12 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string {
|
||||
t.Skip("no binary found, but is covered by a snippet. Please add this case to the 'binary/testdata/config.yaml' and recreate the snippet")
|
||||
}
|
||||
|
||||
t.Fatalf("no binary found for %q", path)
|
||||
t.Fatalf("no binary found for %q", logicalPath)
|
||||
}
|
||||
}
|
||||
|
||||
if fixturePath == "" {
|
||||
t.Fatalf("no fixture found for %q", path)
|
||||
t.Fatalf("no fixture found for %q", logicalPath)
|
||||
}
|
||||
|
||||
// this should be relative to the tests-fixtures directory and should be the directory containing the binary or
|
||||
@@ -101,6 +104,7 @@ func validateSnippet(binaryPath, snippetPath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
expected, err := internal.Sha256SumFile(f)
|
||||
if err != nil {
|
||||
|
||||
@@ -5,7 +5,7 @@ package bitnami
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
@@ -73,7 +73,7 @@ func parseSBOM(_ context.Context, resolver file.Resolver, _ *generic.Environment
|
||||
|
||||
// Bitnami packages reported in a SPDX file are shipped under the same directory
|
||||
// as the SPDX file itself.
|
||||
metadata.Path = filepath.Dir(reader.RealPath)
|
||||
metadata.Path = path.Dir(reader.RealPath)
|
||||
if p.ID() != mainPkgID {
|
||||
metadata.Files = packageFiles(s.Relationships, p, metadata.Path)
|
||||
secondaryPkgsFiles = append(secondaryPkgsFiles, metadata.Files...)
|
||||
|
||||
@@ -3,7 +3,6 @@ package bitnami
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -73,7 +72,7 @@ func packageFiles(relationships []artifact.Relationship, p pkg.Package, baseDire
|
||||
|
||||
// mainPkgFiles returns the files owned by the main package in the SPDX file.
|
||||
func mainPkgFiles(resolver file.Resolver, spdxFilePath string, secondaryPkgsFiles []string) ([]string, error) {
|
||||
ownedPathGlob := fmt.Sprintf("%s/**", filepath.Dir(spdxFilePath))
|
||||
ownedPathGlob := fmt.Sprintf("%s/**", path.Dir(spdxFilePath))
|
||||
ownedLocations, err := resolver.FilesByGlob(ownedPathGlob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -67,7 +67,7 @@ func parseComponentsJSON(_ context.Context, _ file.Resolver, _ *generic.Environm
|
||||
Revision: revision,
|
||||
Architecture: entry.Arch,
|
||||
Distro: entry.Distro,
|
||||
Path: filepath.Join(filepath.Dir(reader.RealPath), name),
|
||||
Path: path.Join(path.Dir(reader.RealPath), name),
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
|
||||
@@ -75,6 +75,7 @@ catalogers:
|
||||
method: glob # AUTO-GENERATED
|
||||
criteria: # AUTO-GENERATED
|
||||
- '**/*.deb'
|
||||
- '**/*.ipk'
|
||||
metadata_types: # AUTO-GENERATED
|
||||
- pkg.DpkgArchiveEntry
|
||||
package_types: # AUTO-GENERATED
|
||||
|
||||
@@ -18,8 +18,8 @@ func NewDBCataloger() pkg.Cataloger {
|
||||
WithProcessors(dependency.Processor(dbEntryDependencySpecifier))
|
||||
}
|
||||
|
||||
// NewArchiveCataloger returns a new Debian package cataloger object capable of parsing .deb archive files
|
||||
// NewArchiveCataloger returns a new Debian package cataloger object capable of parsing .deb and .ipk (opkg) archive files
|
||||
func NewArchiveCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("deb-archive-cataloger").
|
||||
WithParserByGlobs(parseDebArchive, "**/*.deb")
|
||||
WithParserByGlobs(parseDebArchive, "**/*.deb", "**/*.ipk")
|
||||
}
|
||||
|
||||
@@ -276,6 +276,36 @@ func TestDpkgArchiveCataloger(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
}, {
|
||||
name: "image-single-ipk",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "libz1",
|
||||
Version: "1.3.2-r0",
|
||||
FoundBy: "deb-archive-cataloger",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("/libz1_1.3.2-r0_x86-64-v3.ipk"),
|
||||
),
|
||||
Type: pkg.DebPkg,
|
||||
Metadata: pkg.DpkgArchiveEntry{
|
||||
Package: "libz1",
|
||||
Source: "zlib",
|
||||
SourceVersion: "1.3.2",
|
||||
Version: "1.3.2-r0",
|
||||
Architecture: "x86-64-v3",
|
||||
Maintainer: "Poky Maintainers <poky@lists.yoctoproject.org>",
|
||||
InstalledSize: 0,
|
||||
Description: `Zlib Compression Library
|
||||
Zlib is a general-purpose, patent-free, lossless data compression library
|
||||
which is used by many different programs.`,
|
||||
Provides: []string{"zlib"},
|
||||
Depends: []string{"musl (>= 1.2.6+git0+9fa28ece75)"},
|
||||
},
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocationsWithContext(ctx, "Zlib"),
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
@@ -61,32 +63,65 @@ func newDpkgPackage(ctx context.Context, d dpkgExtractedMetadata, dbLocation fil
|
||||
return p
|
||||
}
|
||||
|
||||
// a declared License field holds a single expression, a few hundred bytes at most in practice. Anything
|
||||
// much larger only feeds the expression parser (which recurses on parentheses) attacker-sized input.
|
||||
const maxDeclaredLicenseLen = 4096
|
||||
|
||||
var (
|
||||
// opkg/ipkg (Yocto via bitbake) declare licenses with "&" for AND and "|" for OR
|
||||
bitbakeLicenseOperators = strings.NewReplacer("&", " AND ", "|", " OR ")
|
||||
tightenParens = strings.NewReplacer("( ", "(", " )", ")")
|
||||
licenseListSeparators = strings.NewReplacer("(", " ", ")", " ")
|
||||
)
|
||||
|
||||
// extractDeclaredLicenses converts a License field from the status DB into a license set. Returns nil
|
||||
// for empty input so standard dpkg entries (which never declare License inline) incur no allocation.
|
||||
// Mirrors the alpine cataloger's approach: keep the value whole if it parses as a valid SPDX expression,
|
||||
// otherwise split on whitespace to handle space-separated lists.
|
||||
func extractDeclaredLicenses(ctx context.Context, raw string, dbLocation file.Location) []pkg.License {
|
||||
values := declaredLicenseValues(raw)
|
||||
if len(values) == 0 {
|
||||
return nil
|
||||
}
|
||||
return pkg.NewLicensesFromLocationWithContext(ctx, dbLocation, values...)
|
||||
}
|
||||
|
||||
// declaredLicenseValues converts an inline License field (opkg status DB or an archive control file) into
|
||||
// license values. Bitbake operators are rewritten to SPDX ones and the value is kept whole if it then parses
|
||||
// as a valid SPDX expression (mirroring the alpine cataloger), otherwise it is treated as a list of license
|
||||
// names with operators and grouping dropped.
|
||||
func declaredLicenseValues(raw string) []string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return nil
|
||||
}
|
||||
licenseStrings := []string{raw}
|
||||
if _, err := license.ParseExpression(raw); err != nil {
|
||||
licenseStrings = strings.Fields(raw)
|
||||
if len(raw) > maxDeclaredLicenseLen {
|
||||
log.WithFields("length", len(raw)).Debug("ignoring oversized declared license field")
|
||||
return nil
|
||||
}
|
||||
return pkg.NewLicensesFromLocationWithContext(ctx, dbLocation, licenseStrings...)
|
||||
|
||||
expr := tightenParens.Replace(strings.Join(strings.Fields(bitbakeLicenseOperators.Replace(raw)), " "))
|
||||
if _, err := license.ParseExpression(expr); err == nil {
|
||||
return []string{expr}
|
||||
}
|
||||
|
||||
var values []string
|
||||
seen := strset.New()
|
||||
for _, field := range strings.Fields(licenseListSeparators.Replace(expr)) {
|
||||
if field == "AND" || field == "OR" || seen.Has(field) {
|
||||
continue
|
||||
}
|
||||
seen.Add(field)
|
||||
values = append(values, field)
|
||||
}
|
||||
return values
|
||||
}
|
||||
|
||||
func newDebArchivePackage(ctx context.Context, location file.Location, metadata pkg.DpkgArchiveEntry, licenseStrings []string) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: metadata.Package,
|
||||
Version: metadata.Version,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValuesWithContext(ctx, licenseStrings...)...),
|
||||
Type: pkg.DebPkg,
|
||||
PURL: packageURL(
|
||||
pkg.DpkgDBEntry(metadata),
|
||||
// we don't know the distro information, but since this is a deb file then we can reasonably assume it is a debian-based distro
|
||||
&linux.Release{IDLike: []string{"debian"}},
|
||||
),
|
||||
Name: metadata.Package,
|
||||
Version: metadata.Version,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValuesWithContext(ctx, licenseStrings...)...),
|
||||
Type: pkg.DebPkg,
|
||||
PURL: packageURL(pkg.DpkgDBEntry(metadata), archiveDistro(location)),
|
||||
Metadata: metadata,
|
||||
Locations: file.NewLocationSet(location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
}
|
||||
@@ -95,6 +130,17 @@ func newDebArchivePackage(ctx context.Context, location file.Location, metadata
|
||||
return p
|
||||
}
|
||||
|
||||
// archiveDistro guesses the distro an archive was built for. We don't know the distro information, but a .deb
|
||||
// can reasonably be assumed to target a debian-based distro. An .ipk targets opkg-based distros (Yocto,
|
||||
// OpenWrt) which have no purl type, so it gets no distro and therefore no PURL, the same as packages found in
|
||||
// an opkg status DB.
|
||||
func archiveDistro(location file.Location) *linux.Release {
|
||||
if strings.EqualFold(path.Ext(location.Path()), ".ipk") {
|
||||
return nil
|
||||
}
|
||||
return &linux.Release{IDLike: []string{"debian"}}
|
||||
}
|
||||
|
||||
// PackageURL returns the PURL for the specific Debian package (see https://github.com/package-url/purl-spec)
|
||||
func packageURL(m pkg.DpkgDBEntry, distro *linux.Release) string {
|
||||
if distro == nil {
|
||||
|
||||
@@ -3,6 +3,7 @@ package debian
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -145,6 +146,36 @@ func Test_extractDeclaredLicenses(t *testing.T) {
|
||||
raw: "GPL-2.0 BSD-3-Clause",
|
||||
expected: []string{"GPL-2.0", "BSD-3-Clause"},
|
||||
},
|
||||
{
|
||||
name: "bitbake operators become an SPDX expression",
|
||||
raw: "MIT & (BSD-3-Clause | Apache-2.0) & ( GPL-2.0-or-later )",
|
||||
expected: []string{"MIT AND (BSD-3-Clause OR Apache-2.0) AND (GPL-2.0-or-later)"},
|
||||
},
|
||||
{
|
||||
name: "bitbake grouping is preserved rather than split",
|
||||
raw: "(MIT & BSD-3-Clause) | Apache-2.0",
|
||||
expected: []string{"(MIT AND BSD-3-Clause) OR Apache-2.0"},
|
||||
},
|
||||
{
|
||||
name: "parenthesized alternatives are preserved",
|
||||
raw: "(MIT) | (Apache-2.0)",
|
||||
expected: []string{"(MIT) OR (Apache-2.0)"},
|
||||
},
|
||||
{
|
||||
name: "non-SPDX names drop operators and grouping",
|
||||
raw: "GPLv2+ & (LGPLv2.1 | BSD) & GPLv2+",
|
||||
expected: []string{"GPLv2+", "LGPLv2.1", "BSD"},
|
||||
},
|
||||
{
|
||||
name: "oversized field is ignored",
|
||||
raw: strings.Repeat("MIT & ", maxDeclaredLicenseLen),
|
||||
expected: nil,
|
||||
},
|
||||
{
|
||||
name: "deeply nested parens are ignored",
|
||||
raw: strings.Repeat("(", maxDeclaredLicenseLen) + "MIT" + strings.Repeat(")", maxDeclaredLicenseLen),
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -2,6 +2,7 @@ package debian
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -23,18 +24,27 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
|
||||
// parseDebArchive parses a Debian package archive (.deb) file and returns the packages it contains.
|
||||
// A .deb file is an ar archive containing three main files:
|
||||
// - debian-binary: Version of the .deb format (usually "2.0")
|
||||
// parseDebArchive parses a Debian package archive (.deb) or opkg package (.ipk) file and returns the packages it
|
||||
// contains. Both are ar archives containing three main files:
|
||||
// - debian-binary: Version of the archive format (usually "2.0")
|
||||
// - control.tar.gz/xz/zst: Contains package metadata (control file, md5sums, conffiles)
|
||||
// - data.tar.gz/xz/zst: Contains the actual files to be installed (not processed by this cataloger)
|
||||
//
|
||||
// This function extracts and processes the control information to create package metadata.
|
||||
// This function extracts and processes the control information to create package metadata. Licenses come
|
||||
// from copyright files in the data tar, falling back to the control file's License field (common for .ipk).
|
||||
// Only ar-framed .ipk files are supported; the older gzipped-tar framing (e.g. OpenWrt's ipkg-build) is
|
||||
// rejected as an invalid archive.
|
||||
func parseDebArchive(ctx context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
arReader := ar.NewReader(reader)
|
||||
validatedReader, err := newValidatedArReader(reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid ar archive: %w", err)
|
||||
}
|
||||
|
||||
arReader := ar.NewReader(validatedReader)
|
||||
|
||||
var metadata *pkg.DpkgArchiveEntry
|
||||
var licenses []string
|
||||
var ctrlLicenses []string
|
||||
var unknownErr error
|
||||
var sawControl, sawData bool
|
||||
for {
|
||||
@@ -56,7 +66,7 @@ func parseDebArchive(ctx context.Context, _ file.Resolver, _ *generic.Environmen
|
||||
if err != nil {
|
||||
return nil, nil, unknown.New(reader.Location, fmt.Errorf("failed to decompress control.tar.* file: %w", err))
|
||||
}
|
||||
metadata, err = processControlTar(dcReader)
|
||||
metadata, ctrlLicenses, err = processControlTar(dcReader)
|
||||
switch {
|
||||
case err != nil && metadata == nil:
|
||||
return nil, nil, unknown.New(reader.Location, fmt.Errorf("failed to process control.tar.* file: %w", err))
|
||||
@@ -87,6 +97,10 @@ func parseDebArchive(ctx context.Context, _ file.Resolver, _ *generic.Environmen
|
||||
return nil, nil, unknown.New(reader.Location, fmt.Errorf("no application found described in .dpkg archive"))
|
||||
}
|
||||
|
||||
if len(licenses) == 0 && len(ctrlLicenses) > 0 {
|
||||
licenses = ctrlLicenses
|
||||
}
|
||||
|
||||
// a partial parse still yields a usable package, so report what went wrong alongside it rather than
|
||||
// dropping either one
|
||||
return []pkg.Package{
|
||||
@@ -128,10 +142,10 @@ func processDataTar(dcReader io.ReadCloser) ([]string, error) {
|
||||
}
|
||||
|
||||
// processControlTar always returns whatever metadata it managed to parse, even alongside a non-nil
|
||||
// error. A non-nil error with non-nil metadata means the package is usable but incomplete (e.g. a
|
||||
// clipped file listing); nil metadata means nothing usable was found and the caller should treat it
|
||||
// as fatal.
|
||||
func processControlTar(dcReader io.ReadCloser) (*pkg.DpkgArchiveEntry, error) {
|
||||
// error, along with any licenses declared in the control file's License field. A non-nil error with
|
||||
// non-nil metadata means the package is usable but incomplete (e.g. a clipped file listing); nil
|
||||
// metadata means nothing usable was found and the caller should treat it as fatal.
|
||||
func processControlTar(dcReader io.ReadCloser) (*pkg.DpkgArchiveEntry, []string, error) {
|
||||
defer internal.CloseAndLogError(dcReader, "")
|
||||
|
||||
tarReader := tar.NewReader(dcReader)
|
||||
@@ -140,6 +154,7 @@ func processControlTar(dcReader io.ReadCloser) (*pkg.DpkgArchiveEntry, error) {
|
||||
var files []pkg.DpkgFileRecord
|
||||
var confFileRecords []pkg.DpkgFileRecord
|
||||
var listingErr error
|
||||
var licenses []string
|
||||
|
||||
for {
|
||||
header, err := tarReader.Next()
|
||||
@@ -147,7 +162,7 @@ func processControlTar(dcReader io.ReadCloser) (*pkg.DpkgArchiveEntry, error) {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return metadata, fmt.Errorf("failed to read control tar: %w", err)
|
||||
return metadata, nil, fmt.Errorf("failed to read control tar: %w", err)
|
||||
}
|
||||
|
||||
switch filepath.Base(header.Name) {
|
||||
@@ -155,12 +170,13 @@ func processControlTar(dcReader io.ReadCloser) (*pkg.DpkgArchiveEntry, error) {
|
||||
// parseDpkgStatus already streams via bufio.Reader
|
||||
entries, err := parseDpkgStatus(tarReader)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse control file: %w", err)
|
||||
return nil, nil, fmt.Errorf("failed to parse control file: %w", err)
|
||||
}
|
||||
if len(entries) == 0 {
|
||||
return nil, fmt.Errorf("no package entries found in control file")
|
||||
return nil, nil, fmt.Errorf("no package entries found in control file")
|
||||
}
|
||||
entry := pkg.DpkgArchiveEntry(entries[0].toDpkgEntry())
|
||||
licenses = declaredLicenseValues(entries[0].License)
|
||||
metadata = &entry
|
||||
case "md5sums":
|
||||
// parseDpkgMD5Info streams via bufio.Scanner and reports its own clipping/scan errors
|
||||
@@ -176,7 +192,7 @@ func processControlTar(dcReader io.ReadCloser) (*pkg.DpkgArchiveEntry, error) {
|
||||
}
|
||||
|
||||
if metadata == nil {
|
||||
return nil, fmt.Errorf("control file not found in archive")
|
||||
return nil, nil, fmt.Errorf("control file not found in archive")
|
||||
}
|
||||
|
||||
if len(confFileRecords) > 0 && len(files) > 0 {
|
||||
@@ -195,7 +211,22 @@ func processControlTar(dcReader io.ReadCloser) (*pkg.DpkgArchiveEntry, error) {
|
||||
|
||||
// a clipped or unreadable file listing leaves the package usable, so hand both back and let the
|
||||
// caller decide
|
||||
return metadata, listingErr
|
||||
return metadata, licenses, listingErr
|
||||
}
|
||||
|
||||
// newValidatedArReader checks for the ar global header before handing the stream to the ar reader, which
|
||||
// otherwise skips those bytes unchecked and reads garbage headers from non-ar input.
|
||||
func newValidatedArReader(reader io.ReadCloser) (io.ReadCloser, error) {
|
||||
prefix := make([]byte, len(ar.GLOBAL_HEADER))
|
||||
if _, err := io.ReadFull(reader, prefix); err != nil {
|
||||
return nil, fmt.Errorf("failed to read ar header: %w", err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(prefix, []byte(ar.GLOBAL_HEADER)) {
|
||||
return nil, fmt.Errorf("expected ar header %q, got %q", string(ar.GLOBAL_HEADER), string(prefix))
|
||||
}
|
||||
|
||||
return io.NopCloser(io.MultiReader(bytes.NewReader(prefix), reader)), nil
|
||||
}
|
||||
|
||||
// a .deb's member sizes are bounded by the archive, but what they decompress to is not: xz and zstd
|
||||
|
||||
@@ -26,12 +26,13 @@ import (
|
||||
func TestProcessControlTar(t *testing.T) {
|
||||
tarBytes := createTestTarWithControlFiles(t)
|
||||
|
||||
metadata, err := processControlTar(io.NopCloser(bytes.NewReader(tarBytes)))
|
||||
metadata, licenses, err := processControlTar(io.NopCloser(bytes.NewReader(tarBytes)))
|
||||
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, metadata)
|
||||
|
||||
assert.Equal(t, "test-package", metadata.Package)
|
||||
assert.Equal(t, []string{"MIT"}, licenses)
|
||||
assert.Equal(t, "1.0.0", metadata.Version)
|
||||
|
||||
// md5sums should have been parsed into file records
|
||||
@@ -43,6 +44,14 @@ func TestProcessControlTar(t *testing.T) {
|
||||
assert.True(t, metadata.Files[0].IsConfigFile, "file listed in conffiles should be marked as config")
|
||||
}
|
||||
|
||||
func TestParseDebArchive_RejectsInvalidArchiveHeader(t *testing.T) {
|
||||
reader := file.NewLocationReadCloser(file.NewLocation("test.deb"), io.NopCloser(bytes.NewReader([]byte("not-an-ar-archive"))))
|
||||
|
||||
_, _, err := parseDebArchive(context.Background(), nil, nil, reader)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "ar archive")
|
||||
}
|
||||
|
||||
func TestProcessControlTar_ConfigFileMarking(t *testing.T) {
|
||||
// Create a tar where conffiles lists paths that overlap with md5sums entries
|
||||
var buf bytes.Buffer
|
||||
@@ -61,9 +70,10 @@ func TestProcessControlTar_ConfigFileMarking(t *testing.T) {
|
||||
|
||||
require.NoError(t, tw.Close())
|
||||
|
||||
metadata, err := processControlTar(io.NopCloser(bytes.NewReader(buf.Bytes())))
|
||||
metadata, licenses, err := processControlTar(io.NopCloser(bytes.NewReader(buf.Bytes())))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, metadata.Files, 3)
|
||||
assert.Empty(t, licenses)
|
||||
|
||||
assert.True(t, metadata.Files[0].IsConfigFile, "first file should be marked as config file")
|
||||
assert.True(t, metadata.Files[1].IsConfigFile, "second file should be marked as config file")
|
||||
@@ -75,7 +85,7 @@ func createTestTarWithControlFiles(t *testing.T) []byte {
|
||||
var buf bytes.Buffer
|
||||
tw := tar.NewWriter(&buf)
|
||||
|
||||
controlContent := "Package: test-package\nVersion: 1.0.0\nArchitecture: all\nMaintainer: Test <test@example.com>\nDescription: Test package\n"
|
||||
controlContent := "Package: test-package\nVersion: 1.0.0\nArchitecture: all\nMaintainer: Test <test@example.com>\nDescription: Test package\nLicense: MIT\n"
|
||||
writeTarEntry(t, tw, "control", controlContent)
|
||||
|
||||
md5Content := "d41d8cd98f00b204e9800998ecf8427e usr/bin/test-command\n"
|
||||
@@ -165,7 +175,7 @@ func Test_processControlTar_rejectsABombedControlMember(t *testing.T) {
|
||||
writeTarEntry(t, tw, "md5sums", strings.Repeat("d41d8cd98f00b204e9800998ecf8427e usr/bin/x\n", 20000))
|
||||
require.NoError(t, tw.Close())
|
||||
|
||||
metadata, err := processControlTar(newBoundedReadCloser(io.NopCloser(bytes.NewReader(tarBuf.Bytes())), 4096))
|
||||
metadata, _, err := processControlTar(newBoundedReadCloser(io.NopCloser(bytes.NewReader(tarBuf.Bytes())), 4096))
|
||||
|
||||
require.ErrorIs(t, err, errDecompressedTooLarge)
|
||||
require.NotNil(t, metadata, "a control tar read failure after \"control\" was parsed must not drop the package")
|
||||
@@ -181,7 +191,7 @@ func Test_processControlTar_clippedListingIsUsableButReported(t *testing.T) {
|
||||
writeTarEntry(t, tw, "md5sums", strings.Repeat("d41d8cd98f00b204e9800998ecf8427e usr/bin/x\n", maxDpkgFileRecords+10))
|
||||
require.NoError(t, tw.Close())
|
||||
|
||||
metadata, err := processControlTar(io.NopCloser(bytes.NewReader(tarBuf.Bytes())))
|
||||
metadata, _, err := processControlTar(io.NopCloser(bytes.NewReader(tarBuf.Bytes())))
|
||||
|
||||
require.Error(t, err, "clipping must be reported")
|
||||
require.ErrorIs(t, err, errClippedFileListing)
|
||||
@@ -418,3 +428,18 @@ func Test_decompressionStream_failsEarlyInsteadOfBuffering(t *testing.T) {
|
||||
assert.Less(t, bounded, uint64(8*intFile.MB),
|
||||
"a bounded stream must stop at the cap, not buffer the whole member and check afterwards")
|
||||
}
|
||||
|
||||
func TestProcessControlTar_DeclaredLicense(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
tw := tar.NewWriter(&buf)
|
||||
|
||||
controlContent := "Package: test-package\nVersion: 1.0.0\nArchitecture: all\nLicense: MIT & (BSD-3-Clause | Apache-2.0) & ( GPL-2.0-or-later )\n"
|
||||
writeTarEntry(t, tw, "control", controlContent)
|
||||
|
||||
require.NoError(t, tw.Close())
|
||||
|
||||
metadata, licenses, err := processControlTar(io.NopCloser(bytes.NewReader(buf.Bytes())))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, metadata)
|
||||
assert.Equal(t, []string{"MIT AND (BSD-3-Clause OR Apache-2.0) AND (GPL-2.0-or-later)"}, licenses)
|
||||
}
|
||||
|
||||
@@ -183,9 +183,11 @@ func parseDpkgStatusEntry(reader *bufio.Reader) (*dpkgExtractedMetadata, error)
|
||||
}
|
||||
|
||||
sourceName, sourceVersion := extractSourceVersion(raw.Source)
|
||||
if sourceName != "" {
|
||||
raw.Source = sourceName
|
||||
}
|
||||
if sourceVersion != "" {
|
||||
raw.SourceVersion = sourceVersion
|
||||
raw.Source = sourceName
|
||||
}
|
||||
|
||||
if raw.Package == "" {
|
||||
@@ -263,8 +265,14 @@ func extractAllFields(reader *bufio.Reader) (map[string]any, error) {
|
||||
}
|
||||
|
||||
// If the source entry string is of the form "<name> (<version>)" then parse and return the components, if
|
||||
// of the "<name>" form, then return name and nil
|
||||
// of the "<name>" form, then return name and nil. Yocto (opkg/ipk) instead writes the bitbake recipe filename
|
||||
// ("<name>_<version>.bb"), which is split the same way.
|
||||
func extractSourceVersion(source string) (string, string) {
|
||||
if recipe, ok := strings.CutSuffix(source, ".bb"); ok && !strings.ContainsAny(recipe, " (") {
|
||||
// bitbake reserves "_" as the recipe name / version separator
|
||||
name, version, _ := strings.Cut(recipe, "_")
|
||||
return name, version
|
||||
}
|
||||
// special handling for the Source field since it has formatted data
|
||||
match := internal.MatchNamedCaptureGroups(sourceRegexp, source)
|
||||
return match["name"], match["version"]
|
||||
|
||||
@@ -366,6 +366,16 @@ func TestSourceVersionExtract(t *testing.T) {
|
||||
input: "",
|
||||
expected: []string{"", ""},
|
||||
},
|
||||
{
|
||||
name: "bitbake recipe with version",
|
||||
input: "zlib_1.3.2.bb",
|
||||
expected: []string{"zlib", "1.3.2"},
|
||||
},
|
||||
{
|
||||
name: "bitbake recipe without version",
|
||||
input: "packagegroup-core-boot.bb",
|
||||
expected: []string{"packagegroup-core-boot", ""},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
# assembles a Yocto-style .ipk the same way bitbake's package_ipk does (opkg-build: a GNU ar archive of
|
||||
# debian-binary, control.tar.gz and data.tar.zst) without paying for a full Yocto build. control/ is copied
|
||||
# verbatim from libz1_1.3.2-r0_x86-64-v3.ipk built by poky-tiny (wrynose); the library itself is a stub
|
||||
# since only the archive shape and control metadata matter here.
|
||||
FROM alpine:3.18.4@sha256:48d9183eb12a05c99bcc0bf44a003607b8e941e1d4f41f9ad12bdcc4b5672f86 AS build
|
||||
|
||||
RUN apk add --no-cache binutils zstd
|
||||
|
||||
COPY control /pkg/control
|
||||
|
||||
RUN mkdir -p /pkg/data/usr/lib \
|
||||
&& echo stub > /pkg/data/usr/lib/libz.so.1.3.2 \
|
||||
&& ln -s libz.so.1.3.2 /pkg/data/usr/lib/libz.so.1 \
|
||||
&& cd /pkg \
|
||||
&& echo 2.0 > debian-binary \
|
||||
&& tar -C control -czf control.tar.gz . \
|
||||
&& tar -C data -cf - . | zstd -q -o data.tar.zst \
|
||||
&& ar rcD /libz1_1.3.2-r0_x86-64-v3.ipk debian-binary control.tar.gz data.tar.zst
|
||||
|
||||
FROM scratch
|
||||
COPY --from=build /libz1_1.3.2-r0_x86-64-v3.ipk /
|
||||
@@ -0,0 +1,15 @@
|
||||
Package: libz1
|
||||
Version: 1.3.2-r0
|
||||
Description: Zlib Compression Library
|
||||
Zlib is a general-purpose, patent-free, lossless data compression library
|
||||
which is used by many different programs.
|
||||
Section: libs
|
||||
Priority: optional
|
||||
Maintainer: Poky Maintainers <poky@lists.yoctoproject.org>
|
||||
License: Zlib
|
||||
Architecture: x86-64-v3
|
||||
OE: zlib
|
||||
Homepage: http://zlib.net/
|
||||
Depends: musl (>= 1.2.6+git0+9fa28ece75)
|
||||
Provides: zlib
|
||||
Source: zlib_1.3.2.bb
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
if [ x"$D" = "x" ]; then
|
||||
if [ -x /sbin/ldconfig ]; then /sbin/ldconfig ; fi
|
||||
fi
|
||||
@@ -4,8 +4,9 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
@@ -18,6 +19,8 @@ import (
|
||||
|
||||
var _ generic.Parser = parseDotnetPackagesLock
|
||||
|
||||
var packagesLockTypePrecedence = []string{"Project", "Direct", "CentralTransitive", "Transitive"}
|
||||
|
||||
type dotnetPackagesLock struct {
|
||||
Version int `json:"version"`
|
||||
Dependencies map[string]map[string]dotnetPackagesLockDep `json:"dependencies"`
|
||||
@@ -31,11 +34,38 @@ type dotnetPackagesLockDep struct {
|
||||
Dependencies map[string]string `json:"dependencies,omitempty"`
|
||||
}
|
||||
|
||||
func parseDotnetPackagesLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) { //nolint:funlen
|
||||
var pkgs []pkg.Package
|
||||
var pkgMap = make(map[string]pkg.Package)
|
||||
var relationships []artifact.Relationship
|
||||
type packagesLockEntry struct {
|
||||
name string
|
||||
dep dotnetPackagesLockDep
|
||||
}
|
||||
|
||||
func (e packagesLockEntry) nameVersion() string {
|
||||
return createNameAndVersion(e.name, e.dep.Resolved)
|
||||
}
|
||||
|
||||
// key identifies the package an entry describes. NuGet package IDs are case-insensitive, so two spellings of the
|
||||
// same ID and version are one package.
|
||||
func (e packagesLockEntry) key() string {
|
||||
return createNameAndVersion(strings.ToLower(e.name), e.dep.Resolved)
|
||||
}
|
||||
|
||||
type packagesLockFramework struct {
|
||||
name string
|
||||
entries []packagesLockEntry
|
||||
// byName is keyed by the lowercased package name, since NuGet package IDs are case-insensitive.
|
||||
byName map[string]packagesLockEntry
|
||||
}
|
||||
|
||||
func (f packagesLockFramework) find(name string) (packagesLockEntry, bool) {
|
||||
e, ok := f.byName[strings.ToLower(name)]
|
||||
return e, ok
|
||||
}
|
||||
|
||||
type packagesLockEdge struct {
|
||||
child, parent artifact.ID
|
||||
}
|
||||
|
||||
func parseDotnetPackagesLock(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
dec := json.NewDecoder(reader)
|
||||
|
||||
// unmarshal file
|
||||
@@ -44,70 +74,21 @@ func parseDotnetPackagesLock(_ context.Context, _ file.Resolver, _ *generic.Envi
|
||||
return nil, nil, fmt.Errorf("failed to parse packages.lock.json file: %w", err)
|
||||
}
|
||||
|
||||
// collect all deps here
|
||||
allDependencies := make(map[string]dotnetPackagesLockDep)
|
||||
|
||||
var names []string
|
||||
for _, dependencies := range lockFile.Dependencies {
|
||||
for name, dep := range dependencies {
|
||||
depNameVersion := createNameAndVersion(name, dep.Resolved)
|
||||
|
||||
if slices.Contains(names, depNameVersion) {
|
||||
continue
|
||||
}
|
||||
|
||||
names = append(names, depNameVersion)
|
||||
allDependencies[depNameVersion] = dep
|
||||
}
|
||||
}
|
||||
|
||||
// sort the names so that the order of the packages is deterministic
|
||||
sort.Strings(names)
|
||||
frameworks := newPackagesLockFrameworks(lockFile)
|
||||
|
||||
// create artifact for each pkg
|
||||
for _, nameVersion := range names {
|
||||
name, _ := extractNameAndVersion(nameVersion)
|
||||
var pkgs []pkg.Package
|
||||
pkgMap := make(map[string]pkg.Package)
|
||||
|
||||
dep := allDependencies[nameVersion]
|
||||
dotnetPkg := newDotnetPackagesLockPackage(name, dep, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
||||
for _, entry := range mergePackagesLockEntries(frameworks) {
|
||||
dotnetPkg := newDotnetPackagesLockPackage(entry.name, entry.dep, reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation))
|
||||
if dotnetPkg != nil {
|
||||
pkgs = append(pkgs, *dotnetPkg)
|
||||
pkgMap[nameVersion] = *dotnetPkg
|
||||
pkgMap[entry.key()] = *dotnetPkg
|
||||
}
|
||||
}
|
||||
|
||||
// fill up relationships
|
||||
for depNameVersion, dep := range allDependencies {
|
||||
parentPkg, ok := pkgMap[depNameVersion]
|
||||
if !ok {
|
||||
log.Debugf("package \"%s\" not found in map of all packages", depNameVersion)
|
||||
continue
|
||||
}
|
||||
|
||||
for childDepName, childDepVersion := range dep.Dependencies {
|
||||
childDepNameVersion := createNameAndVersion(childDepName, childDepVersion)
|
||||
|
||||
// try and find pkg for dependency with exact name and version
|
||||
childPkg, ok := pkgMap[childDepNameVersion]
|
||||
if !ok {
|
||||
// no exact match found, lets match on name only, lockfile will contain other version of pkg
|
||||
cpkg, ok := findPkgByName(childDepName, pkgMap)
|
||||
if !ok {
|
||||
log.Debugf("dependency \"%s\" of package \"%s\" not found in map of all packages", childDepNameVersion, depNameVersion)
|
||||
continue
|
||||
}
|
||||
|
||||
childPkg = *cpkg
|
||||
}
|
||||
|
||||
rel := artifact.Relationship{
|
||||
From: childPkg,
|
||||
To: parentPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
}
|
||||
relationships = append(relationships, rel)
|
||||
}
|
||||
}
|
||||
relationships := packagesLockRelationships(frameworks, pkgMap)
|
||||
|
||||
// sort the relationships for deterministic output
|
||||
relationship.Sort(relationships)
|
||||
@@ -115,6 +96,147 @@ func parseDotnetPackagesLock(_ context.Context, _ file.Resolver, _ *generic.Envi
|
||||
return pkgs, relationships, nil
|
||||
}
|
||||
|
||||
func newPackagesLockFrameworks(lockFile dotnetPackagesLock) []packagesLockFramework {
|
||||
var frameworks []packagesLockFramework
|
||||
|
||||
for _, frameworkName := range slices.Sorted(maps.Keys(lockFile.Dependencies)) {
|
||||
deps := lockFile.Dependencies[frameworkName]
|
||||
framework := packagesLockFramework{
|
||||
name: frameworkName,
|
||||
byName: make(map[string]packagesLockEntry, len(deps)),
|
||||
}
|
||||
|
||||
for _, name := range slices.Sorted(maps.Keys(deps)) {
|
||||
entry := packagesLockEntry{name: name, dep: deps[name]}
|
||||
framework.entries = append(framework.entries, entry)
|
||||
framework.byName[strings.ToLower(name)] = entry
|
||||
}
|
||||
|
||||
frameworks = append(frameworks, framework)
|
||||
}
|
||||
|
||||
return frameworks
|
||||
}
|
||||
|
||||
func mergePackagesLockEntries(frameworks []packagesLockFramework) []packagesLockEntry {
|
||||
merged := make(map[string]packagesLockEntry)
|
||||
|
||||
for _, framework := range frameworks {
|
||||
for _, entry := range framework.entries {
|
||||
key := entry.key()
|
||||
if existing, ok := merged[key]; ok && !isMoreDirect(entry.dep.Type, existing.dep.Type) {
|
||||
continue
|
||||
}
|
||||
merged[key] = entry
|
||||
}
|
||||
}
|
||||
|
||||
var entries []packagesLockEntry
|
||||
for _, key := range slices.Sorted(maps.Keys(merged)) {
|
||||
entries = append(entries, merged[key])
|
||||
}
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
func isMoreDirect(a, b string) bool {
|
||||
rankA, rankB := packagesLockTypeRank(a), packagesLockTypeRank(b)
|
||||
if rankA != rankB {
|
||||
return rankA < rankB
|
||||
}
|
||||
return a < b
|
||||
}
|
||||
|
||||
func packagesLockTypeRank(t string) int {
|
||||
if i := slices.Index(packagesLockTypePrecedence, t); i >= 0 {
|
||||
return i
|
||||
}
|
||||
return len(packagesLockTypePrecedence)
|
||||
}
|
||||
|
||||
func packagesLockRelationships(frameworks []packagesLockFramework, pkgMap map[string]pkg.Package) []artifact.Relationship {
|
||||
var relationships []artifact.Relationship
|
||||
|
||||
frameworksByName := make(map[string]packagesLockFramework, len(frameworks))
|
||||
for _, framework := range frameworks {
|
||||
frameworksByName[framework.name] = framework
|
||||
}
|
||||
|
||||
seen := make(map[packagesLockEdge]struct{})
|
||||
for _, framework := range frameworks {
|
||||
base, hasBase := frameworksByName[baseFrameworkName(framework.name)]
|
||||
if !hasBase || base.name == framework.name {
|
||||
base = packagesLockFramework{}
|
||||
}
|
||||
|
||||
for _, entry := range framework.entries {
|
||||
parentPkg, ok := pkgMap[entry.key()]
|
||||
if !ok {
|
||||
log.Debugf("package %q not found in map of all packages", entry.nameVersion())
|
||||
continue
|
||||
}
|
||||
|
||||
relationships = append(relationships, packagesLockEntryRelationships(entry, parentPkg, framework, base, pkgMap, seen)...)
|
||||
}
|
||||
}
|
||||
|
||||
return relationships
|
||||
}
|
||||
|
||||
func packagesLockEntryRelationships(entry packagesLockEntry, parentPkg pkg.Package, framework, base packagesLockFramework, pkgMap map[string]pkg.Package, seen map[packagesLockEdge]struct{}) []artifact.Relationship {
|
||||
var relationships []artifact.Relationship
|
||||
|
||||
for _, childName := range slices.Sorted(maps.Keys(entry.dep.Dependencies)) {
|
||||
child, ok := findPackagesLockDependency(childName, framework, base)
|
||||
if !ok {
|
||||
log.Debugf("dependency %q of package %q not found under target framework %q", childName, entry.nameVersion(), framework.name)
|
||||
continue
|
||||
}
|
||||
|
||||
childPkg, ok := pkgMap[child.key()]
|
||||
if !ok {
|
||||
log.Debugf("package %q not found in map of all packages", child.nameVersion())
|
||||
continue
|
||||
}
|
||||
|
||||
// a package listing itself (possibly under another casing) is not a dependency
|
||||
if childPkg.ID() == parentPkg.ID() {
|
||||
continue
|
||||
}
|
||||
|
||||
key := packagesLockEdge{child: childPkg.ID(), parent: parentPkg.ID()}
|
||||
if _, exists := seen[key]; exists {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
|
||||
relationships = append(relationships, artifact.Relationship{
|
||||
From: childPkg,
|
||||
To: parentPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
}
|
||||
|
||||
return relationships
|
||||
}
|
||||
|
||||
// findPackagesLockDependency finds the entry a dependency edge points at. The version on an edge is only the lower
|
||||
// bound of a version range, so the edge resolves to whatever version its target framework resolved. Runtime-specific
|
||||
// sections such as "net8.0/win-x64" may reference packages listed only in their base framework ("net8.0"). An edge
|
||||
// that resolves nowhere is dropped rather than guessed from another target framework.
|
||||
func findPackagesLockDependency(name string, framework, base packagesLockFramework) (packagesLockEntry, bool) {
|
||||
if entry, ok := framework.find(name); ok {
|
||||
return entry, true
|
||||
}
|
||||
return base.find(name)
|
||||
}
|
||||
|
||||
// baseFrameworkName returns the target framework of a runtime-specific section, such as "net8.0" for "net8.0/win-x64".
|
||||
func baseFrameworkName(frameworkName string) string {
|
||||
name, _, _ := strings.Cut(frameworkName, "/")
|
||||
return name
|
||||
}
|
||||
|
||||
func newDotnetPackagesLockPackage(name string, dep dotnetPackagesLockDep, locations ...file.Location) *pkg.Package {
|
||||
metadata := pkg.DotnetPackagesLockEntry{
|
||||
Name: name,
|
||||
@@ -150,14 +272,3 @@ func packagesLockPackageURL(name, version string) string {
|
||||
"",
|
||||
).ToString()
|
||||
}
|
||||
|
||||
func findPkgByName(pkgName string, pkgMap map[string]pkg.Package) (*pkg.Package, bool) {
|
||||
for pkgNameVersion, pkg := range pkgMap {
|
||||
name, _ := extractNameAndVersion(pkgNameVersion)
|
||||
if name == pkgName {
|
||||
return &pkg, true
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ package dotnet
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"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/pkg"
|
||||
@@ -197,3 +200,304 @@ func TestParseDotnetPackagesLock(t *testing.T) {
|
||||
|
||||
pkgtest.TestFileParser(t, fixture, parseDotnetPackagesLock, expectedPkgs, expectedRelationships)
|
||||
}
|
||||
|
||||
func TestParseDotnetPackagesLock_multipleTargetFrameworks(t *testing.T) {
|
||||
fixture := "testdata/packages.lock-multi-framework.json"
|
||||
fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture))
|
||||
|
||||
myLibPkg := pkg.Package{
|
||||
Name: "MyLib",
|
||||
Version: "1.0.0",
|
||||
PURL: "pkg:nuget/MyLib@1.0.0",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
Metadata: pkg.DotnetPackagesLockEntry{
|
||||
Name: "MyLib",
|
||||
Version: "1.0.0",
|
||||
ContentHash: "mylibhash==",
|
||||
Type: "Direct",
|
||||
},
|
||||
}
|
||||
|
||||
log4net2Pkg := pkg.Package{
|
||||
Name: "log4net",
|
||||
Version: "2.0.5",
|
||||
PURL: "pkg:nuget/log4net@2.0.5",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
Metadata: pkg.DotnetPackagesLockEntry{
|
||||
Name: "log4net",
|
||||
Version: "2.0.5",
|
||||
ContentHash: "log4net205hash==",
|
||||
Type: "Transitive",
|
||||
},
|
||||
}
|
||||
|
||||
log4net1Pkg := pkg.Package{
|
||||
Name: "log4net",
|
||||
Version: "1.2.15",
|
||||
PURL: "pkg:nuget/log4net@1.2.15",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
Metadata: pkg.DotnetPackagesLockEntry{
|
||||
Name: "log4net",
|
||||
Version: "1.2.15",
|
||||
ContentHash: "log4net1215hash==",
|
||||
Type: "Transitive",
|
||||
},
|
||||
}
|
||||
|
||||
// resolved to the same version under both frameworks (spelled "newtonsoft.json" under one), but declared "Direct" by
|
||||
// only one of them
|
||||
newtonsoftPkg := pkg.Package{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
Metadata: pkg.DotnetPackagesLockEntry{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
ContentHash: "newtonsoft1303hash==",
|
||||
Type: "Direct",
|
||||
},
|
||||
}
|
||||
|
||||
// only listed under the runtime-specific "net8.0/win-x64" section, and depends on a package from "net8.0"
|
||||
myLibNativePkg := pkg.Package{
|
||||
Name: "runtime.win-x64.MyLib.Native",
|
||||
Version: "1.0.0",
|
||||
PURL: "pkg:nuget/runtime.win-x64.MyLib.Native@1.0.0",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
Metadata: pkg.DotnetPackagesLockEntry{
|
||||
Name: "runtime.win-x64.MyLib.Native",
|
||||
Version: "1.0.0",
|
||||
ContentHash: "mylibnativehash==",
|
||||
Type: "Transitive",
|
||||
},
|
||||
}
|
||||
|
||||
// only listed under "net8.0", so the "netstandard2.0" edge to it must be dropped rather than guessed
|
||||
serilogPkg := pkg.Package{
|
||||
Name: "Serilog",
|
||||
Version: "3.1.1",
|
||||
PURL: "pkg:nuget/Serilog@3.1.1",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
Metadata: pkg.DotnetPackagesLockEntry{
|
||||
Name: "Serilog",
|
||||
Version: "3.1.1",
|
||||
ContentHash: "serilog311hash==",
|
||||
Type: "Transitive",
|
||||
},
|
||||
}
|
||||
|
||||
expectedPkgs := []pkg.Package{
|
||||
myLibPkg,
|
||||
newtonsoftPkg,
|
||||
log4net1Pkg,
|
||||
log4net2Pkg,
|
||||
myLibNativePkg,
|
||||
serilogPkg,
|
||||
}
|
||||
|
||||
// the same package is resolved to a different version per target framework, so both edges must be captured, while
|
||||
// the Newtonsoft.Json edge declared by both frameworks must appear only once
|
||||
expectedRelationships := []artifact.Relationship{
|
||||
{
|
||||
From: log4net1Pkg,
|
||||
To: myLibPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{
|
||||
From: log4net2Pkg,
|
||||
To: myLibPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{
|
||||
From: log4net2Pkg,
|
||||
To: myLibNativePkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
{
|
||||
From: newtonsoftPkg,
|
||||
To: myLibPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}
|
||||
|
||||
pkgtest.TestFileParser(t, fixture, parseDotnetPackagesLock, expectedPkgs, expectedRelationships)
|
||||
}
|
||||
|
||||
func TestParseDotnetPackagesLock_projectReference(t *testing.T) {
|
||||
fixture := "testdata/packages.lock-project-reference.json"
|
||||
fixtureLocationSet := file.NewLocationSet(file.NewLocation(fixture))
|
||||
|
||||
newtonsoftPkg := pkg.Package{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
PURL: "pkg:nuget/Newtonsoft.Json@13.0.3",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
Metadata: pkg.DotnetPackagesLockEntry{
|
||||
Name: "Newtonsoft.Json",
|
||||
Version: "13.0.3",
|
||||
ContentHash: "newtonsoft1303hash==",
|
||||
Type: "Transitive",
|
||||
},
|
||||
}
|
||||
|
||||
// a project reference has no resolved version or content hash
|
||||
myLibPkg := pkg.Package{
|
||||
Name: "mylib",
|
||||
PURL: "pkg:nuget/mylib",
|
||||
Locations: fixtureLocationSet,
|
||||
Language: pkg.Dotnet,
|
||||
Type: pkg.DotnetPkg,
|
||||
Metadata: pkg.DotnetPackagesLockEntry{
|
||||
Name: "mylib",
|
||||
Type: "Project",
|
||||
},
|
||||
}
|
||||
|
||||
expectedPkgs := []pkg.Package{
|
||||
newtonsoftPkg,
|
||||
myLibPkg,
|
||||
}
|
||||
|
||||
// project references declare their dependencies as version ranges, which resolve to the version the target
|
||||
// framework resolved
|
||||
expectedRelationships := []artifact.Relationship{
|
||||
{
|
||||
From: newtonsoftPkg,
|
||||
To: myLibPkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}
|
||||
|
||||
pkgtest.TestFileParser(t, fixture, parseDotnetPackagesLock, expectedPkgs, expectedRelationships)
|
||||
}
|
||||
|
||||
func Test_findPackagesLockDependency(t *testing.T) {
|
||||
frameworks := newPackagesLockFrameworks(dotnetPackagesLock{
|
||||
Dependencies: map[string]map[string]dotnetPackagesLockDep{
|
||||
"net8.0": {
|
||||
"log4net": {Resolved: "2.0.5"},
|
||||
},
|
||||
"net8.0/win-x64": {
|
||||
"runtime.win-x64.Native": {Resolved: "1.0.0"},
|
||||
},
|
||||
"netstandard2.0": {
|
||||
"log4net": {Resolved: "1.2.15"},
|
||||
"Newtonsoft.Json": {Resolved: "13.0.3"},
|
||||
},
|
||||
},
|
||||
})
|
||||
net8, rid, netstandard := frameworks[0], frameworks[1], frameworks[2]
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
depName string
|
||||
framework packagesLockFramework
|
||||
base packagesLockFramework
|
||||
wantVersion string
|
||||
wantFound bool
|
||||
}{
|
||||
{
|
||||
name: "resolves to the version pinned by its own target framework",
|
||||
depName: "log4net",
|
||||
framework: netstandard,
|
||||
wantVersion: "1.2.15",
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "package names are matched case-insensitively",
|
||||
depName: "LOG4NET",
|
||||
framework: net8,
|
||||
wantVersion: "2.0.5",
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "runtime-specific sections fall back to their base target framework",
|
||||
depName: "log4net",
|
||||
framework: rid,
|
||||
base: net8,
|
||||
wantVersion: "2.0.5",
|
||||
wantFound: true,
|
||||
},
|
||||
{
|
||||
name: "a package absent from the target framework is not guessed from another one",
|
||||
depName: "Newtonsoft.Json",
|
||||
framework: net8,
|
||||
wantFound: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, found := findPackagesLockDependency(tt.depName, tt.framework, tt.base)
|
||||
require.Equal(t, tt.wantFound, found)
|
||||
if !tt.wantFound {
|
||||
return
|
||||
}
|
||||
assert.Equal(t, tt.wantVersion, got.dep.Resolved)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_mergePackagesLockEntries_typePrecedence(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
types map[string]string // target framework -> type
|
||||
wantType string
|
||||
}{
|
||||
{
|
||||
name: "direct wins over transitive",
|
||||
types: map[string]string{"net8.0": "Transitive", "netstandard2.0": "Direct"},
|
||||
wantType: "Direct",
|
||||
},
|
||||
{
|
||||
name: "central transitive wins over transitive regardless of framework order",
|
||||
types: map[string]string{"net8.0": "Transitive", "netstandard2.0": "CentralTransitive"},
|
||||
wantType: "CentralTransitive",
|
||||
},
|
||||
{
|
||||
name: "central transitive wins over transitive in the reverse framework order",
|
||||
types: map[string]string{"net8.0": "CentralTransitive", "netstandard2.0": "Transitive"},
|
||||
wantType: "CentralTransitive",
|
||||
},
|
||||
{
|
||||
name: "direct wins over central transitive",
|
||||
types: map[string]string{"net472": "CentralTransitive", "net8.0": "Direct", "netstandard2.0": "Transitive"},
|
||||
wantType: "Direct",
|
||||
},
|
||||
{
|
||||
name: "known types win over unknown ones",
|
||||
types: map[string]string{"net8.0": "Unknown", "netstandard2.0": "Transitive"},
|
||||
wantType: "Transitive",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
lockFile := dotnetPackagesLock{Dependencies: map[string]map[string]dotnetPackagesLockDep{}}
|
||||
for framework, depType := range tt.types {
|
||||
lockFile.Dependencies[framework] = map[string]dotnetPackagesLockDep{
|
||||
"Newtonsoft.Json": {Type: depType, Resolved: "13.0.3"},
|
||||
}
|
||||
}
|
||||
|
||||
entries := mergePackagesLockEntries(newPackagesLockFrameworks(lockFile))
|
||||
require.Len(t, entries, 1)
|
||||
assert.Equal(t, tt.wantType, entries[0].dep.Type)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0": {
|
||||
"MyLib": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0, )",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "mylibhash==",
|
||||
"dependencies": {
|
||||
"log4net": "2.0.5",
|
||||
"Newtonsoft.Json": "13.0.3"
|
||||
}
|
||||
},
|
||||
"log4net": {
|
||||
"type": "Transitive",
|
||||
"resolved": "2.0.5",
|
||||
"contentHash": "log4net205hash=="
|
||||
},
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Direct",
|
||||
"requested": "[13.0.3, )",
|
||||
"resolved": "13.0.3",
|
||||
"contentHash": "newtonsoft1303hash=="
|
||||
},
|
||||
"Serilog": {
|
||||
"type": "Transitive",
|
||||
"resolved": "3.1.1",
|
||||
"contentHash": "serilog311hash=="
|
||||
}
|
||||
},
|
||||
"net8.0/win-x64": {
|
||||
"runtime.win-x64.MyLib.Native": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "mylibnativehash==",
|
||||
"dependencies": {
|
||||
"log4net": "2.0.0",
|
||||
"RUNTIME.WIN-X64.MYLIB.NATIVE": "1.0.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"netstandard2.0": {
|
||||
"MyLib": {
|
||||
"type": "Direct",
|
||||
"requested": "[1.0.0, )",
|
||||
"resolved": "1.0.0",
|
||||
"contentHash": "mylibhash==",
|
||||
"dependencies": {
|
||||
"log4net": "1.2.15",
|
||||
"newtonsoft.json": "13.0.3",
|
||||
"Serilog": "3.1.1"
|
||||
}
|
||||
},
|
||||
"log4net": {
|
||||
"type": "Transitive",
|
||||
"resolved": "1.2.15",
|
||||
"contentHash": "log4net1215hash=="
|
||||
},
|
||||
"newtonsoft.json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.3",
|
||||
"contentHash": "newtonsoft1303hash=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": 1,
|
||||
"dependencies": {
|
||||
"net8.0": {
|
||||
"Newtonsoft.Json": {
|
||||
"type": "Transitive",
|
||||
"resolved": "13.0.3",
|
||||
"contentHash": "newtonsoft1303hash=="
|
||||
},
|
||||
"mylib": {
|
||||
"type": "Project",
|
||||
"dependencies": {
|
||||
"Newtonsoft.Json": "[13.0.3, )"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user