mirror of
https://github.com/anchore/syft.git
synced 2026-10-15 07:37:20 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae649b09cf | ||
|
|
f76ae8e57c | ||
|
|
509f1a1a5b | ||
|
|
8d5567c423 | ||
|
|
d61c2cada3 | ||
|
|
0bbcff1ae3 | ||
|
|
f21e439a63 | ||
|
|
c511d509ef | ||
|
|
fdeb78fc19 | ||
|
|
6ac7afb439 | ||
|
|
b23e461731 | ||
|
|
8c63a2403c | ||
|
|
e4ff7f5973 | ||
|
|
37d45de9ff | ||
|
|
26927eab02 | ||
|
|
1dc16fbac4 | ||
|
|
d0e1e7be05 | ||
|
|
57bd6a5f9c | ||
|
|
c60aabad37 | ||
|
|
c0072873c9 | ||
|
|
236ab25393 | ||
|
|
fa6ea7b10c | ||
|
|
ae7843e17c | ||
|
|
a4d3aedf4e | ||
|
|
50dbb33e66 | ||
|
|
10af8342c0 | ||
|
|
f7f3c6efc3 | ||
|
|
a8d4fb845f | ||
|
|
ff45b9ea63 | ||
|
|
fb55856e33 | ||
|
|
a76b5fa36b | ||
|
|
643bf2c24d | ||
|
|
c5baac0e94 | ||
|
|
19341fd0a2 | ||
|
|
ec8f035584 | ||
|
|
9d39701d50 | ||
|
|
82718d36ca | ||
|
|
cc1bb1f28c | ||
|
|
14e96fcbf8 | ||
|
|
af508550e6 | ||
|
|
409ac78f62 | ||
|
|
41870fec7f | ||
|
|
97fd939355 | ||
|
|
3f4105a657 | ||
|
|
b49f0172f6 | ||
|
|
46fd83817f | ||
|
|
a2ed08dfdd | ||
|
|
34e7b0df98 | ||
|
|
3b41acb0ff | ||
|
|
3325493831 |
+1
-1
@@ -16,7 +16,7 @@ tools:
|
||||
# used to upload test fixture cache
|
||||
- name: oras
|
||||
version:
|
||||
want: v1.3.3
|
||||
want: v1.3.4
|
||||
method: github-release
|
||||
with:
|
||||
repo: oras-project/oras
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
# check out text files with lf on every platform (regardless of core.autocrlf), letting git detect binary files
|
||||
* text=auto eol=lf
|
||||
|
||||
# fixtures that must keep their crlf line endings byte for byte
|
||||
syft/linux/testdata/unprintable -text
|
||||
syft/pkg/cataloger/bitnami/testdata/no-rel/opt/bitnami/redis/.spdx-redis.spdx -text
|
||||
syft/pkg/cataloger/java/testdata/jar-metadata/**/META-INF/MANIFEST.MF -text
|
||||
@@ -30,7 +30,7 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
- name: Setup go + go-make tooling
|
||||
uses: anchore/go-make/.github/actions/setup@430e2175bb166bfd138ef75466adbcc8ddab875f # v0.8.0
|
||||
uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
cache-key-prefix: ${{ inputs.cache-key-prefix }}
|
||||
|
||||
@@ -86,7 +86,7 @@ jobs:
|
||||
# dockers_v2 builds multi-platform manifests in a single buildx invocation, which requires a
|
||||
# docker-container driver builder (the default builder on the runner cannot build multi-platform).
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@37fe631027851001ddb9b187196cc803df7f5f0e # v4.3.0
|
||||
uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f #v4.6.0
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run zizmor"
|
||||
uses: zizmorcore/zizmor-action@70fb788f84895a7701f5643d103d587e460b5c99 # v0.6.3
|
||||
uses: zizmorcore/zizmor-action@cc914d7f3750a2d13d75c7f184a1060aa0e9d482 # v0.6.4
|
||||
with:
|
||||
# there is a pass/fail gate as a repo ruleset (if there is no ruleset configured then the action will pass by default)
|
||||
advanced-security: true
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
# enough; a 4 vCPU instance can't pull much more than that anyway.
|
||||
# note: s3-cache intentionally omitted -- PR runs are untrusted and must not write to the
|
||||
# shared cache backend that the trusted release workflow reads from (cache poisoning).
|
||||
runs-on: &test-runner "runs-on=${{ github.run_id }}/cpu=4+8/ram=16+32/family=m5+m6+m7+m8+c5+c6+c7+c8/spot=price-capacity-optimized/volume=100gb"
|
||||
runs-on: &test-runner "runs-on=${{ github.run_id }}/cpu=4+8/ram=16+32/family=m6+m7+m8+c6+c7+c8/spot=price-capacity-optimized/volume=100gb"
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
@@ -49,6 +49,13 @@ jobs:
|
||||
Unit-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Unit tests"
|
||||
runs-on: ubuntu-latest
|
||||
needs: [Unit-Test-Linux, Unit-Test-Windows]
|
||||
steps:
|
||||
- run: echo All unit tests passed!
|
||||
|
||||
Unit-Test-Linux:
|
||||
name: "Unit tests (Linux)"
|
||||
runs-on: *test-runner
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -68,6 +75,27 @@ jobs:
|
||||
- name: Check for capability drift
|
||||
run: make check-capability-drift
|
||||
|
||||
Unit-Test-Windows:
|
||||
name: "Unit tests (Windows)"
|
||||
runs-on: windows-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 #v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# go-make's setup only (not ./.github/actions/bootstrap): no apt, binny tools, or fixture cache are needed
|
||||
- name: Setup go + go-make tooling
|
||||
uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.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
|
||||
- name: Run unit tests
|
||||
run: make unit:windows
|
||||
|
||||
Integration-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Integration tests"
|
||||
|
||||
+18
-1
@@ -7,6 +7,7 @@ linters:
|
||||
- asciicheck
|
||||
- bodyclose
|
||||
- copyloopvar
|
||||
- depguard
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
@@ -29,9 +30,20 @@ linters:
|
||||
- unused
|
||||
- whitespace
|
||||
settings:
|
||||
depguard:
|
||||
rules:
|
||||
yaml:
|
||||
deny:
|
||||
# gopkg.in/yaml.v3 is unmaintained and panics on some malformed input (see #5327)
|
||||
- pkg: gopkg.in/yaml.v3
|
||||
desc: use go.yaml.in/yaml/v3 instead
|
||||
funlen:
|
||||
lines: 70
|
||||
statements: 50
|
||||
goconst:
|
||||
# the default of 3 turns every repeated literal in a lookup table or a test fixture into a finding.
|
||||
# 5 keeps the ones worth hoisting without flagging data that reads better written out.
|
||||
min-occurrences: 5
|
||||
gocritic:
|
||||
enabled-checks:
|
||||
- deferInLoop
|
||||
@@ -54,6 +66,12 @@ linters:
|
||||
- linters:
|
||||
- revive
|
||||
text: "var-naming: avoid package names that conflict"
|
||||
# cpegenerate is almost entirely lookup tables mapping artifact names to vendors, products, and
|
||||
# maven group ids. The repeated strings are the data itself, so hoisting them to constants would
|
||||
# replace a readable table with indirection for no gain.
|
||||
- linters:
|
||||
- goconst
|
||||
path: syft/pkg/cataloger/internal/cpegenerate/
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
@@ -61,7 +79,6 @@ linters:
|
||||
|
||||
# do not enable...
|
||||
# - deadcode # The owner seems to have abandoned the linter. Replaced by "unused".
|
||||
# - depguard # We don't have a configuration for this yet
|
||||
# - goprintffuncname # does not catch all cases and there are exceptions
|
||||
# - nakedret # does not catch all cases and should not fail a build
|
||||
# - gochecknoglobals
|
||||
|
||||
+3
-3
@@ -2,11 +2,11 @@ module github.com/anchore/syft/.make
|
||||
|
||||
go 1.25.8
|
||||
|
||||
require github.com/anchore/go-make v0.8.0
|
||||
require github.com/anchore/go-make v0.8.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.37.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/mod v0.40.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
)
|
||||
|
||||
+6
-6
@@ -1,10 +1,10 @@
|
||||
github.com/anchore/go-make v0.8.0 h1:3XAD4WNHIbqY4kwFyCQJ7kKiYLzSzjB0UwzDVwRqlUY=
|
||||
github.com/anchore/go-make v0.8.0/go.mod h1:4M6TnArb5w693VyWsgr5dCWrk2BLNu/ed4JUcsrzS34=
|
||||
github.com/anchore/go-make v0.8.1 h1:GAhPXR+9qVQhFB1pfVlHFOzxAt3dTDTUIA7Tp7nx9tg=
|
||||
github.com/anchore/go-make v0.8.1/go.mod h1:xALbVsPsDoJnJxdkjPlxOLZV7GbAdT5Pv3gd0kP1Lz0=
|
||||
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.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
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=
|
||||
|
||||
+19
-1
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -52,7 +53,7 @@ func main() {
|
||||
raceFlag = " -race"
|
||||
}
|
||||
Run(
|
||||
"go test -count=1 -timeout=30m -v"+raceFlag+" ./cmd/syft/internal/test/integration/...",
|
||||
"go test -count=1 -timeout=30m"+raceFlag+" ./cmd/syft/internal/test/integration/...",
|
||||
run.Env("GODEBUG", "dontfreezetheworld=1"),
|
||||
)
|
||||
},
|
||||
@@ -96,6 +97,23 @@ 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.
|
||||
Task{
|
||||
Name: "unit:windows",
|
||||
Description: "run the windows-relevant subset of unit tests",
|
||||
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),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
// default validation pipeline (replaces Taskfile `default`/`pr-validations`/`validations`).
|
||||
Task{
|
||||
Name: "default",
|
||||
|
||||
@@ -158,6 +158,11 @@ func (l *backgroundLineReader) read(reader io.Reader, stage *progress.Stage) {
|
||||
|
||||
l.lock.Unlock()
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
log.WithFields("error", err).Debug("unable to read attestation output")
|
||||
// keep draining so the writer never blocks on a full pipe
|
||||
_, _ = io.Copy(io.Discard, reader)
|
||||
}
|
||||
}
|
||||
|
||||
func (l backgroundLineReader) Lines() []string {
|
||||
|
||||
@@ -349,8 +349,8 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
|
||||
})),
|
||||
tablewriter.WithConfig(tablewriter.Config{
|
||||
Row: tw.CellConfig{
|
||||
Formatting: tw.CellFormatting{MergeMode: tw.MergeHierarchical},
|
||||
Alignment: tw.CellAlignment{Global: tw.AlignLeft},
|
||||
Merging: tw.CellMerging{Mode: tw.MergeHierarchical},
|
||||
Alignment: tw.CellAlignment{Global: tw.AlignLeft},
|
||||
},
|
||||
}),
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -16,19 +16,21 @@ import (
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// multiplatformImage is busybox:1.38.0 pinned by index digest, since docker hub periodically rebuilds and
|
||||
// re-pushes official image tags, which changes the per-platform digests asserted below.
|
||||
const multiplatformImage = "docker.io/library/busybox@sha256:fd7dc98638c8e305f4dc34e979f1c0fdfdcaeb0fbf8fcff77ae834b6da3d7e6e"
|
||||
|
||||
// TestMultiPlatformOCIImageSelection verifies that syft can load a single platform out of a
|
||||
// multi-platform OCI image on disk (both --from oci-dir and --from oci-archive) by selecting the
|
||||
// image that matches the requested platform.
|
||||
func TestMultiPlatformOCIImageSelection(t *testing.T) {
|
||||
remoteImage := "docker.io/library/busybox:1.38.0"
|
||||
|
||||
// Per-platform image config digests within the multi-platform index, obtained from the OCI layout
|
||||
// (see anchore/stereoscope integration tests: TestPlatformSelectionWithOciLocalSources).
|
||||
expectedDigest := map[string]string{
|
||||
"arm64": "sha256:e0e8b3cbfed68a90084781e2962f9c0deead51c5a3f11a488eef0283a4284bc2",
|
||||
"s390x": "sha256:0cf160e720a8e4f20883b72276a6eaded83b79539f3f1d39e35a3336154b9960",
|
||||
"amd64": "sha256:c6348fa86ba0fb2108c9334f5fe913ddc6d853313e655891f133a0127c30099f",
|
||||
"ppc64le": "sha256:b144fc0e06537d07956cde340b878a81a717e394edb736d3110858a12a6635cb",
|
||||
"arm64": "sha256:d2482869a6b838d3b4c9cad0c8085c06277909482771eb525d298fc3a7d07927",
|
||||
"s390x": "sha256:e55a0fa55032cceac2d983f4b4a25b28e42303b46a2f1915fc0fa45051e6ca70",
|
||||
"amd64": "sha256:aaef90e065235eb0b2d9938be85d1b81add2902cf89affe2cdfa19be33476ac0",
|
||||
"ppc64le": "sha256:8158829b015d1c5285d63d54d37e95998ef0af4177e3f416678decbcaff3170c",
|
||||
}
|
||||
|
||||
// syft --from source tag -> stereoscope OCI source used to prepare the local fixture
|
||||
@@ -39,7 +41,7 @@ func TestMultiPlatformOCIImageSelection(t *testing.T) {
|
||||
|
||||
for from, imageSource := range sources {
|
||||
t.Run(from, func(t *testing.T) {
|
||||
localPath := imagetest.PrepareMultiplatformFixtureImage(t, imageSource, remoteImage)
|
||||
localPath := imagetest.PrepareMultiplatformFixtureImage(t, imageSource, multiplatformImage)
|
||||
for _, arch := range []string{"amd64", "arm64", "s390x", "ppc64le"} {
|
||||
t.Run(fmt.Sprintf("linux/%s", arch), func(t *testing.T) {
|
||||
platform, err := image.NewPlatform("linux/" + arch)
|
||||
@@ -73,8 +75,6 @@ func TestMultiPlatformOCIImageSelection(t *testing.T) {
|
||||
// TestMultiPlatformOCIImageSelection_UnavailablePlatform verifies that requesting a platform not present
|
||||
// in the multi-platform OCI image results in an error rather than silently selecting the wrong image.
|
||||
func TestMultiPlatformOCIImageSelection_UnavailablePlatform(t *testing.T) {
|
||||
remoteImage := "docker.io/library/busybox:1.38.0"
|
||||
|
||||
// windows/amd64 is not present in this linux-only multi-platform image
|
||||
platform, err := image.NewPlatform("windows/amd64")
|
||||
require.NoError(t, err)
|
||||
@@ -86,7 +86,7 @@ func TestMultiPlatformOCIImageSelection_UnavailablePlatform(t *testing.T) {
|
||||
|
||||
for from, imageSource := range sources {
|
||||
t.Run(from, func(t *testing.T) {
|
||||
localPath := imagetest.PrepareMultiplatformFixtureImage(t, imageSource, remoteImage)
|
||||
localPath := imagetest.PrepareMultiplatformFixtureImage(t, imageSource, multiplatformImage)
|
||||
|
||||
_, err := syft.GetSource(
|
||||
context.Background(),
|
||||
@@ -101,8 +101,6 @@ func TestMultiPlatformOCIImageSelection_UnavailablePlatform(t *testing.T) {
|
||||
// TestMultiPlatformOCIImageSelection_DefaultPlatform verifies that not specifying a platform results
|
||||
// in the current platform being selected.
|
||||
func TestMultiPlatformOCIImageSelection_DefaultPlatform(t *testing.T) {
|
||||
remoteImage := "docker.io/library/busybox:1.38.0"
|
||||
|
||||
sources := map[string]image.Source{
|
||||
"oci-dir": image.OciDirectorySource,
|
||||
"oci-archive": image.OciTarballSource,
|
||||
@@ -110,7 +108,7 @@ func TestMultiPlatformOCIImageSelection_DefaultPlatform(t *testing.T) {
|
||||
|
||||
for from, imageSource := range sources {
|
||||
t.Run(from, func(t *testing.T) {
|
||||
localPath := imagetest.PrepareMultiplatformFixtureImage(t, imageSource, remoteImage)
|
||||
localPath := imagetest.PrepareMultiplatformFixtureImage(t, imageSource, multiplatformImage)
|
||||
|
||||
src, err := syft.GetSource(
|
||||
context.Background(),
|
||||
|
||||
+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"
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
@@ -8,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/wagoodman/go-partybus"
|
||||
|
||||
"github.com/anchore/bubbly"
|
||||
@@ -52,7 +54,7 @@ func New(out io.Writer, quiet bool, handlers ...bubbly.EventHandler) *UI {
|
||||
func (m *UI) Setup(subscription partybus.Unsubscribable) error {
|
||||
// we still want to collect log messages, however, we also the logger shouldn't write to the screen directly
|
||||
if logWrapper, ok := log.Get().(logger.Controller); ok {
|
||||
logWrapper.SetOutput(m.frame.(*frame.Frame).Footer())
|
||||
logWrapper.SetOutput(&logFooter{w: m.frame.(*frame.Frame).Footer()})
|
||||
}
|
||||
|
||||
m.subscription = subscription
|
||||
@@ -187,6 +189,48 @@ func (m UI) View() string {
|
||||
return m.frame.View()
|
||||
}
|
||||
|
||||
var (
|
||||
logTitleStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8")) // dark grey
|
||||
// the first log line gets a branch pointing at it from the title, the rest are indented to match
|
||||
logBranch = logTitleStyle.Render("└─▶") + " "
|
||||
logIndent = " "
|
||||
)
|
||||
|
||||
// logFooter decorates log entries drawn below the TUI with a one-time title (written when the first entry arrives)
|
||||
// and a branch from the title to the first log line. Writes are serialized by the logger.
|
||||
type logFooter struct {
|
||||
w io.Writer
|
||||
started bool
|
||||
}
|
||||
|
||||
func (l *logFooter) Write(p []byte) (int, error) {
|
||||
var buf bytes.Buffer
|
||||
first := !l.started
|
||||
if first {
|
||||
buf.WriteString(logTitleStyle.Render("Logs (non-fatal, see -v for more):") + "\n")
|
||||
l.started = true
|
||||
}
|
||||
for i, line := range bytes.SplitAfter(p, []byte("\n")) {
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
if first && i == 0 {
|
||||
buf.WriteString(logBranch)
|
||||
} else {
|
||||
buf.WriteString(logIndent)
|
||||
}
|
||||
buf.Write(line)
|
||||
}
|
||||
if _, err := l.w.Write(buf.Bytes()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// IsTerminal marks the footer as terminal-bound (it is drawn by the TUI) so the logger keeps colors. NO_COLOR is
|
||||
// still honored by the formatter.
|
||||
func (*logFooter) IsTerminal() bool { return true }
|
||||
|
||||
func runWithTimeout(timeout time.Duration, fn func() error) (err error) {
|
||||
c := make(chan struct{}, 1)
|
||||
go func() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/anchore/syft
|
||||
|
||||
go 1.26.3
|
||||
go 1.26.8
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.6.0
|
||||
@@ -11,18 +11,18 @@ require (
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/acobaugh/osrelease v0.1.0
|
||||
github.com/adrg/xdg v0.5.3
|
||||
github.com/anchore/bubbly v0.2.1
|
||||
github.com/anchore/bubbly v0.2.2
|
||||
github.com/anchore/clio v0.1.1
|
||||
github.com/anchore/fangs v0.1.1
|
||||
github.com/anchore/go-collections v0.1.1
|
||||
github.com/anchore/go-homedir v0.1.1
|
||||
github.com/anchore/go-logger v0.1.1
|
||||
github.com/anchore/go-logger v0.2.0
|
||||
github.com/anchore/go-macholibre v0.1.1
|
||||
github.com/anchore/go-rpmdb v0.2.0
|
||||
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.3
|
||||
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
|
||||
@@ -36,7 +36,7 @@ require (
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
github.com/diskfs/go-diskfs v1.9.4
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/dustin/go-humanize v1.1.0
|
||||
github.com/elliotchance/phpserialize v1.4.0
|
||||
github.com/facebookincubator/nvdtools v0.1.5
|
||||
github.com/github/go-spdx/v2 v2.7.0
|
||||
@@ -56,18 +56,18 @@ require (
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2
|
||||
github.com/hashicorp/go-getter v1.8.9
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/hcl/v2 v2.24.0
|
||||
github.com/hashicorp/hcl/v2 v2.25.0
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/invopop/jsonschema v0.14.0
|
||||
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/magiconair/properties v1.18.11
|
||||
github.com/magiconair/properties v1.18.12
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/moby/sys/mountinfo v0.7.2
|
||||
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1
|
||||
github.com/olekukonko/tablewriter v1.1.4
|
||||
github.com/olekukonko/tablewriter v1.1.5
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.23
|
||||
@@ -94,11 +94,10 @@ require (
|
||||
go.yaml.in/yaml/v3 v3.0.5
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
|
||||
golang.org/x/mod v0.41.0
|
||||
golang.org/x/net v0.58.0
|
||||
golang.org/x/net v0.59.0
|
||||
golang.org/x/time v0.16.0
|
||||
golang.org/x/tools v0.49.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.58.0
|
||||
golang.org/x/tools v0.50.0
|
||||
modernc.org/sqlite v1.59.0
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -116,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
|
||||
@@ -123,14 +123,16 @@ 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
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
|
||||
github.com/apparentlymart/go-textseg/v17 v17.0.1 // indirect
|
||||
github.com/aquasecurity/go-version v0.0.1 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.44.0 // 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.5 // 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.1+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-logr/logr v1.4.3 // 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,22 +248,23 @@ 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
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.4.1 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
github.com/olekukonko/errors v1.2.0 // indirect
|
||||
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/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,64 +275,77 @@ 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.0 // 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.1 // indirect
|
||||
github.com/sigstore/sigstore v1.10.8 // 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/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/zclconf/go-cty v1.16.3 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/zclconf/go-cty v1.19.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.44.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // 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
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.56.0 // indirect
|
||||
golang.org/x/crypto v0.57.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/sys v0.47.0 // indirect
|
||||
golang.org/x/term v0.45.0 // indirect
|
||||
golang.org/x/text v0.41.0 // indirect
|
||||
golang.org/x/sync v0.23.0 // indirect
|
||||
golang.org/x/sys v0.48.0 // indirect
|
||||
golang.org/x/term v0.46.0 // indirect
|
||||
golang.org/x/text v0.42.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
gonum.org/v1/gonum v0.17.0 // indirect
|
||||
google.golang.org/api v0.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.6 // indirect
|
||||
modernc.org/libc v1.75.7 // 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=
|
||||
@@ -120,8 +124,8 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anchore/bubbly v0.2.1 h1:b3ZNBefY8ks5DI4SQ7BXSbBIsN6eoaTD6s1su7NuCBo=
|
||||
github.com/anchore/bubbly v0.2.1/go.mod h1:o6a9aH3/fCn+mb5g1T1tBbQZ4hGR0yO5/uD1d2Vtago=
|
||||
github.com/anchore/bubbly v0.2.2 h1:53JHRlpaNZjMcUYdz7n1U7+kOhy0zsJv9gpGEXvTcz8=
|
||||
github.com/anchore/bubbly v0.2.2/go.mod h1:M4twoFkczlfuNdU8QV7oQjVJwPbxdUWRMfKydcLba0U=
|
||||
github.com/anchore/clio v0.1.1 h1:9bkYnaFbpT0J3G0qSHyQqa/lpK5zV0C9Gtgh82H/jj0=
|
||||
github.com/anchore/clio v0.1.1/go.mod h1:CFPIopJlakJbBpKtAL/EPpj4W9hgBO7PfFc8N2FAtlE=
|
||||
github.com/anchore/fangs v0.1.1 h1:NBTHocmWM2/0gNhebpoE/wEB0Z0nxh2lESww2o/Uf5M=
|
||||
@@ -130,8 +134,8 @@ github.com/anchore/go-collections v0.1.1 h1:kGlmSehs+ctTBZr+98F2+taHeolHy+XDx1Nc
|
||||
github.com/anchore/go-collections v0.1.1/go.mod h1:zgDbPSNDpfU47tVo7aW+2suG7+vXB1Sq1qtDhRJknQ0=
|
||||
github.com/anchore/go-homedir v0.1.1 h1:KpaO5BPrVT7cVMcEZ9KfFfEi0iuo3DtagPGl0C744X0=
|
||||
github.com/anchore/go-homedir v0.1.1/go.mod h1:9B0DGhbmMAJVGGEbrlb+PkORM5eDFCEOtZ3xQ22qaLA=
|
||||
github.com/anchore/go-logger v0.1.1 h1:HPBNmQVBdYliwaFVUL7oJ3UvSlaoXAMj/ZagAIYaCpA=
|
||||
github.com/anchore/go-logger v0.1.1/go.mod h1:ekWuh5BkZVwyXnyEd4fwL29N4pNLssgRsqh2+kFN/b8=
|
||||
github.com/anchore/go-logger v0.2.0 h1:V8Gazz+F8xwWEWz+whqt8/5163IkO4XrAie+1qbK10k=
|
||||
github.com/anchore/go-logger v0.2.0/go.mod h1:omqLWBwT/B5/aTwIH6pl5WBylBzeH3BGHUTS6OFeFvY=
|
||||
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=
|
||||
@@ -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.3 h1:sur8G2THAOnOrSXDjIc1SXzQwco8hjFBtGFq1jFvNVA=
|
||||
github.com/anchore/stereoscope v0.3.3/go.mod h1:LxL4iOFtB8XMbA1MykUUTkmtQmDuYdbr4+TkBkCfljU=
|
||||
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=
|
||||
@@ -156,6 +160,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuW
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/apparentlymart/go-textseg/v17 v17.0.1 h1:bpMXRgQ5cEoRNuQke1a80/Nl6w3G5eoIbWo9f3gXkAs=
|
||||
github.com/apparentlymart/go-textseg/v17 v17.0.1/go.mod h1:fa8X4jgGeevslICIY6LcdjkSecWnXmYd9Lk34z/VxZs=
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1 h1:8VKKQtH2aV61+0hovZS3T//rUF+6GDn18paFTVS0h0M=
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1/go.mod h1:3naPe+Bp6wi3n4l5iBFCZgS0JG8vY6FT0H4NGhFJ+i4=
|
||||
github.com/aquasecurity/go-version v0.0.1 h1:4cNl516agK0TCn5F7mmYN+xVs1E3S45LkgZk3cbaW2E=
|
||||
@@ -292,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.5 h1:9MYlI81gUcOZ0WsCkSMtvOU7rTR3hqAoa2eCzhoLlkA=
|
||||
github.com/containerd/containerd/v2 v2.3.5/go.mod h1:RXDyLPaI3zoO7dFdAW9/54W4cix+z3A6larieufC9mg=
|
||||
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=
|
||||
@@ -304,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=
|
||||
@@ -337,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.1+incompatible h1:qYL1bCp6cRw2SB1xmLlIOPyV171dilw9W2Jew38vy9c=
|
||||
github.com/docker/cli v29.8.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker-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=
|
||||
@@ -348,8 +366,8 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/dustin/go-humanize v1.1.0 h1:dbKTrvD0klcbBV/h4AWJdMuZogJACoMlvWIWZ5b2xWg=
|
||||
github.com/dustin/go-humanize v1.1.0/go.mod h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY=
|
||||
@@ -379,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=
|
||||
@@ -394,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=
|
||||
@@ -425,15 +443,15 @@ 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=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc=
|
||||
@@ -451,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=
|
||||
@@ -505,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=
|
||||
@@ -541,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=
|
||||
@@ -556,6 +577,8 @@ github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj
|
||||
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
||||
github.com/gookit/color v1.6.1 h1:KoTnDxJPRgrL0SoX0f8rCFg2zI0t4E3GZZBMo2nN8LU=
|
||||
github.com/gookit/color v1.6.1/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
|
||||
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.3 h1:F63PlUPIW56HGU5k4Crv0JAG9ojtz/TuwzgMYlb82Ec=
|
||||
github.com/gpustack/gguf-parser-go v0.26.3/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
@@ -595,8 +618,8 @@ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl/v2 v2.24.0 h1:2QJdZ454DSsYGoaE6QheQZjtKZSUs9Nh2izTWiwQxvE=
|
||||
github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM=
|
||||
github.com/hashicorp/hcl/v2 v2.25.0 h1:HmmQVYRny4MaBo4b20TjmL46wyuUxpnMWkPZ4+NTbWk=
|
||||
github.com/hashicorp/hcl/v2 v2.25.0/go.mod h1:vR+FKETxoZAmRlHgFfKmuqivj+C4Izm/c66XkmZ3r7M=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
@@ -668,8 +691,8 @@ github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQ
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.18.11 h1:j5ozYZl0zCjG7ahMDH0GWIobOvvUzT0BdAguG0ViKy0=
|
||||
github.com/magiconair/properties v1.18.11/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/magiconair/properties v1.18.12 h1:sT9zQpvTB3B4gzrX0tmZNTEaGyg8Zw55MFYRE32Mr9I=
|
||||
github.com/magiconair/properties v1.18.12/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/maruel/natural v1.3.0 h1:VsmCsBmEyrR46RomtgHs5hbKADGRVtliHTyCOLFBpsg=
|
||||
github.com/maruel/natural v1.3.0/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
|
||||
@@ -690,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=
|
||||
@@ -700,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=
|
||||
@@ -725,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=
|
||||
@@ -756,16 +785,16 @@ github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1 h1:kpt9ZfKcm+
|
||||
github.com/nix-community/go-nix v0.0.0-20250101154619-4bdde671e0a1/go.mod h1:qgCw4bBKZX8qMgGeEZzGFVT3notl42dBjNqO2jut0M0=
|
||||
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249 h1:NHrXEjTNQY7P0Zfx1aMrNhpgxHmow66XQtm0aQLY0AE=
|
||||
github.com/nsf/jsondiff v0.0.0-20210926074059-1e845ec5d249/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
|
||||
github.com/nwaples/rardecode/v2 v2.2.0 h1:4ufPGHiNe1rYJxYfehALLjup4Ls3ck42CWwjKiOqu0A=
|
||||
github.com/nwaples/rardecode/v2 v2.2.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/nwaples/rardecode/v2 v2.4.1 h1:F7zNW2LdAuuBThHWXQaiFUGVD/sef299NfWSB1nHAl4=
|
||||
github.com/nwaples/rardecode/v2 v2.4.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo=
|
||||
github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.1.6 h1:lGVTHO+Qc4Qm+fce/2h2m5y9LvqaW+DCN7xW9hsU3uA=
|
||||
github.com/olekukonko/ll v0.1.6/go.mod h1:NVUmjBb/aCtUpjKk75BhWrOlARz3dqsM+OtszpY4o88=
|
||||
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
|
||||
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
|
||||
github.com/olekukonko/tablewriter v1.1.5 h1:4LoZSfMySpMQY3PT8RWJsJeuEuMIoo9xGRgvmqjg6IQ=
|
||||
github.com/olekukonko/tablewriter v1.1.5/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
@@ -774,6 +803,8 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.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=
|
||||
@@ -812,6 +843,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=
|
||||
@@ -824,8 +857,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=
|
||||
@@ -851,6 +884,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=
|
||||
@@ -858,21 +893,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.0 h1:iuCR9kcMFD4QurdKrGvPLoKZLv9YvwPYVr0473BdtFs=
|
||||
github.com/secure-systems-lab/go-securesystemslib v0.11.0/go.mod h1:+PMOTjUGwHj2vcZ+TFKlb1tXRbrdWE1LYDT5i9JC80Q=
|
||||
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.1 h1:/5OPaNuolRJmQfeZLayJGFXMpsRJEdgC6ah1/+7Px7U=
|
||||
github.com/sigstore/protobuf-specs v0.5.1/go.mod h1:DRBzpFuE+LnvQMN10/dU6nBeKwVLGEQ6o2FovN2Rats=
|
||||
github.com/sigstore/sigstore v1.10.8 h1:1Mgkxvkw4AXMfIP1DOjc6kw0GkUgA8pGVpveN/EfOq4=
|
||||
github.com/sigstore/sigstore v1.10.8/go.mod h1:f9+B/4iaYimvUkySyb2mvc73n3RLqNn24grHZM/ET8M=
|
||||
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=
|
||||
@@ -902,6 +947,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=
|
||||
@@ -929,6 +976,8 @@ 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=
|
||||
@@ -949,6 +998,12 @@ 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/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=
|
||||
@@ -970,14 +1025,16 @@ 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=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zclconf/go-cty v1.16.3 h1:osr++gw2T61A8KVYHoQiFbFd1Lh3JOCXc/jFLJXKTxk=
|
||||
github.com/zclconf/go-cty v1.16.3/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
|
||||
github.com/zclconf/go-cty v1.19.0 h1:IV8WdqYZc2c5rLX9bEoLNXKojBAp0MZPBHMIrCoa/s4=
|
||||
github.com/zclconf/go-cty v1.19.0/go.mod h1:12W89jGn3JCOIQi7infWr9m80rOkb5RNYJqXMZcN4c8=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 h1:V+UsotZpAVvfj3X/LMoEytoLzSiP6Lg0F7wdVyu9gGg=
|
||||
@@ -992,31 +1049,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.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
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.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/metric/x v0.66.0 h1:YkCrx1zLOChi9ZcZ6euupOcsgzbVlec7D/xoEU1+cTA=
|
||||
go.opentelemetry.io/otel/metric/x v0.66.0/go.mod h1:d1+BDj9t96do0/1LoU1ayfCv79ZgNE41qbhBvnMOBZk=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
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=
|
||||
@@ -1041,8 +1100,12 @@ 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.56.0 h1:GUh5Ii4J5jtcseSMiRqr1jXCNHoxjeV9Fmekc2oLy6Y=
|
||||
golang.org/x/crypto v0.56.0/go.mod h1:OMW5y6CY9l38uPLmxU6l6pwcXp1obtLo3e6gT7gQR2I=
|
||||
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=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1082,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=
|
||||
@@ -1127,9 +1194,14 @@ 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.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
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=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1161,8 +1233,13 @@ 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.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
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=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1237,13 +1314,24 @@ 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.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
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.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
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=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1254,8 +1342,13 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
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=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1316,8 +1409,11 @@ 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.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
|
||||
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1432,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=
|
||||
@@ -1526,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.6 h1:yKk8qo+Di4gkmvRboK8ocCqH22FiUCR6jRy2OwtCRus=
|
||||
modernc.org/libc v1.75.6/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
|
||||
modernc.org/libc v1.75.7 h1:o3DTP9/0p9pKmY2WCKQaySW6wIiZhNM7wc2lUoyhfew=
|
||||
modernc.org/libc v1.75.7/go.mod h1:bO5o2ztHxBb2rjz0PgdHN0sSMw57CgxGFLZ3Qd/QpVQ=
|
||||
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=
|
||||
@@ -1536,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.58.0 h1:38u40/bwkfM7f0Myhosl+SEMltSDxnGdQf8o6Kjmys0=
|
||||
modernc.org/sqlite v1.58.0/go.mod h1:rsD2CckafgObKC4DhBlGBf+RiHxkc3hINGt1Xw32tVY=
|
||||
modernc.org/sqlite v1.59.0 h1:X1es1GpqBlS/5T+vbM4HLUdaa8OtQx468DF2vrx+38A=
|
||||
modernc.org/sqlite v1.59.0/go.mod h1:+paeT2A3iPRHkQDwG7oA6Tk0zQd5woMEI8q7orfry8k=
|
||||
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=
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"sort"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/syft/internal/task"
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// this file handles YAML file reading and writing with comment preservation, using gopkg.in/yaml.v3's node tree to maintain all existing comments during regeneration.
|
||||
// this file handles YAML file reading and writing with comment preservation, using go.yaml.in/yaml/v3's node tree to maintain all existing comments during regeneration.
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/syft/internal/capabilities"
|
||||
"github.com/anchore/syft/internal/capabilities/internal"
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
func TestFindFieldValue(t *testing.T) {
|
||||
|
||||
@@ -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()),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/syft/internal/capabilities"
|
||||
)
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
)
|
||||
|
||||
func TestConfigSchemaExtensions(t *testing.T) {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
)
|
||||
|
||||
// maxScannedLineSize is the longest line NewLineScanner will return before Scan stops with bufio.ErrTooLong.
|
||||
// The buffer only grows to this size when a line needs it.
|
||||
const maxScannedLineSize = 1024 * 1024
|
||||
|
||||
// NewLineScanner returns a bufio.Scanner that accepts lines up to 1MB rather than the 64KB default, since
|
||||
// package metadata files carry long single-line fields (descriptions, license text, dependency lists). Callers
|
||||
// must still check Err() after scanning.
|
||||
func NewLineScanner(r io.Reader) *bufio.Scanner {
|
||||
s := bufio.NewScanner(r)
|
||||
s.Buffer(nil, maxScannedLineSize)
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewLineScanner(t *testing.T) {
|
||||
long := strings.Repeat("a", 100*1024)
|
||||
s := NewLineScanner(strings.NewReader(long + "\nb\n"))
|
||||
require.True(t, s.Scan())
|
||||
require.Equal(t, long, s.Text())
|
||||
require.True(t, s.Scan())
|
||||
require.Equal(t, "b", s.Text())
|
||||
require.False(t, s.Scan())
|
||||
require.NoError(t, s.Err())
|
||||
|
||||
s = NewLineScanner(strings.NewReader(strings.Repeat("a", maxScannedLineSize+1)))
|
||||
require.False(t, s.Scan())
|
||||
require.ErrorIs(t, s.Err(), bufio.ErrTooLong)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
@@ -191,12 +192,9 @@ func hasInstalledESMPackage(packages *pkg.Collection) bool {
|
||||
if packages == nil {
|
||||
return false
|
||||
}
|
||||
for p := range packages.Enumerate(pkg.DebPkg) {
|
||||
if esmVersionPattern.MatchString(p.Version) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.ContainsFunc(packages.Sorted(pkg.DebPkg), func(p pkg.Package) bool {
|
||||
return esmVersionPattern.MatchString(p.Version)
|
||||
})
|
||||
}
|
||||
|
||||
func fileMatchesAny(resolver file.Resolver, location file.Location, patterns ...*regexp.Regexp) (bool, error) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
@@ -232,6 +233,33 @@ func Test_findUbuntuFeatures(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// returning early from Collection.Enumerate leaks its goroutine and read lock, so any later write blocks forever.
|
||||
func Test_findUbuntuFeatures_releasesPackageCollectionLock(t *testing.T) {
|
||||
resolver := fixtureResolverForDir(t, "testdata/ubuntu_plain")
|
||||
|
||||
s := sbom.SBOM{}
|
||||
s.Artifacts.LinuxDistribution = &linux.Release{ID: "ubuntu"}
|
||||
s.Artifacts.Packages = pkg.NewCollection(
|
||||
pkg.Package{Name: "openssl", Version: "1.1.1f-1ubuntu2.19+esm1", Type: pkg.DebPkg},
|
||||
pkg.Package{Name: "libcap2", Version: "1:2.32-1ubuntu0.1~esm1", Type: pkg.DebPkg},
|
||||
)
|
||||
|
||||
require.NoError(t, os.DetectFeatures(context.Background(), resolver, sbomsync.NewBuilder(&s)))
|
||||
require.True(t, s.Artifacts.LinuxDistribution.ExtendedSupport)
|
||||
|
||||
added := make(chan struct{})
|
||||
go func() {
|
||||
s.Artifacts.Packages.Add(pkg.Package{Name: "zlib1g", Version: "1:1.2.11.dfsg-2ubuntu1", Type: pkg.DebPkg})
|
||||
close(added)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-added:
|
||||
case <-time.After(5 * time.Second):
|
||||
t.Fatal("package collection is still read-locked after feature detection")
|
||||
}
|
||||
}
|
||||
|
||||
// a read error in the apt-source signal must not suppress the ubuntu-advantage status signal; the three
|
||||
// signals are meant to be independent fallbacks.
|
||||
func Test_findUbuntuFeatures_signalErrorDoesNotSuppressOthers(t *testing.T) {
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package packagemetadata
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// untypedFieldAllowlist holds package metadata fields that are allowed to carry untyped (interface) values.
|
||||
// Values in these fields come straight from whatever a parser decoded, so they are not shaped by syft
|
||||
// and can hold anything an encoder or hasher chokes on. Every entry is part of existing SBOM output and
|
||||
// cannot be retyped without breaking it; new entries need a reason and a review.
|
||||
var untypedFieldAllowlist = map[string]string{
|
||||
"GGUFFileHeader.RemainingKeyValues": "raw GGUF header key-values; retyping changes the header output and metadataHash",
|
||||
}
|
||||
|
||||
// TestNoUntypedMetadataFields fails when a package metadata type gains a field typed any, []any,
|
||||
// map[string]any (or any other interface) that is not on the allowlist.
|
||||
func TestNoUntypedMetadataFields(t *testing.T) {
|
||||
var hits []string
|
||||
for _, ty := range AllTypes() {
|
||||
rt := reflect.TypeOf(ty)
|
||||
collectUntyped(rt, rt.Name(), map[reflect.Type]bool{}, &hits)
|
||||
}
|
||||
|
||||
seen := map[string]bool{}
|
||||
for _, h := range hits {
|
||||
seen[h] = true
|
||||
if _, ok := untypedFieldAllowlist[h]; !ok {
|
||||
t.Errorf("untyped field %s: use a concrete type, or add it to untypedFieldAllowlist with a reason", h)
|
||||
}
|
||||
}
|
||||
for k := range untypedFieldAllowlist {
|
||||
if !seen[k] {
|
||||
t.Errorf("stale untypedFieldAllowlist entry %s: field no longer exists or is now typed", k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collectUntyped records "Struct.Field" for every field whose type reaches an interface, descending
|
||||
// through pointers, slices, arrays, maps and nested syft-owned structs.
|
||||
func collectUntyped(rt reflect.Type, path string, visited map[reflect.Type]bool, hits *[]string) {
|
||||
switch rt.Kind() {
|
||||
case reflect.Interface:
|
||||
*hits = append(*hits, path)
|
||||
case reflect.Pointer, reflect.Slice, reflect.Array:
|
||||
collectUntyped(rt.Elem(), path, visited, hits)
|
||||
case reflect.Map:
|
||||
collectUntyped(rt.Key(), path, visited, hits)
|
||||
collectUntyped(rt.Elem(), path, visited, hits)
|
||||
case reflect.Struct:
|
||||
if visited[rt] {
|
||||
return
|
||||
}
|
||||
if rt.Name() != "" && !strings.HasPrefix(rt.PkgPath(), "github.com/anchore/syft/") {
|
||||
// a third-party type in the model carries whatever shape the library gives it (the same leak as an
|
||||
// interface field), so it counts as untyped. The standard library is left alone.
|
||||
if strings.Contains(strings.Split(rt.PkgPath(), "/")[0], ".") {
|
||||
*hits = append(*hits, path)
|
||||
}
|
||||
return
|
||||
}
|
||||
visited[rt] = true
|
||||
for i := 0; i < rt.NumField(); i++ {
|
||||
f := rt.Field(i)
|
||||
// unexported embedded structs still promote their exported fields to the encoders and the hasher. A
|
||||
// field is only out of reach when it is neither encoded nor hashed by SetID.
|
||||
if (!f.IsExported() && !f.Anonymous) || (f.Tag.Get("json") == "-" && f.Tag.Get("hash") == "ignore") {
|
||||
continue
|
||||
}
|
||||
collectUntyped(f.Type, rt.Name()+"."+f.Name, visited, hits)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package internal
|
||||
|
||||
// MaxSafeTensorsHeaderSize caps a safetensors header's JSON (excluding its
|
||||
// 8-byte length prefix). Shared by the ai cataloger and the OCI model source so
|
||||
// dir and OCI scans accept the same headers. Real-world shard headers are well
|
||||
// under 1 MB.
|
||||
const MaxSafeTensorsHeaderSize = 8 * 1024 * 1024
|
||||
@@ -44,6 +44,7 @@ type LicenseInfo struct {
|
||||
// LicenseByURL returns the license ID and name for a given URL from the SPDX license list.
|
||||
// The URL should match one of the URLs in the seeAlso field of an SPDX license.
|
||||
// The scheme (http:// or https://) is stripped before lookup, so both schemes match.
|
||||
// opensource.org URLs additionally match regardless of the form used (see normalizeOpenSourceOrgURL).
|
||||
func LicenseByURL(url string) (LicenseInfo, bool) {
|
||||
url = strings.TrimSpace(url)
|
||||
url = stripScheme(url)
|
||||
@@ -52,9 +53,51 @@ func LicenseByURL(url string) (LicenseInfo, bool) {
|
||||
ID: id,
|
||||
}, true
|
||||
}
|
||||
if normalized, ok := normalizeOpenSourceOrgURL(url); ok {
|
||||
if id, exists := openSourceOrgURLToLicense[normalized]; exists {
|
||||
return LicenseInfo{
|
||||
ID: id,
|
||||
}, true
|
||||
}
|
||||
}
|
||||
return LicenseInfo{}, false
|
||||
}
|
||||
|
||||
// openSourceOrgURLToLicense indexes the opensource.org entries of urlToLicense by normalized URL.
|
||||
var openSourceOrgURLToLicense = buildOpenSourceOrgURLToLicense(urlToLicense)
|
||||
|
||||
func buildOpenSourceOrgURLToLicense(urls map[string]string) map[string]string {
|
||||
index := make(map[string]string)
|
||||
ambiguous := make(map[string]bool)
|
||||
for url, id := range urls {
|
||||
normalized, ok := normalizeOpenSourceOrgURL(url)
|
||||
if !ok || ambiguous[normalized] {
|
||||
continue
|
||||
}
|
||||
if existing, exists := index[normalized]; exists && existing != id {
|
||||
// don't guess between licenses that only differ by URL form
|
||||
delete(index, normalized)
|
||||
ambiguous[normalized] = true
|
||||
continue
|
||||
}
|
||||
index[normalized] = id
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
// normalizeOpenSourceOrgURL reduces a scheme-less opensource.org URL to a canonical form.
|
||||
// SPDX has changed these URLs over time (e.g. 3.29.0 replaced opensource.org/licenses/MIT and
|
||||
// opensource.org/license/mit/ with opensource.org/license/MIT), while package metadata in the wild
|
||||
// still uses the older forms. The www. prefix, /licenses/ vs /license/, case, and trailing slash are ignored.
|
||||
func normalizeOpenSourceOrgURL(url string) (string, bool) {
|
||||
url = strings.TrimPrefix(strings.ToLower(url), "www.")
|
||||
if !strings.HasPrefix(url, "opensource.org/") {
|
||||
return "", false
|
||||
}
|
||||
url = strings.Replace(url, "opensource.org/licenses/", "opensource.org/license/", 1)
|
||||
return strings.TrimSuffix(url, "/"), true
|
||||
}
|
||||
|
||||
// stripScheme removes http:// or https:// prefix from a URL.
|
||||
// This allows a single map entry to match both schemes.
|
||||
func stripScheme(url string) string {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// This file was generated using data from https://spdx.org/licenses/licenses.json
|
||||
package spdxlicense
|
||||
|
||||
const Version = "3.28.0"
|
||||
const Version = "3.29.0"
|
||||
|
||||
var licenseIDs = map[string]string{
|
||||
"0bsd": "0BSD",
|
||||
@@ -116,6 +116,7 @@ var licenseIDs = map[string]string{
|
||||
"aswfdigitalassets1.0.0": "ASWF-Digital-Assets-1.0",
|
||||
"aswfdigitalassets1.1": "ASWF-Digital-Assets-1.1",
|
||||
"aswfdigitalassets1.1.0": "ASWF-Digital-Assets-1.1",
|
||||
"atcgame": "atc-game",
|
||||
"baekmuk": "Baekmuk",
|
||||
"bahyph": "Bahyph",
|
||||
"barr": "Barr",
|
||||
@@ -142,8 +143,11 @@ var licenseIDs = map[string]string{
|
||||
"briangladman2.0clause": "Brian-Gladman-2-Clause",
|
||||
"briangladman2clause": "Brian-Gladman-2-Clause",
|
||||
"briangladman3.0.0clause": "Brian-Gladman-3-Clause",
|
||||
"briangladman3.0.0clausenoconversion": "Brian-Gladman-3-Clause-no-conversion",
|
||||
"briangladman3.0clause": "Brian-Gladman-3-Clause",
|
||||
"briangladman3.0clausenoconversion": "Brian-Gladman-3-Clause-no-conversion",
|
||||
"briangladman3clause": "Brian-Gladman-3-Clause",
|
||||
"briangladman3clausenoconversion": "Brian-Gladman-3-Clause-no-conversion",
|
||||
"bsd1.0.0clause": "BSD-1-Clause",
|
||||
"bsd1.0clause": "BSD-1-Clause",
|
||||
"bsd1clause": "BSD-1-Clause",
|
||||
@@ -154,6 +158,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd2.0.0clausenetbsd": "BSD-2-Clause",
|
||||
"bsd2.0.0clausepatent": "BSD-2-Clause-Patent",
|
||||
"bsd2.0.0clausepkgconfdisclaimer": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"bsd2.0.0clauseposunchanged": "BSD-2-Clause-pos-unchanged",
|
||||
"bsd2.0.0clauseviews": "BSD-2-Clause-Views",
|
||||
"bsd2.0clause": "BSD-2-Clause",
|
||||
"bsd2.0clausedarwin": "BSD-2-Clause-Darwin",
|
||||
@@ -162,6 +167,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd2.0clausenetbsd": "BSD-2-Clause",
|
||||
"bsd2.0clausepatent": "BSD-2-Clause-Patent",
|
||||
"bsd2.0clausepkgconfdisclaimer": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"bsd2.0clauseposunchanged": "BSD-2-Clause-pos-unchanged",
|
||||
"bsd2.0clauseviews": "BSD-2-Clause-Views",
|
||||
"bsd2clause": "BSD-2-Clause",
|
||||
"bsd2clausedarwin": "BSD-2-Clause-Darwin",
|
||||
@@ -170,6 +176,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd2clausenetbsd": "BSD-2-Clause",
|
||||
"bsd2clausepatent": "BSD-2-Clause-Patent",
|
||||
"bsd2clausepkgconfdisclaimer": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"bsd2clauseposunchanged": "BSD-2-Clause-pos-unchanged",
|
||||
"bsd2clauseviews": "BSD-2-Clause-Views",
|
||||
"bsd3.0.0clause": "BSD-3-Clause",
|
||||
"bsd3.0.0clauseacpica": "BSD-3-Clause-acpica",
|
||||
@@ -184,6 +191,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd3.0.0clausenonuclearlicense2014": "BSD-3-Clause-No-Nuclear-License-2014",
|
||||
"bsd3.0.0clausenonuclearwarranty": "BSD-3-Clause-No-Nuclear-Warranty",
|
||||
"bsd3.0.0clauseopenmpi": "BSD-3-Clause-Open-MPI",
|
||||
"bsd3.0.0clauseopenwebui": "BSD-3-Clause-OpenWebUI",
|
||||
"bsd3.0.0clausesun": "BSD-3-Clause-Sun",
|
||||
"bsd3.0.0clausetso": "BSD-3-Clause-Tso",
|
||||
"bsd3.0clause": "BSD-3-Clause",
|
||||
@@ -199,6 +207,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd3.0clausenonuclearlicense2014": "BSD-3-Clause-No-Nuclear-License-2014",
|
||||
"bsd3.0clausenonuclearwarranty": "BSD-3-Clause-No-Nuclear-Warranty",
|
||||
"bsd3.0clauseopenmpi": "BSD-3-Clause-Open-MPI",
|
||||
"bsd3.0clauseopenwebui": "BSD-3-Clause-OpenWebUI",
|
||||
"bsd3.0clausesun": "BSD-3-Clause-Sun",
|
||||
"bsd3.0clausetso": "BSD-3-Clause-Tso",
|
||||
"bsd3clause": "BSD-3-Clause",
|
||||
@@ -214,6 +223,7 @@ var licenseIDs = map[string]string{
|
||||
"bsd3clausenonuclearlicense2014": "BSD-3-Clause-No-Nuclear-License-2014",
|
||||
"bsd3clausenonuclearwarranty": "BSD-3-Clause-No-Nuclear-Warranty",
|
||||
"bsd3clauseopenmpi": "BSD-3-Clause-Open-MPI",
|
||||
"bsd3clauseopenwebui": "BSD-3-Clause-OpenWebUI",
|
||||
"bsd3clausesun": "BSD-3-Clause-Sun",
|
||||
"bsd3clausetso": "BSD-3-Clause-Tso",
|
||||
"bsd4.0.0clause": "BSD-4-Clause",
|
||||
@@ -232,12 +242,15 @@ var licenseIDs = map[string]string{
|
||||
"bsd4reno": "BSD-4.3RENO",
|
||||
"bsd4tahoe": "BSD-4.3TAHOE",
|
||||
"bsdadvertisingacknowledgement": "BSD-Advertising-Acknowledgement",
|
||||
"bsdasktoendorse": "BSD-ask-to-endorse",
|
||||
"bsdattributionhpnddisclaimer": "BSD-Attribution-HPND-disclaimer",
|
||||
"bsdinfernonettverk": "BSD-Inferno-Nettverk",
|
||||
"bsdmarkmodifications": "BSD-Mark-Modifications",
|
||||
"bsdprotection": "BSD-Protection",
|
||||
"bsdsourcealtgpl": "BSD-Source-alt-GPL",
|
||||
"bsdsourcebeginningfile": "BSD-Source-beginning-file",
|
||||
"bsdsourcecode": "BSD-Source-Code",
|
||||
"bsdsourcecodenodisclaimer": "BSD-Source-Code-no-disclaimer",
|
||||
"bsdsystemics": "BSD-Systemics",
|
||||
"bsdsystemicsw3.0.0works": "BSD-Systemics-W3Works",
|
||||
"bsdsystemicsw3.0works": "BSD-Systemics-W3Works",
|
||||
@@ -246,6 +259,7 @@ var licenseIDs = map[string]string{
|
||||
"bsl1.0": "BSL-1.0",
|
||||
"bsl1.0.0": "BSL-1.0",
|
||||
"buddy": "Buddy",
|
||||
"bugroff": "Bugroff",
|
||||
"busl1": "BUSL-1.1",
|
||||
"busl1.1": "BUSL-1.1",
|
||||
"busl1.1.0": "BUSL-1.1",
|
||||
@@ -316,8 +330,11 @@ var licenseIDs = map[string]string{
|
||||
"ccbync3.0": "CC-BY-NC-3.0",
|
||||
"ccbync3.0.0": "CC-BY-NC-3.0",
|
||||
"ccbync3.0.0de": "CC-BY-NC-3.0-DE",
|
||||
"ccbync3.0.0igo": "CC-BY-NC-3.0-IGO",
|
||||
"ccbync3.0de": "CC-BY-NC-3.0-DE",
|
||||
"ccbync3.0igo": "CC-BY-NC-3.0-IGO",
|
||||
"ccbync3de": "CC-BY-NC-3.0-DE",
|
||||
"ccbync3igo": "CC-BY-NC-3.0-IGO",
|
||||
"ccbync4": "CC-BY-NC-4.0",
|
||||
"ccbync4.0": "CC-BY-NC-4.0",
|
||||
"ccbync4.0.0": "CC-BY-NC-4.0",
|
||||
@@ -592,6 +609,7 @@ var licenseIDs = map[string]string{
|
||||
"fair": "Fair",
|
||||
"fbm": "FBM",
|
||||
"fdkaac": "FDK-AAC",
|
||||
"fdkmpegh": "FDK-MPEG-H",
|
||||
"fergusontwofish": "Ferguson-Twofish",
|
||||
"frameworx1": "Frameworx-1.0",
|
||||
"frameworx1.0": "Frameworx-1.0",
|
||||
@@ -767,6 +785,9 @@ var licenseIDs = map[string]string{
|
||||
"hippocratic2": "Hippocratic-2.1",
|
||||
"hippocratic2.1": "Hippocratic-2.1",
|
||||
"hippocratic2.1.0": "Hippocratic-2.1",
|
||||
"hippocratic3.0.0core": "Hippocratic-3.0-core",
|
||||
"hippocratic3.0core": "Hippocratic-3.0-core",
|
||||
"hippocratic3core": "Hippocratic-3.0-core",
|
||||
"hp1986": "HP-1986",
|
||||
"hp1986.0": "HP-1986",
|
||||
"hp1986.0.0": "HP-1986",
|
||||
@@ -813,6 +834,7 @@ var licenseIDs = map[string]string{
|
||||
"imlib2": "Imlib2",
|
||||
"imlib2.0": "Imlib2",
|
||||
"imlib2.0.0": "Imlib2",
|
||||
"informatica": "Informatica",
|
||||
"infozip": "Info-ZIP",
|
||||
"innernet2": "Inner-Net-2.0",
|
||||
"innernet2.0": "Inner-Net-2.0",
|
||||
@@ -1011,6 +1033,9 @@ var licenseIDs = map[string]string{
|
||||
"mulanpsl2.0.0": "MulanPSL-2.0",
|
||||
"multics": "Multics",
|
||||
"mup": "Mup",
|
||||
"mvt1": "MVT-1.1",
|
||||
"mvt1.1": "MVT-1.1",
|
||||
"mvt1.1.0": "MVT-1.1",
|
||||
"naist2003": "NAIST-2003",
|
||||
"naist2003.0": "NAIST-2003",
|
||||
"naist2003.0.0": "NAIST-2003",
|
||||
@@ -1489,6 +1514,8 @@ var urlToLicense = map[string]string{
|
||||
"cdla.io/permissive-1-0": "CDLA-Permissive-1.0",
|
||||
"cdla.io/sharing-1-0": "CDLA-Sharing-1.0",
|
||||
"celestrak.org/publications/AIAA/2006-6753/faq.php": "SGP4",
|
||||
"cgit.freebsd.org/src/plain/sys/dev/aic7xxx/aic7xxx_osm.c": "BSD-Source-alt-GPL",
|
||||
"cgit.freebsd.org/src/tree/usr.bin/sockstat/sockstat.c": "BSD-2-Clause-pos-unchanged",
|
||||
"cgit.freedesktop.org/libbsd/tree/man/setproctitle.3bsd": "libutil-David-Nugent",
|
||||
"coil.apotheon.org/plaintext/01.0.txt": "COIL-1.0",
|
||||
"core.tcl-lang.org/tk/file?name=compat/unistd.h": "HPND-UC",
|
||||
@@ -1512,6 +1539,7 @@ var urlToLicense = map[string]string{
|
||||
"creativecommons.org/licenses/by-nc/2.0/legalcode": "CC-BY-NC-2.0",
|
||||
"creativecommons.org/licenses/by-nc/2.5/legalcode": "CC-BY-NC-2.5",
|
||||
"creativecommons.org/licenses/by-nc/3.0/de/legalcode": "CC-BY-NC-3.0-DE",
|
||||
"creativecommons.org/licenses/by-nc/3.0/igo/legalcode": "CC-BY-NC-3.0-IGO",
|
||||
"creativecommons.org/licenses/by-nc/3.0/legalcode": "CC-BY-NC-3.0",
|
||||
"creativecommons.org/licenses/by-nc/4.0/legalcode": "CC-BY-NC-4.0",
|
||||
"creativecommons.org/licenses/by-nd-nc/1.0/legalcode": "CC-BY-NC-ND-1.0",
|
||||
@@ -1564,6 +1592,7 @@ var urlToLicense = map[string]string{
|
||||
"directory.fsf.org/wiki/License:Fdk": "FDK-AAC",
|
||||
"doc.qt.io/archives/3.3/license.html": "QPL-1.0",
|
||||
"doc.qt.nokia.com/3.3/license.html": "QPL-1.0",
|
||||
"docs.mvt.re/en/latest/license/": "MVT-1.1",
|
||||
"docs.omniverse.nvidia.com/install-guide/latest/common/licenses.html": "AML-glslang",
|
||||
"docs.oracle.com/cd/E81115_01/html/E81116/licenses.html": "man2html",
|
||||
"docs.python.org/3/license.html": "Python-2.0.1",
|
||||
@@ -1669,6 +1698,7 @@ var urlToLicense = map[string]string{
|
||||
"fedoraproject.org/wiki/Licensing:Mplus?rd=Licensing/mplus": "mplus",
|
||||
"fedoraproject.org/wiki/Licensing:ThorPublicLicense": "TPL-1.0",
|
||||
"firstdonoharm.dev/version/2/1/license.html": "Hippocratic-2.1",
|
||||
"firstdonoharm.dev/version/3/0/core.txt": "Hippocratic-3.0-core",
|
||||
"forge.gouv.qc.ca/licence/fr/liliq-v1-1/": "LiLiQ-P-1.1",
|
||||
"forge.gouv.qc.ca/licence/liliq-p": "LiLiQ-R-1.1",
|
||||
"forge.gouv.qc.ca/licence/liliq-p/": "LiLiQ-P-1.1",
|
||||
@@ -1686,68 +1716,70 @@ var urlToLicense = map[string]string{
|
||||
"ftp://ftp.tin.org/pub/news/utils/newsx/newsx-1.6.tar.gz": "Zeeff",
|
||||
"gianluca.dellavedova.org/2011/01/03/clarified-artistic-license/": "ClArtistic",
|
||||
"git.denx.de/?p=u-boot.git;a=blob;f=arch/powerpc/cpu/ppc4xx/miiphy.c;h=297155fdafa064b955e53e9832de93bfb0cfb85b;hb=9fab4bf4cc077c21e43941866f3f2c196f28670d": "IBM-pibs",
|
||||
"git.enlightenment.org/legacy/imlib2.git/tree/COPYING": "Imlib2",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/getcpu.2#n4": "Linux-man-pages-1-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/migrate_pages.2#n8": "Linux-man-pages-copyleft-2-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/move_pages.2#n5": "Linux-man-pages-copyleft-2-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/set_mempolicy.2#n5": "Linux-man-pages-copyleft-var",
|
||||
"git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h": "Linux-OpenIB",
|
||||
"git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h=v4.19": "HPND-sell-variant",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT#L55-63": "BSD-4.3RENO",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT?ref_type=heads#L39-51": "Furuseth",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/libraries/libldap/os-local.c?ref_type=heads#L19-23": "PADL",
|
||||
"git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT": "FTL",
|
||||
"git.savannah.gnu.org/cgit/gcl.git/tree/gcl/lsp/gcl_loop.lsp?h=Version_2_6_13pre": "LOOP",
|
||||
"git.savannah.gnu.org/cgit/gnulib.git/tree/modules/COPYING?id=7b08932179d0d6b017f7df01a2ddf6e096b038e3": "FSFULLRSD",
|
||||
"git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id=a74c6b4ee49397cf330b333da1042bffa60ed14f#n1788": "BSD-4.3TAHOE",
|
||||
"git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id=a74c6b4ee49397cf330b333da1042bffa60ed14f#n74": "Latex2e-translated-notice",
|
||||
"git.savannah.gnu.org/cgit/kazlib.git/tree/except.c?id=0062df360c2d17d57f6af19b0e444c51feb99036": "Kazlib",
|
||||
"git.savannah.gnu.org/cgit/wget.git/tree/util/trunc.c?h=v1.21.3&id=40747a11e44ced5a8ac628a41f879ced3e2ebce9#n6": "FSFAP-no-warranty-disclaimer",
|
||||
"git.sr.ht/~nabijaczleweli/ossp-var": "OSSP",
|
||||
"github.com/389ds/389-ds-base/blob/main/ldap/include/sysexits-compat.h#L15": "BSD-4.3TAHOE",
|
||||
"github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.0.txt": "ASWF-Digital-Assets-1.0",
|
||||
"github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.1.txt": "ASWF-Digital-Assets-1.1",
|
||||
"github.com/AdaCore/gnatcoll-core/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/AdaCore/gnatcoll-db/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/AdaCore/xmlada/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/BuildCraft/BuildCraft/blob/623d323b1868712f29f4a8b0979a02e8d1835131/LICENSE": "MMPL-1.0.1",
|
||||
"github.com/CommunitySpecification/1.0/blob/master/1._Community_Specification_License-v1.md": "Community-Spec-1.0",
|
||||
"github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt": "TU-Berlin-2.0",
|
||||
"github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md": "LPD-document",
|
||||
"github.com/DISIC/politique-de-contribution-open-source/blob/master/LICENSE.pdf": "etalab-2.0",
|
||||
"github.com/Distrotech/hdparm/blob/4517550db29a91420fb2b020349523b1b4512df2/LICENSE.TXT": "hdparm",
|
||||
"github.com/Distrotech/transfig/blob/master/transfig/transfig.c": "Xfig",
|
||||
"github.com/DrItanium/maya/blob/master/LICENSE.CLIPS": "Clips",
|
||||
"github.com/Dual-Life/mime-base64/blob/master/Base64.xs#L12": "metamail",
|
||||
"github.com/EthicalSource/hippocratic-license/blob/58c0e646d64ff6fbee275bfe2b9492f914e3ab2a/LICENSE.txt": "Hippocratic-2.1",
|
||||
"github.com/FreeRADIUS/freeradius-client/blob/master/COPYRIGHT#L32": "HPND-Fenneberg-Livingston",
|
||||
"github.com/GNOME/gcr/blob/master/docs/COPYING": "GCR-docs",
|
||||
"github.com/GNOME/libxml2/blob/master/dict.c": "ISC-Veillard",
|
||||
"github.com/HDFGroup/hdf5/?tab=License-1-ov-file#readme": "HDF5",
|
||||
"github.com/KhronosGroup/SPIRV-Cross/blob/main/LICENSES/LicenseRef-KhronosFreeUse.txt": "MIT-Khronos-old",
|
||||
"github.com/KhronosGroup/glslang/blob/main/LICENSE.txt#L949": "AML-glslang",
|
||||
"github.com/MariaDB/server/blob/11.6/libmysqld/lib_sql.cc": "Boehm-GC-without-fee",
|
||||
"github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md": "DRL-1.0",
|
||||
"github.com/OGRECave/ogre/blob/master/OgreMain/include/OgreSingleton.h#L28C3-L35C46": "Game-Programming-Gems",
|
||||
"github.com/Perl/perl5/blob/blead/util.c#L6136": "Martin-Birgmeier",
|
||||
"github.com/PixarAnimationStudios/OpenSubdiv/blob/v3_5_0/opensubdiv/version.cpp#L2-L22": "Pixar",
|
||||
"github.com/PixarAnimationStudios/OpenSubdiv/raw/v3_5_0/LICENSE.txt": "Pixar",
|
||||
"github.com/RTimothyEdwards/magic/blob/master/LICENSE": "HPND-UC-export-US",
|
||||
"github.com/SELinuxProject/selinux/blob/master/libselinux/LICENSE": "libselinux-1.0",
|
||||
"github.com/SWI-Prolog/packages-clib/blob/master/sha1/brg_endian.h": "Brian-Gladman-3-Clause",
|
||||
"github.com/SWI-Prolog/packages-xpce/blob/161a40cd82004f731ba48024f9d30af388a7edf5/src/img/gifwrite.c#L21-L26": "FBM",
|
||||
"github.com/SWI-Prolog/swipl-devel/blob/master/src/os/dtoa.c": "dtoa",
|
||||
"github.com/SigmaHQ/Detection-Rule-License/blob/6ec7fbde6101d101b5b5d1fcb8f9b69fbc76c04a/LICENSE.Detection.Rules.md": "DRL-1.1",
|
||||
"github.com/Slicer/Slicer/blob/main/License.txt": "3D-Slicer-1.0",
|
||||
"github.com/Unidata/UDUNITS-2/blob/master/COPYRIGHT": "UCAR",
|
||||
"github.com/acpica/acpica/blob/master/source/common/acfileio.c#L119": "BSD-3-Clause-acpica",
|
||||
"github.com/adaptivecomputing/torque/blob/master/PBS_License.txt": "OpenPBS-2.3",
|
||||
"github.com/adversary-org/wtfnmf/raw/3f2cd8235a64350a57a51b9739715edaea63ec1a/COPYING.WTFNMFPL-utf8": "WTFNMFPL",
|
||||
"github.com/adversary-org/wtfnmf/raw/refs/tags/1.0/COPYING.WTFNMFPL": "WTFNMFPL",
|
||||
"github.com/ap/Text-Tabs/blob/master/lib.modern/Text/Tabs.pm#L148": "TTWL",
|
||||
"github.com/apache/apr/blob/trunk/LICENSE#L298C6-L298C29": "Caldera-no-preamble",
|
||||
"github.com/audacious-media-player/audacious/blob/master/COPYING": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"github.com/audacious-media-player/audacious/blob/master/src/audacious/main.cc": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"git.enlightenment.org/legacy/imlib2.git/tree/COPYING": "Imlib2",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/getcpu.2#n4": "Linux-man-pages-1-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/migrate_pages.2#n8": "Linux-man-pages-copyleft-2-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/move_pages.2#n5": "Linux-man-pages-copyleft-2-para",
|
||||
"git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man2/set_mempolicy.2#n5": "Linux-man-pages-copyleft-var",
|
||||
"git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/sa.h": "Linux-OpenIB",
|
||||
"git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/net/sunrpc/auth_gss/gss_generic_token.c?h=v4.19": "HPND-sell-variant",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT#L55-63": "BSD-4.3RENO",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/COPYRIGHT?ref_type=heads#L39-51": "Furuseth",
|
||||
"git.openldap.org/openldap/openldap/-/blob/master/libraries/libldap/os-local.c?ref_type=heads#L19-23": "PADL",
|
||||
"git.savannah.gnu.org/cgit/freetype/freetype2.git/tree/docs/FTL.TXT": "FTL",
|
||||
"git.savannah.gnu.org/cgit/gcl.git/tree/gcl/lsp/gcl_loop.lsp?h=Version_2_6_13pre": "LOOP",
|
||||
"git.savannah.gnu.org/cgit/gnulib.git/tree/modules/COPYING?id=7b08932179d0d6b017f7df01a2ddf6e096b038e3": "FSFULLRSD",
|
||||
"git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id=a74c6b4ee49397cf330b333da1042bffa60ed14f#n1788": "BSD-4.3TAHOE",
|
||||
"git.savannah.gnu.org/cgit/indent.git/tree/doc/indent.texi?id=a74c6b4ee49397cf330b333da1042bffa60ed14f#n74": "Latex2e-translated-notice",
|
||||
"git.savannah.gnu.org/cgit/kazlib.git/tree/except.c?id=0062df360c2d17d57f6af19b0e444c51feb99036": "Kazlib",
|
||||
"git.savannah.gnu.org/cgit/wget.git/tree/util/trunc.c?h=v1.21.3&id=40747a11e44ced5a8ac628a41f879ced3e2ebce9#n6": "FSFAP-no-warranty-disclaimer",
|
||||
"git.sr.ht/~nabijaczleweli/ossp-var": "OSSP",
|
||||
"github.com/389ds/389-ds-base/blob/main/ldap/include/sysexits-compat.h#L15": "BSD-4.3TAHOE",
|
||||
"github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.0.txt": "ASWF-Digital-Assets-1.0",
|
||||
"github.com/AcademySoftwareFoundation/foundation/blob/main/digital_assets/aswf_digital_assets_license_v1.1.txt": "ASWF-Digital-Assets-1.1",
|
||||
"github.com/AdaCore/gnatcoll-core/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/AdaCore/gnatcoll-db/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/AdaCore/xmlada/blob/master/docs/index.rst": "AdaCore-doc",
|
||||
"github.com/BuildCraft/BuildCraft/blob/623d323b1868712f29f4a8b0979a02e8d1835131/LICENSE": "MMPL-1.0.1",
|
||||
"github.com/CommunitySpecification/1.0/blob/master/1._Community_Specification_License-v1.md": "Community-Spec-1.0",
|
||||
"github.com/CorsixTH/deps/blob/fd339a9f526d1d9c9f01ccf39e438a015da50035/licences/libgsm.txt": "TU-Berlin-2.0",
|
||||
"github.com/Cyan4973/xxHash/blob/dev/doc/xxhash_spec.md": "LPD-document",
|
||||
"github.com/DISIC/politique-de-contribution-open-source/blob/master/LICENSE.pdf": "etalab-2.0",
|
||||
"github.com/Distrotech/hdparm/blob/4517550db29a91420fb2b020349523b1b4512df2/LICENSE.TXT": "hdparm",
|
||||
"github.com/Distrotech/transfig/blob/master/transfig/transfig.c": "Xfig",
|
||||
"github.com/DrItanium/maya/blob/master/LICENSE.CLIPS": "Clips",
|
||||
"github.com/Dual-Life/mime-base64/blob/master/Base64.xs#L12": "metamail",
|
||||
"github.com/EthicalSource/hippocratic-license/blob/58c0e646d64ff6fbee275bfe2b9492f914e3ab2a/LICENSE.txt": "Hippocratic-2.1",
|
||||
"github.com/Fraunhofer-IIS/mpeghdec/blob/main/LICENSE.txt": "FDK-MPEG-H",
|
||||
"github.com/FreeRADIUS/freeradius-client/blob/master/COPYRIGHT#L32": "HPND-Fenneberg-Livingston",
|
||||
"github.com/GNOME/gcr/blob/master/docs/COPYING": "GCR-docs",
|
||||
"github.com/GNOME/libxml2/blob/master/dict.c": "ISC-Veillard",
|
||||
"github.com/HDFGroup/hdf5/?tab=License-1-ov-file#readme": "HDF5",
|
||||
"github.com/KhronosGroup/SPIRV-Cross/blob/main/LICENSES/LicenseRef-KhronosFreeUse.txt": "MIT-Khronos-old",
|
||||
"github.com/KhronosGroup/glslang/blob/main/LICENSE.txt#L949": "AML-glslang",
|
||||
"github.com/MariaDB/server/blob/11.6/libmysqld/lib_sql.cc": "Boehm-GC-without-fee",
|
||||
"github.com/Neo23x0/sigma/blob/master/LICENSE.Detection.Rules.md": "DRL-1.0",
|
||||
"github.com/OGRECave/ogre/blob/master/OgreMain/include/OgreSingleton.h#L28C3-L35C46": "Game-Programming-Gems",
|
||||
"github.com/Perl/perl5/blob/blead/util.c#L6136": "Martin-Birgmeier",
|
||||
"github.com/PicoEFI/PicoEFI/blob/2263ac1a2bc98e5534d64a4896d3cfbbae00745a/LICENSES/LicenseRef-scancode-bsd-no-disclaimer-unmodified.txt": "BSD-Source-Code-no-disclaimer",
|
||||
"github.com/PixarAnimationStudios/OpenSubdiv/blob/v3_5_0/opensubdiv/version.cpp#L2-L22": "Pixar",
|
||||
"github.com/PixarAnimationStudios/OpenSubdiv/raw/v3_5_0/LICENSE.txt": "Pixar",
|
||||
"github.com/RTimothyEdwards/magic/blob/master/LICENSE": "HPND-UC-export-US",
|
||||
"github.com/SELinuxProject/selinux/blob/master/libselinux/LICENSE": "libselinux-1.0",
|
||||
"github.com/SWI-Prolog/packages-clib/blob/master/sha1/brg_endian.h": "Brian-Gladman-3-Clause",
|
||||
"github.com/SWI-Prolog/packages-xpce/blob/161a40cd82004f731ba48024f9d30af388a7edf5/src/img/gifwrite.c#L21-L26": "FBM",
|
||||
"github.com/SWI-Prolog/swipl-devel/blob/master/src/os/dtoa.c": "dtoa",
|
||||
"github.com/SigmaHQ/Detection-Rule-License/blob/6ec7fbde6101d101b5b5d1fcb8f9b69fbc76c04a/LICENSE.Detection.Rules.md": "DRL-1.1",
|
||||
"github.com/Slicer/Slicer/blob/main/License.txt": "3D-Slicer-1.0",
|
||||
"github.com/Unidata/UDUNITS-2/blob/master/COPYRIGHT": "UCAR",
|
||||
"github.com/acpica/acpica/blob/master/source/common/acfileio.c#L119": "BSD-3-Clause-acpica",
|
||||
"github.com/adaptivecomputing/torque/blob/master/PBS_License.txt": "OpenPBS-2.3",
|
||||
"github.com/adversary-org/wtfnmf/raw/3f2cd8235a64350a57a51b9739715edaea63ec1a/COPYING.WTFNMFPL-utf8": "WTFNMFPL",
|
||||
"github.com/adversary-org/wtfnmf/raw/refs/tags/1.0/COPYING.WTFNMFPL": "WTFNMFPL",
|
||||
"github.com/ap/Text-Tabs/blob/master/lib.modern/Text/Tabs.pm#L148": "TTWL",
|
||||
"github.com/apache/apr/blob/trunk/LICENSE#L298C6-L298C29": "Caldera-no-preamble",
|
||||
"github.com/audacious-media-player/audacious/blob/master/COPYING": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"github.com/audacious-media-player/audacious/blob/master/src/audacious/main.cc": "BSD-2-Clause-pkgconf-disclaimer",
|
||||
"github.com/aws/mit-0": "MIT-0",
|
||||
"github.com/awsdocs/aws-cloud9-user-guide/blob/master/LICENSE-SAMPLECODE": "MIT-0",
|
||||
"github.com/bagder/curl/blob/master/COPYING": "curl",
|
||||
@@ -1772,6 +1804,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/file/file/blob/master/COPYING": "BSD-2-Clause-Darwin",
|
||||
"github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_acosh.c": "SunPro",
|
||||
"github.com/freebsd/freebsd-src/blob/main/lib/msun/src/e_lgammal.c": "SunPro",
|
||||
"github.com/freebsd/pkg/blob/main/src/add.c": "BSD-2-Clause-pos-unchanged",
|
||||
"github.com/gabegundy/atlserver/blob/master/License.txt": "MS-LPL",
|
||||
"github.com/greymass/swift-eosio/blob/master/LICENSE": "BSD-3-Clause-No-Military-License",
|
||||
"github.com/hamano/man2html/blob/master/man2html.c": "man2html",
|
||||
@@ -1784,6 +1817,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/jsommers/pytricia/blob/master/patricia.c#L33-L67": "BSD-4-Clause",
|
||||
"github.com/kfish/xsel/blob/master/COPYING": "HPND-sell-variant",
|
||||
"github.com/kohler/t1utils/blob/master/LICENSE": "MIT-Click",
|
||||
"github.com/krb5/krb5/blob/kfw-4.0.1-beta1/src/lib/crypto/builtin/aes/aesopt.h": "Brian-Gladman-3-Clause-no-conversion",
|
||||
"github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L111-L133": "HPND-export2-US",
|
||||
"github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L1157-L1182": "HPND-export-US-modify",
|
||||
"github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L140-L156": "Brian-Gladman-2-Clause",
|
||||
@@ -1793,6 +1827,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/krb5/krb5/blob/krb5-1.21.2-final/NOTICE#L831-L852": "HPND-export-US-acknowledgement",
|
||||
"github.com/krzyzanowskim/CryptoSwift/blob/main/LICENSE": "CryptoSwift",
|
||||
"github.com/lattera/freebsd/blob/master/sys/cam/cam.c#L4": "BSD-Source-beginning-file",
|
||||
"github.com/libarchive/libarchive/blob/master/libarchive_fe/line_reader.c": "BSD-2-Clause-pos-unchanged",
|
||||
"github.com/libcheck/check/blob/master/checkmk/checkmk.in": "checkmk",
|
||||
"github.com/libjpeg-turbo/libjpeg-turbo/blob/main/README.ijg#L117-L161": "IJG",
|
||||
"github.com/lsof-org/lsof/blob/master/COPYING": "lsof",
|
||||
@@ -1805,6 +1840,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/mm2/Little-CMS/blob/master/src/cmssm.c#L207": "softSurfer",
|
||||
"github.com/mruby/mruby/blob/83d12f8d52522cdb7c8cc46fad34821359f453e6/mrbgems/mruby-dir/src/Win/dirent.c#L127-L140": "HPND-Kevlin-Henney",
|
||||
"github.com/mtoyoda/sl/blob/master/LICENSE": "SL",
|
||||
"github.com/mvt-project/mvt/blob/main/LICENSE": "MVT-1.1",
|
||||
"github.com/n8n-io/n8n/blob/master/LICENSE.md": "SUL-1.0",
|
||||
"github.com/ncbi/datasets/blob/0ea4cd16b61e5b799d9cc55aecfa016d6c9bd2bf/LICENSE.md": "NCBI-PD",
|
||||
"github.com/ncbi/datasets/blob/master/LICENSE.md": "NCBI-PD",
|
||||
@@ -1815,6 +1851,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/novnc/noVNC/blob/master/core/crypto/des.js#L24": "Widget-Workshop",
|
||||
"github.com/oetiker/rrdtool-1.x/blob/master/src/plbasename.c#L8-L11": "gtkbook",
|
||||
"github.com/open-quantum-safe/liboqs/blob/40b01fdbb270f8614fde30e65d30e9da18c02393/src/common/rand/rand_nist.c#L1-L15": "NIST-Software",
|
||||
"github.com/open-webui/open-webui/blob/main/LICENSE": "BSD-3-Clause-OpenWebUI",
|
||||
"github.com/openjdk/jdk/blob/6162e2c5213c5dd7c1127fd9616b543efa898962/LICENSE": "GPL-2.0-or-later",
|
||||
"github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/LICENCE#L10": "SSH-OpenSSH",
|
||||
"github.com/openssh/openssh-portable/blob/1b11ea7c58cd5c59838b5fa574cd456d6047b2d4/pathnames.h": "SSH-short",
|
||||
@@ -1846,6 +1883,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/sigmavirus24/x11-ssh-askpass/blob/master/dynlist.c": "HPND-sell-variant-MIT-disclaimer-rev",
|
||||
"github.com/signal11/hidapi/blob/master/LICENSE-orig.txt": "HIDAPI",
|
||||
"github.com/slogan621/gtkbook": "gtkbook",
|
||||
"github.com/sudo-project/sudo/blob/c1307ea9ff340ce0538779f8e456501461fc44b7/plugins/sudoers/redblack.c#L24-L43": "BSD-ask-to-endorse",
|
||||
"github.com/swh/ladspa/blob/7bf6f3799fdba70fda297c2d8fd9f526803d9680/gsm/COPYRIGHT": "TU-Berlin-1.0",
|
||||
"github.com/tats/w3m/blob/master/COPYING": "w3m",
|
||||
"github.com/tcheneau/Routing/blob/f09f46fcfe636107f22f2c98348188a65a135d98/README.md": "NIST-PD",
|
||||
@@ -1856,6 +1894,7 @@ var urlToLicense = map[string]string{
|
||||
"github.com/uim/libgcroots/blob/master/COPYING": "Boehm-GC",
|
||||
"github.com/usnistgov/fipy/blob/86aaa5c2ba2c6f1be19593c5986071cf6568cc34/LICENSE.rst": "NIST-PD-fallback",
|
||||
"github.com/usnistgov/jsip/blob/59700e6926cbe96c5cdae897d9a7d2656b42abe3/LICENSE": "NIST-PD-fallback",
|
||||
"github.com/vattam/BSDGames/blob/master/atc/extern.c#L36": "atc-game",
|
||||
"github.com/vixie/cron/tree/545b3f5246824a9cda5905eeb7cf019c95e66995": "Vixie-Cron",
|
||||
"github.com/vstroebel/jpeg-encoder/blob/main/src/fdct.rs#L1-L72": "IJG",
|
||||
"github.com/wernerd/ZRTPCPP/blob/6b3cd8e6783642292bad0c21e3e5e5ce45ff3e03/cryptcommon/twofish.c#L113C3-L127": "Ferguson-Twofish",
|
||||
@@ -1869,6 +1908,7 @@ var urlToLicense = map[string]string{
|
||||
"gitlab.com/embeddable-common-lisp/ecl/-/blob/develop/src/lsp/loop.lsp": "LOOP",
|
||||
"gitlab.com/libeigen/eigen/-/blob/master/COPYING.MINPACK": "Minpack",
|
||||
"gitlab.common-lisp.net/cmucl/cmucl/-/blob/master/src/code/loop.lisp": "LOOP",
|
||||
"gitlab.freedesktop.org/dbus/dbus/-/blob/789d97ad53044f94351a958e785c2923643b346a/COPYING#L11-207": "AFL-2.1",
|
||||
"gitlab.freedesktop.org/freetype/freetype/-/raw/master/docs/FTL.TXT": "FTL",
|
||||
"gitlab.freedesktop.org/libopenraw/libopenraw/blob/master/lib/ljpegdecompressor.cpp#L32": "Cornell-Lossless-JPEG",
|
||||
"gitlab.freedesktop.org/mesa/glw/-/blob/master/README?ref_type=heads": "SGI-OpenGL",
|
||||
@@ -1892,6 +1932,7 @@ var urlToLicense = map[string]string{
|
||||
"gitlab.freedesktop.org/xorg/lib/libxinerama/-/blob/cc22c2f60c3862482562955116d5455263b443dc/COPYING#L44-66": "X11-no-permit-persons",
|
||||
"gitlab.freedesktop.org/xorg/lib/libxtst/-/blob/master/COPYING?ref_type=heads#L108-117": "HPND-doc-sell",
|
||||
"gitlab.freedesktop.org/xorg/lib/libxtst/-/blob/master/COPYING?ref_type=heads#L70-77": "HPND-doc",
|
||||
"gitlab.freedesktop.org/xorg/xserver/-/blob/dd5c2595a42d3ff0c4f18d9b53d1f6c3fd934fd4/COPYING#L365-389": "MIT",
|
||||
"gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type=heads#L1781": "HPND-sell-MIT-disclaimer-xserver",
|
||||
"gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type=heads#L239": "DEC-3-Clause",
|
||||
"gitlab.freedesktop.org/xorg/xserver/-/blob/master/COPYING?ref_type=heads#L752": "Adobe-Display-PostScript",
|
||||
@@ -1906,6 +1947,7 @@ var urlToLicense = map[string]string{
|
||||
"heasarc.gsfc.nasa.gov/docs/software/ftools/fv/doc/license.html": "CFITSIO",
|
||||
"helixcommunity.org/content/rpsl": "RPSL-1.0",
|
||||
"hstechdocs.helpsystems.com/manuals/globalscape/archive/cuteftp6/open_ssl_license_agreement.htm": "OpenSSL-standalone",
|
||||
"interoperable-europe.ec.europa.eu/sites/default/files/inline-files/EUPL%20v1_2%20EN(1).txt": "EUPL-1.2",
|
||||
"java.net/projects/javaeetutorial/pages/BerkeleyLicense": "BSD-3-Clause-No-Nuclear-License-2014",
|
||||
"javaee.github.io/glassfish/LICENSE": "CDDL-1.1",
|
||||
"jogamp.org/git/?p=gluegen.git;a=blob_plain;f=LICENSE.txt": "BSD-3-Clause-No-Nuclear-Warranty",
|
||||
@@ -1924,6 +1966,7 @@ var urlToLicense = map[string]string{
|
||||
"library.netapp.com/ecm/ecm_download_file/ECMP1196395": "OpenSSL-standalone",
|
||||
"license.coscl.org.cn/MulanPSL/": "MulanPSL-1.0",
|
||||
"license.coscl.org.cn/MulanPSL2": "MulanPSL-2.0",
|
||||
"license.mvt.re/1.1/": "MVT-1.1",
|
||||
"lists.gnu.org/archive/html/autoconf/2012-04/msg00061.html": "FSFULLRWD",
|
||||
"lists.opensource.org/pipermail/license-discuss_lists.opensource.org/2002-November/006304.html": "HPND",
|
||||
"mariadb.com/bsl11/": "BUSL-1.1",
|
||||
@@ -1965,99 +2008,105 @@ var urlToLicense = map[string]string{
|
||||
"opensource.linux-mirror.org/licenses/afl-1.1.txt": "AFL-1.1",
|
||||
"opensource.linux-mirror.org/licenses/afl-1.2.txt": "AFL-1.2",
|
||||
"opensource.linux-mirror.org/licenses/afl-2.1.txt": "AFL-2.1",
|
||||
"opensource.org/license/apache-2-0": "Apache-2.0",
|
||||
"opensource.org/license/mit/": "MIT",
|
||||
"opensource.org/license/olfl-1-3/": "OLFL-1.3",
|
||||
"opensource.org/license/osc-license-1-0": "OSC-1.0",
|
||||
"opensource.org/licenses/0BSD": "0BSD",
|
||||
"opensource.org/licenses/AGPL-3.0": "AGPL-3.0-or-later",
|
||||
"opensource.org/licenses/APL-1.0": "APL-1.0",
|
||||
"opensource.org/licenses/Apache-1.1": "Apache-1.1",
|
||||
"opensource.org/licenses/Apache-2.0": "Apache-2.0",
|
||||
"opensource.org/licenses/Artistic-1.0": "Artistic-1.0-cl8",
|
||||
"opensource.org/licenses/BSD-2-Clause": "BSD-2-Clause",
|
||||
"opensource.org/licenses/BSD-3-Clause": "BSD-3-Clause",
|
||||
"opensource.org/licenses/BSDplusPatent": "BSD-2-Clause-Patent",
|
||||
"opensource.org/licenses/BSL-1.0": "BSL-1.0",
|
||||
"opensource.org/licenses/CAL-1.0": "CAL-1.0-Combined-Work-Exception",
|
||||
"opensource.org/licenses/CATOSL-1.1": "CATOSL-1.1",
|
||||
"opensource.org/licenses/CNRI-Python": "CNRI-Python",
|
||||
"opensource.org/licenses/CPAL-1.0": "CPAL-1.0",
|
||||
"opensource.org/licenses/CPL-1.0": "CPL-1.0",
|
||||
"opensource.org/licenses/CUA-OPL-1.0": "CUA-OPL-1.0",
|
||||
"opensource.org/licenses/ECL-1.0": "ECL-1.0",
|
||||
"opensource.org/licenses/ECL-2.0": "ECL-2.0",
|
||||
"opensource.org/licenses/EFL-1.0": "EFL-1.0",
|
||||
"opensource.org/licenses/EFL-2.0": "EFL-2.0",
|
||||
"opensource.org/licenses/EPL-1.0": "EPL-1.0",
|
||||
"opensource.org/licenses/EUDatagrid": "EUDatagrid",
|
||||
"opensource.org/licenses/EUPL-1.1": "EUPL-1.1",
|
||||
"opensource.org/licenses/EUPL-1.2": "EUPL-1.2",
|
||||
"opensource.org/licenses/Entessa": "Entessa",
|
||||
"opensource.org/licenses/Fair": "Fair",
|
||||
"opensource.org/licenses/Frameworx-1.0": "Frameworx-1.0",
|
||||
"opensource.org/licenses/GPL-2.0": "GPL-2.0-or-later",
|
||||
"opensource.org/licenses/GPL-3.0": "GPL-3.0-or-later",
|
||||
"opensource.org/licenses/HPND": "HPND",
|
||||
"opensource.org/licenses/IPA": "IPA",
|
||||
"opensource.org/licenses/IPL-1.0": "IPL-1.0",
|
||||
"opensource.org/licenses/ISC": "ISC",
|
||||
"opensource.org/licenses/Intel": "Intel",
|
||||
"opensource.org/licenses/LGPL-2.1": "LGPL-2.1-or-later",
|
||||
"opensource.org/licenses/LGPL-3.0": "LGPL-3.0-or-later",
|
||||
"opensource.org/licenses/LPL-1.0": "LPL-1.0",
|
||||
"opensource.org/licenses/LPL-1.02": "LPL-1.02",
|
||||
"opensource.org/licenses/LPPL-1.3c": "LPPL-1.3c",
|
||||
"opensource.org/licenses/LiLiQ-P-1.1": "LiLiQ-P-1.1",
|
||||
"opensource.org/licenses/LiLiQ-R-1.1": "LiLiQ-R-1.1",
|
||||
"opensource.org/licenses/LiLiQ-Rplus-1.1": "LiLiQ-Rplus-1.1",
|
||||
"opensource.org/licenses/MIT": "MIT",
|
||||
"opensource.org/licenses/MPL-1.0": "MPL-1.0",
|
||||
"opensource.org/licenses/MPL-1.1": "MPL-1.1",
|
||||
"opensource.org/licenses/MPL-2.0": "MPL-2.0-no-copyleft-exception",
|
||||
"opensource.org/licenses/MS-PL": "MS-PL",
|
||||
"opensource.org/licenses/MS-RL": "MS-RL",
|
||||
"opensource.org/licenses/MirOS": "MirOS",
|
||||
"opensource.org/licenses/Motosoto": "Motosoto",
|
||||
"opensource.org/licenses/Multics": "Multics",
|
||||
"opensource.org/licenses/NASA-1.3": "NASA-1.3",
|
||||
"opensource.org/licenses/NCSA": "NCSA",
|
||||
"opensource.org/licenses/NGPL": "NGPL",
|
||||
"opensource.org/licenses/NOSL3.0": "NPOSL-3.0",
|
||||
"opensource.org/licenses/NTP": "NTP",
|
||||
"opensource.org/licenses/Naumen": "Naumen",
|
||||
"opensource.org/licenses/OCLC-2.0": "OCLC-2.0",
|
||||
"opensource.org/licenses/OFL-1.1": "OFL-1.1-no-RFN",
|
||||
"opensource.org/licenses/OGTSL": "OGTSL",
|
||||
"opensource.org/licenses/OPL-2.1": "OSET-PL-2.1",
|
||||
"opensource.org/licenses/OSL-1.0": "OSL-1.0",
|
||||
"opensource.org/licenses/OSL-2.1": "OSL-2.1",
|
||||
"opensource.org/licenses/OSL-3.0": "OSL-3.0",
|
||||
"opensource.org/licenses/PHP-3.0": "PHP-3.0",
|
||||
"opensource.org/licenses/PostgreSQL": "PostgreSQL",
|
||||
"opensource.org/licenses/Python-2.0": "Python-2.0",
|
||||
"opensource.org/licenses/QPL-1.0": "QPL-1.0",
|
||||
"opensource.org/licenses/RPL-1.1": "RPL-1.1",
|
||||
"opensource.org/licenses/RPL-1.5": "RPL-1.5",
|
||||
"opensource.org/licenses/RPSL-1.0": "RPSL-1.0",
|
||||
"opensource.org/licenses/RSCPL": "RSCPL",
|
||||
"opensource.org/licenses/SISSL": "SISSL",
|
||||
"opensource.org/licenses/SPL-1.0": "SPL-1.0",
|
||||
"opensource.org/licenses/SimPL-2.0": "SimPL-2.0",
|
||||
"opensource.org/licenses/Sleepycat": "Sleepycat",
|
||||
"opensource.org/licenses/UCL-1.0": "UCL-1.0",
|
||||
"opensource.org/licenses/UPL": "UPL-1.0",
|
||||
"opensource.org/licenses/VSL-1.0": "VSL-1.0",
|
||||
"opensource.org/licenses/W3C": "W3C",
|
||||
"opensource.org/licenses/Watcom-1.0": "Watcom-1.0",
|
||||
"opensource.org/licenses/Xnet": "Xnet",
|
||||
"opensource.org/licenses/ZPL-2.0": "ZPL-2.0",
|
||||
"opensource.org/licenses/Zlib": "Zlib",
|
||||
"opensource.org/licenses/afl-3.0": "AFL-3.0",
|
||||
"opensource.org/licenses/artistic-license-2.0": "Artistic-2.0",
|
||||
"opensource.org/licenses/attribution": "AAL",
|
||||
"opensource.org/licenses/cddl1": "CDDL-1.0",
|
||||
"opensource.org/licenses/nokia": "Nokia",
|
||||
"opensource.org/license/0BSD": "0BSD",
|
||||
"opensource.org/license/AAL": "AAL",
|
||||
"opensource.org/license/AFL-3.0": "AFL-3.0",
|
||||
"opensource.org/license/AGPL-3.0": "AGPL-3.0-or-later",
|
||||
"opensource.org/license/AGPL-3.0-only": "AGPL-3.0-only",
|
||||
"opensource.org/license/APL-1.0": "APL-1.0",
|
||||
"opensource.org/license/Artistic-1.0": "Artistic-1.0-cl8",
|
||||
"opensource.org/license/Artistic-2.0": "Artistic-2.0",
|
||||
"opensource.org/license/BSD-2-Clause": "BSD-2-Clause",
|
||||
"opensource.org/license/BSD-2-Clause-Patent": "BSD-2-Clause-Patent",
|
||||
"opensource.org/license/BSD-3-Clause": "BSD-3-Clause",
|
||||
"opensource.org/license/BSL-1.0": "BSL-1.0",
|
||||
"opensource.org/license/CAL-1.0": "CAL-1.0-Combined-Work-Exception",
|
||||
"opensource.org/license/CATOSL-1.1": "CATOSL-1.1",
|
||||
"opensource.org/license/CDDL-1.0": "CDDL-1.0",
|
||||
"opensource.org/license/CNRI-Python": "CNRI-Python",
|
||||
"opensource.org/license/CNRI-Python-GPL-Compatible": "CNRI-Python-GPL-Compatible",
|
||||
"opensource.org/license/CPAL-1.0": "CPAL-1.0",
|
||||
"opensource.org/license/CPL-1.0": "CPL-1.0",
|
||||
"opensource.org/license/CUA-OPL-1.0": "CUA-OPL-1.0",
|
||||
"opensource.org/license/ECL-1.0": "ECL-1.0",
|
||||
"opensource.org/license/ECL-2.0": "ECL-2.0",
|
||||
"opensource.org/license/EFL-1.0": "EFL-1.0",
|
||||
"opensource.org/license/EFL-2.0": "EFL-2.0",
|
||||
"opensource.org/license/EPL-1.0": "EPL-1.0",
|
||||
"opensource.org/license/EUDatagrid": "EUDatagrid",
|
||||
"opensource.org/license/EUPL-1.1": "EUPL-1.1",
|
||||
"opensource.org/license/EUPL-1.2": "EUPL-1.2",
|
||||
"opensource.org/license/Entessa": "Entessa",
|
||||
"opensource.org/license/Fair": "Fair",
|
||||
"opensource.org/license/Frameworx-1.0": "Frameworx-1.0",
|
||||
"opensource.org/license/GPL-2.0": "GPL-2.0-or-later",
|
||||
"opensource.org/license/GPL-2.0-only": "GPL-2.0-only",
|
||||
"opensource.org/license/GPL-3.0": "GPL-3.0-or-later",
|
||||
"opensource.org/license/GPL-3.0-only": "GPL-3.0-only",
|
||||
"opensource.org/license/HPND": "HPND",
|
||||
"opensource.org/license/IPA": "IPA",
|
||||
"opensource.org/license/IPL-1.0": "IPL-1.0",
|
||||
"opensource.org/license/ISC": "ISC",
|
||||
"opensource.org/license/Intel": "Intel",
|
||||
"opensource.org/license/LGPL-2.1": "LGPL-2.1-or-later",
|
||||
"opensource.org/license/LGPL-2.1-only": "LGPL-2.1-only",
|
||||
"opensource.org/license/LGPL-3.0": "LGPL-3.0-or-later",
|
||||
"opensource.org/license/LPL-1.0": "LPL-1.0",
|
||||
"opensource.org/license/LPL-1.02": "LPL-1.02",
|
||||
"opensource.org/license/LPPL-1.3c": "LPPL-1.3c",
|
||||
"opensource.org/license/LiLiQ-P-1.1": "LiLiQ-P-1.1",
|
||||
"opensource.org/license/LiLiQ-R-1.1": "LiLiQ-R-1.1",
|
||||
"opensource.org/license/LiLiQ-Rplus-1.1": "LiLiQ-Rplus-1.1",
|
||||
"opensource.org/license/MIT": "MIT",
|
||||
"opensource.org/license/MPL-1.0": "MPL-1.0",
|
||||
"opensource.org/license/MPL-1.1": "MPL-1.1",
|
||||
"opensource.org/license/MPL-2.0": "MPL-2.0-no-copyleft-exception",
|
||||
"opensource.org/license/MS-PL": "MS-PL",
|
||||
"opensource.org/license/MS-RL": "MS-RL",
|
||||
"opensource.org/license/MirOS": "MirOS",
|
||||
"opensource.org/license/Motosoto": "Motosoto",
|
||||
"opensource.org/license/Multics": "Multics",
|
||||
"opensource.org/license/NASA-1.3": "NASA-1.3",
|
||||
"opensource.org/license/NCSA": "NCSA",
|
||||
"opensource.org/license/NGPL": "NGPL",
|
||||
"opensource.org/license/NPOSL-3.0": "NPOSL-3.0",
|
||||
"opensource.org/license/NTP": "NTP",
|
||||
"opensource.org/license/Naumen": "Naumen",
|
||||
"opensource.org/license/Nokia": "Nokia",
|
||||
"opensource.org/license/OCLC-2.0": "OCLC-2.0",
|
||||
"opensource.org/license/OFL-1.1": "OFL-1.1-no-RFN",
|
||||
"opensource.org/license/OGTSL": "OGTSL",
|
||||
"opensource.org/license/OLFL-1.3": "OLFL-1.3",
|
||||
"opensource.org/license/OSC-1.0": "OSC-1.0",
|
||||
"opensource.org/license/OSET-PL-2.1": "OSET-PL-2.1",
|
||||
"opensource.org/license/OSL-1.0": "OSL-1.0",
|
||||
"opensource.org/license/OSL-2.1": "OSL-2.1",
|
||||
"opensource.org/license/OSL-3.0": "OSL-3.0",
|
||||
"opensource.org/license/PHP-3.0": "PHP-3.0",
|
||||
"opensource.org/license/PSF-2-0": "PSF-2.0",
|
||||
"opensource.org/license/PostgreSQL": "PostgreSQL",
|
||||
"opensource.org/license/Python-2.0": "Python-2.0",
|
||||
"opensource.org/license/Python-2.0.1": "Python-2.0.1",
|
||||
"opensource.org/license/QPL-1.0": "QPL-1.0",
|
||||
"opensource.org/license/RPL-1.1": "RPL-1.1",
|
||||
"opensource.org/license/RPL-1.5": "RPL-1.5",
|
||||
"opensource.org/license/RPSL-1.0": "RPSL-1.0",
|
||||
"opensource.org/license/RSCPL": "RSCPL",
|
||||
"opensource.org/license/SISSL": "SISSL",
|
||||
"opensource.org/license/SPL-1.0": "SPL-1.0",
|
||||
"opensource.org/license/SiMPL-2.0": "SimPL-2.0",
|
||||
"opensource.org/license/Sleepycat": "Sleepycat",
|
||||
"opensource.org/license/UCL-1.0": "UCL-1.0",
|
||||
"opensource.org/license/UPL-1.0": "UPL-1.0",
|
||||
"opensource.org/license/VSL-1.0": "VSL-1.0",
|
||||
"opensource.org/license/W3C": "W3C",
|
||||
"opensource.org/license/Watcom-1.0": "Watcom-1.0",
|
||||
"opensource.org/license/Xnet": "Xnet",
|
||||
"opensource.org/license/ZPL-2.0": "ZPL-2.0",
|
||||
"opensource.org/license/Zlib": "Zlib",
|
||||
"opensource.org/license/apache-1.1": "Apache-1.1",
|
||||
"opensource.org/license/apache-2.0": "Apache-2.0",
|
||||
"opensource.org/license/curl": "curl",
|
||||
"oss.sgi.com/projects/FreeB/": "SGI-B-1.1",
|
||||
"oss.sgi.com/projects/FreeB/SGIFreeSWLicB.1.0.html": "SGI-B-1.0",
|
||||
"oss.sgi.com/projects/FreeB/SGIFreeSWLicB.2.0.pdf": "SGI-B-2.0",
|
||||
@@ -2082,37 +2131,40 @@ var urlToLicense = map[string]string{
|
||||
"raw.githubusercontent.com/NTIA/scos-sensor/refs/heads/master/LICENSE.md": "NTIA-PD",
|
||||
"raw.githubusercontent.com/OpenMDW/OpenMDW/refs/heads/main/1.0/LICENSE.openmdw": "OpenMDW-1.0",
|
||||
"raw.githubusercontent.com/blackhole89/notekit/master/data/fonts/Charter%20license.txt": "Bitstream-Charter",
|
||||
"rc.quest.com/topics/openssh/license.php#tcpwrappers": "TCP-wrappers",
|
||||
"research.cs.wisc.edu/condor/license.html#condor": "Condor-1.1",
|
||||
"research.scea.com/scea_shared_source_license.html": "SCEA",
|
||||
"romanrm.net/mit-zero": "MIT-0",
|
||||
"sam.zoy.org/wtfpl/COPYING": "WTFPL",
|
||||
"scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web": "OFL-1.0-no-RFN",
|
||||
"scripts.sil.org/cms/scripts/page.php?item_id=OFL_web": "OFL-1.1-no-RFN",
|
||||
"slicer.org/LICENSE": "3D-Slicer-1.0",
|
||||
"solderpad.org/licenses/SHL-0.5/": "SHL-0.5",
|
||||
"solderpad.org/licenses/SHL-0.51/": "SHL-0.51",
|
||||
"source.icu-project.org/repos/icu/icu/trunk/license.html": "ICU",
|
||||
"sourceforge.net/p/buddy/gitcode/ci/master/tree/README": "Buddy",
|
||||
"sourceforge.net/p/ctrio/git/ci/master/tree/README": "ISC-Veillard",
|
||||
"sourceforge.net/p/mcj/xfig/ci/master/tree/src/Makefile.am": "Xfig",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/pnmtorle.c": "URT-RLE",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/rletopnm.c": "URT-RLE",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/ppm/ppmtompeg/jrevdct.c#l1189": "MPEG-SSG",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/editor/pnmstitch.c#l2": "pnmstitch",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/netpbm.c#l8": "HPND-Pbmplus",
|
||||
"sourceforge.net/p/sbcl/sbcl/ci/master/tree/src/code/loop.lisp": "LOOP",
|
||||
"sourceforge.net/p/trustedqsl/tqsl/ci/master/tree/LICENSE.txt": "TrustedQSL",
|
||||
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/dicom/README": "OFFIS",
|
||||
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/ljpg/": "IJG-short",
|
||||
"sourceforge.net/projects/sevenzip/files/LZMA%20SDK/": "LZMA-SDK-9.22",
|
||||
"sources.debian.org/copyright/license/debianutils/4.11.2/": "SMAIL-GPL",
|
||||
"sources.debian.org/src/openmpi/4.1.0-10/ompi/debuggers/msgq_interface.h/?hl=19#L19": "mpi-permissive",
|
||||
"sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1=1.1&r2=1.1.1.1&diff_format=s": "BitTorrent-1.0",
|
||||
"sourceware.org/bzip2/1.0.5/bzip2-manual-1.0.5.html": "bzip2-1.0.6",
|
||||
"sourceware.org/cgit/binutils-gdb/tree/include/coff/sym.h#n11": "MIPS",
|
||||
"sourceware.org/cgit/binutils-gdb/tree/include/coff/symconst.h#n11": "ThirdEye",
|
||||
"sourceware.org/cgit/valgrind/tree/mpi/libmpiwrap.c": "bzip2-1.0.6",
|
||||
"raw.githubusercontent.com/landtuna/open-mtools/6c04c2882eca7c4313ec267766ea53ac9bfe129b/mdump.c": "Informatica",
|
||||
"raw.githubusercontent.com/landtuna/open-mtools/6c04c2882eca7c4313ec267766ea53ac9bfe129b/msend.c": "Informatica",
|
||||
"rc.quest.com/topics/openssh/license.php#tcpwrappers": "TCP-wrappers",
|
||||
"research.cs.wisc.edu/condor/license.html#condor": "Condor-1.1",
|
||||
"research.scea.com/scea_shared_source_license.html": "SCEA",
|
||||
"romanrm.net/mit-zero": "MIT-0",
|
||||
"sam.zoy.org/wtfpl/COPYING": "WTFPL",
|
||||
"scancode-licensedb.aboutcode.org/bsd-unchanged": "BSD-2-Clause-pos-unchanged",
|
||||
"scripts.sil.org/cms/scripts/page.php?item_id=OFL10_web": "OFL-1.0-no-RFN",
|
||||
"scripts.sil.org/cms/scripts/page.php?item_id=OFL_web": "OFL-1.1-no-RFN",
|
||||
"slicer.org/LICENSE": "3D-Slicer-1.0",
|
||||
"solderpad.org/licenses/SHL-0.5/": "SHL-0.5",
|
||||
"solderpad.org/licenses/SHL-0.51/": "SHL-0.51",
|
||||
"source.icu-project.org/repos/icu/icu/trunk/license.html": "ICU",
|
||||
"sourceforge.net/p/buddy/gitcode/ci/master/tree/README": "Buddy",
|
||||
"sourceforge.net/p/ctrio/git/ci/master/tree/README": "ISC-Veillard",
|
||||
"sourceforge.net/p/mcj/xfig/ci/master/tree/src/Makefile.am": "Xfig",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/pnmtorle.c": "URT-RLE",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/other/rletopnm.c": "URT-RLE",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/converter/ppm/ppmtompeg/jrevdct.c#l1189": "MPEG-SSG",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/editor/pnmstitch.c#l2": "pnmstitch",
|
||||
"sourceforge.net/p/netpbm/code/HEAD/tree/super_stable/netpbm.c#l8": "HPND-Pbmplus",
|
||||
"sourceforge.net/p/sbcl/sbcl/ci/master/tree/src/code/loop.lisp": "LOOP",
|
||||
"sourceforge.net/p/trustedqsl/tqsl/ci/master/tree/LICENSE.txt": "TrustedQSL",
|
||||
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/dicom/README": "OFFIS",
|
||||
"sourceforge.net/p/xmedcon/code/ci/master/tree/libs/ljpg/": "IJG-short",
|
||||
"sourceforge.net/projects/sevenzip/files/LZMA%20SDK/": "LZMA-SDK-9.22",
|
||||
"sources.debian.org/copyright/license/debianutils/4.11.2/": "SMAIL-GPL",
|
||||
"sources.debian.org/src/openmpi/4.1.0-10/ompi/debuggers/msgq_interface.h/?hl=19#L19": "mpi-permissive",
|
||||
"sources.gentoo.org/cgi-bin/viewvc.cgi/gentoo-x86/licenses/BitTorrent?r1=1.1&r2=1.1.1.1&diff_format=s": "BitTorrent-1.0",
|
||||
"sourceware.org/bzip2/1.0.5/bzip2-manual-1.0.5.html": "bzip2-1.0.6",
|
||||
"sourceware.org/cgit/binutils-gdb/tree/include/coff/sym.h#n11": "MIPS",
|
||||
"sourceware.org/cgit/binutils-gdb/tree/include/coff/symconst.h#n11": "ThirdEye",
|
||||
"sourceware.org/cgit/valgrind/tree/mpi/libmpiwrap.c": "bzip2-1.0.6",
|
||||
"sourceware.org/git/?p=binutils-gdb.git;a=blob;f=libiberty/strcasecmp.c;h=131d81c2ce7881fa48c363dc5bf5fb302c61ce0b;hb=HEAD": "BSD-4.3RENO",
|
||||
"sourceware.org/git/?p=binutils-gdb.git;a=blob;f=readline/readline/support/wcwidth.c;h=0f5ec995796f4813abbcf4972aec0378ab74722a;hb=HEAD#l55": "HPND-Markus-Kuhn",
|
||||
"sourceware.org/git/?p=bzip2.git;a=blob;f=LICENSE;hb=bzip2-1.0.6": "bzip2-1.0.6",
|
||||
@@ -2129,6 +2181,7 @@ var urlToLicense = map[string]string{
|
||||
"tahoe-lafs.org/trac/tahoe-lafs/browser/trunk/COPYING.TGPPL.rst": "TGPPL-1.0",
|
||||
"ti.arc.nasa.gov/opensource/nosa/": "NASA-1.3",
|
||||
"trac.enlightenment.org/e/browser/trunk/imlib2/COPYING": "Imlib2",
|
||||
"tunes.org/legalese/bugroff.html": "Bugroff",
|
||||
"ubuntu.com/legal/font-licence": "Ubuntu-font-1.0",
|
||||
"unlicense.org/": "Unlicense",
|
||||
"vimdoc.sourceforge.net/htmldoc/uganda.html": "Vim",
|
||||
@@ -2139,6 +2192,7 @@ var urlToLicense = map[string]string{
|
||||
"web.archive.org/web/20041020171434/http://www.rosenlaw.com/osl2.0.html": "OSL-2.0",
|
||||
"web.archive.org/web/20050212003940/http://www.rosenlaw.com/osl21.htm": "OSL-2.1",
|
||||
"web.archive.org/web/20060319014854/http://info.borland.com/devsupport/interbase/opensource/IPL.html": "Interbase-1.0",
|
||||
"web.archive.org/web/20061013013632/http://opensource.linux-mirror.org/licenses/afl-2.1.txt": "AFL-2.1",
|
||||
"web.archive.org/web/20080207013128/http://www.gnome.org/fonts/": "Bitstream-Vera",
|
||||
"web.archive.org/web/20100302225219/http://www.zimbra.com/license/zimbra-public-license-1-3.html": "Zimbra-1.3",
|
||||
"web.archive.org/web/20111123062036/http://research.cs.wisc.edu/condor/license.html#condor": "Condor-1.1",
|
||||
@@ -2243,11 +2297,11 @@ var urlToLicense = map[string]string{
|
||||
"www.jython.org/license.html": "CNRI-Jython",
|
||||
"www.kermitproject.org/ck90.html#source": "HPND-export-US",
|
||||
"www.kernel.org/doc/man-pages/licenses.html": "Linux-man-pages-copyleft",
|
||||
"www.latex-project.org/lppl/LPPL-1.3c.txt": "LPPL-1.3c",
|
||||
"www.latex-project.org/lppl/lppl-1-0.txt": "LPPL-1.0",
|
||||
"www.latex-project.org/lppl/lppl-1-1.txt": "LPPL-1.1",
|
||||
"www.latex-project.org/lppl/lppl-1-2.txt": "LPPL-1.2",
|
||||
"www.latex-project.org/lppl/lppl-1-3a.txt": "LPPL-1.3a",
|
||||
"www.latex-project.org/lppl/lppl-1-3c.txt": "LPPL-1.3c",
|
||||
"www.lemis.com/grog/UNIX/ancient-source-all.pdf": "Caldera",
|
||||
"www.libpng.org/pub/png/src/libpng-LICENSE.txt": "libpng-2.0",
|
||||
"www.mail-archive.com/debian-bugs-rc@lists.debian.org/msg11494.html": "Symlinks",
|
||||
@@ -2269,16 +2323,17 @@ var urlToLicense = map[string]string{
|
||||
"www.netlib.org/lapack/LICENSE.txt": "BSD-3-Clause-Open-MPI",
|
||||
"www.netlib.org/minpack/disclaimer": "Minpack",
|
||||
"www.networkcultures.org/_uploads/%233notebook_telekommunist.pdf": "PPL",
|
||||
"www.oclc.org/research/activities/software/license/v2final.htm": "OCLC-2.0",
|
||||
"www.ogc.org/ogc/software/1.0": "OGC-1.0",
|
||||
"www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2": "CERN-OHL-W-2.0",
|
||||
"www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1": "CERN-OHL-1.1",
|
||||
"www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2": "CERN-OHL-1.2",
|
||||
"www.open-mpi.org/community/license.php": "BSD-3-Clause-Open-MPI",
|
||||
"www.opencascade.com/content/occt-public-license": "OCCT-PL",
|
||||
"www.opendatacommons.org/licenses/odbl/1.0/": "ODbL-1.0",
|
||||
"www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt": "OGTSL",
|
||||
"www.openhub.net/licenses/mslpl": "MS-LPL",
|
||||
"www.nist.gov/open/copyright-fair-use-and-licensing-statements-srd-data-software-and-technical-series-publications": "NIST-Software",
|
||||
"www.oclc.org/research/activities/software/license/v2final.htm": "OCLC-2.0",
|
||||
"www.ogc.org/ogc/software/1.0": "OGC-1.0",
|
||||
"www.ohwr.org/project/cernohl/wikis/Documents/CERN-OHL-version-2": "CERN-OHL-W-2.0",
|
||||
"www.ohwr.org/project/licenses/wikis/cern-ohl-v1.1": "CERN-OHL-1.1",
|
||||
"www.ohwr.org/project/licenses/wikis/cern-ohl-v1.2": "CERN-OHL-1.2",
|
||||
"www.open-mpi.org/community/license.php": "BSD-3-Clause-Open-MPI",
|
||||
"www.opencascade.com/content/occt-public-license": "OCCT-PL",
|
||||
"www.opendatacommons.org/licenses/odbl/1.0/": "ODbL-1.0",
|
||||
"www.opengroup.org/testing/downloads/The_Open_Group_TSL.txt": "OGTSL",
|
||||
"www.openhub.net/licenses/mslpl": "MS-LPL",
|
||||
"www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=1cae062821881f41b73012ba816434897abf4205": "OLDAP-2.6",
|
||||
"www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=37b4b3f6cc4bf34e1d3dec61e69914b9819d8894": "NBPL-1.0",
|
||||
"www.openldap.org/devel/gitweb.cgi?p=openldap.git;a=blob;f=LICENSE;hb=42b0383c50c299977b5893ee695cf4e486fb0dc7": "OLDAP-1.2",
|
||||
@@ -2301,7 +2356,7 @@ var urlToLicense = map[string]string{
|
||||
"www.opensource.apple.com/license/apsl/": "APSL-2.0",
|
||||
"www.opensource.apple.com/source/IOSerialFamily/IOSerialFamily-7/APPLE_LICENSE": "APSL-1.1",
|
||||
"www.opensource.apple.com/source/tcl/tcl-5/tcl/generic/regfronts.c": "Spencer-99",
|
||||
"www.opensource.org/licenses/EPL-2.0": "EPL-2.0",
|
||||
"www.opensource.org/license/EPL-2.0": "EPL-2.0",
|
||||
"www.openssl.org/source/license.html": "OpenSSL",
|
||||
"www.osetfoundation.org/public-license": "OSET-PL-2.1",
|
||||
"www.parliament.uk/site-information/copyright-parliament/open-parliament-licence/": "OPL-UK-3.0",
|
||||
|
||||
@@ -123,6 +123,96 @@ func TestLicenseByURL_AlternateScheme(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLicenseByURL_OpenSourceOrgVariants(t *testing.T) {
|
||||
// SPDX has changed the form of opensource.org URLs across list versions (e.g. 3.29.0 moved
|
||||
// opensource.org/licenses/MIT to opensource.org/license/MIT); every form should keep resolving.
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
wantID string
|
||||
}{
|
||||
{name: "old plural path", url: "http://opensource.org/licenses/MIT", wantID: "MIT"},
|
||||
{name: "new singular path", url: "https://opensource.org/license/MIT", wantID: "MIT"},
|
||||
{name: "lowercase with trailing slash", url: "https://opensource.org/license/mit/", wantID: "MIT"},
|
||||
{name: "plural path with trailing slash", url: "https://opensource.org/licenses/MIT/", wantID: "MIT"},
|
||||
{name: "www prefix", url: "https://www.opensource.org/licenses/MIT", wantID: "MIT"},
|
||||
{name: "uppercase host", url: "https://WWW.OpenSource.org/licenses/MIT", wantID: "MIT"},
|
||||
{name: "old plural path BSD-3-Clause", url: "https://opensource.org/licenses/BSD-3-Clause", wantID: "BSD-3-Clause"},
|
||||
{name: "new singular path BSD-3-Clause", url: "https://opensource.org/license/BSD-3-Clause", wantID: "BSD-3-Clause"},
|
||||
{name: "lowercase BSD-3-Clause", url: "https://opensource.org/licenses/bsd-3-clause", wantID: "BSD-3-Clause"},
|
||||
{name: "old plural path Apache-2.0", url: "http://opensource.org/licenses/Apache-2.0", wantID: "Apache-2.0"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
info, found := LicenseByURL(tt.url)
|
||||
if !found {
|
||||
t.Fatalf("LicenseByURL(%q) found = false, want true", tt.url)
|
||||
}
|
||||
if info.ID != tt.wantID {
|
||||
t.Errorf("LicenseByURL(%q) ID = %v, want %v", tt.url, info.ID, tt.wantID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLicenseByURL_NonOpenSourceOrgStaysExact(t *testing.T) {
|
||||
// the relaxed matching is scoped to opensource.org; other hosts may have case-sensitive paths
|
||||
if _, found := LicenseByURL("https://www.apache.org/licenses/license-2.0"); found {
|
||||
t.Errorf("LicenseByURL() found = true for case-altered non-opensource.org URL, want false")
|
||||
}
|
||||
if _, found := LicenseByURL("https://opensource.org/licenses/not-a-real-license"); found {
|
||||
t.Errorf("LicenseByURL() found = true for unknown opensource.org URL, want false")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeOpenSourceOrgURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
url string
|
||||
want string
|
||||
wantOK bool
|
||||
}{
|
||||
{url: "opensource.org/licenses/MIT", want: "opensource.org/license/mit", wantOK: true},
|
||||
{url: "opensource.org/license/mit/", want: "opensource.org/license/mit", wantOK: true},
|
||||
{url: "www.opensource.org/licenses/EPL-2.0", want: "opensource.org/license/epl-2.0", wantOK: true},
|
||||
{url: "lists.opensource.org/pipermail/license-discuss", wantOK: false},
|
||||
{url: "www.apache.org/licenses/LICENSE-2.0", wantOK: false},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.url, func(t *testing.T) {
|
||||
got, ok := normalizeOpenSourceOrgURL(tt.url)
|
||||
if ok != tt.wantOK {
|
||||
t.Fatalf("normalizeOpenSourceOrgURL(%q) ok = %v, want %v", tt.url, ok, tt.wantOK)
|
||||
}
|
||||
if got != tt.want {
|
||||
t.Errorf("normalizeOpenSourceOrgURL(%q) = %q, want %q", tt.url, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildOpenSourceOrgURLToLicense(t *testing.T) {
|
||||
index := buildOpenSourceOrgURLToLicense(map[string]string{
|
||||
"opensource.org/licenses/MIT": "MIT",
|
||||
"opensource.org/license/mit/": "MIT",
|
||||
"opensource.org/licenses/Foo": "Foo-1.0",
|
||||
"opensource.org/license/foo": "Foo-2.0",
|
||||
"opensource.org/license/FOO/": "Foo-1.0",
|
||||
"www.apache.org/licenses/Bar": "Bar",
|
||||
})
|
||||
|
||||
if got := index["opensource.org/license/mit"]; got != "MIT" {
|
||||
t.Errorf("index[mit] = %q, want MIT", got)
|
||||
}
|
||||
if got, exists := index["opensource.org/license/foo"]; exists {
|
||||
t.Errorf("index[foo] = %q, want no entry for URL forms mapping to different licenses", got)
|
||||
}
|
||||
if len(index) != 1 {
|
||||
t.Errorf("len(index) = %d, want 1 (non-opensource.org URLs excluded): %v", len(index), index)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStripScheme(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -8,12 +8,12 @@ import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/internal/tmpdir"
|
||||
)
|
||||
|
||||
@@ -161,13 +161,9 @@ func TestSparsePlacementIsNotAnAllocationKnob(t *testing.T) {
|
||||
b := newTestBuffer(t, WithMemLimit(1<<20))
|
||||
mustWrite(t, b, 0, repeat('A', 64))
|
||||
|
||||
var before, after runtime.MemStats
|
||||
runtime.GC()
|
||||
runtime.ReadMemStats(&before)
|
||||
mustWrite(t, b, far, repeat('B', 64))
|
||||
runtime.ReadMemStats(&after)
|
||||
|
||||
allocated := after.TotalAlloc - before.TotalAlloc
|
||||
allocated := testutils.MeasureAlloc(t, func() {
|
||||
mustWrite(t, b, far, repeat('B', 64))
|
||||
})
|
||||
t.Logf("writing 64 bytes at offset %d allocated %d bytes", far, allocated)
|
||||
assert.Less(t, allocated, uint64(1<<20),
|
||||
"a far placement must cost its own bytes, not its offset")
|
||||
@@ -714,15 +710,11 @@ func TestMemoryTierGrowsAmortized(t *testing.T) {
|
||||
)
|
||||
b := newTestBuffer(t, WithMemLimit(limit))
|
||||
|
||||
var before, after runtime.MemStats
|
||||
runtime.GC()
|
||||
runtime.ReadMemStats(&before)
|
||||
for off := int64(0); off < limit; off += chunk {
|
||||
mustWrite(t, b, off, repeat('A', chunk))
|
||||
}
|
||||
runtime.ReadMemStats(&after)
|
||||
|
||||
allocated := after.TotalAlloc - before.TotalAlloc
|
||||
allocated := testutils.MeasureAlloc(t, func() {
|
||||
for off := int64(0); off < limit; off += chunk {
|
||||
mustWrite(t, b, off, repeat('A', chunk))
|
||||
}
|
||||
})
|
||||
t.Logf("filling a %d byte tier in %d byte chunks allocated %d bytes", limit, chunk, allocated)
|
||||
assert.Less(t, allocated, uint64(4*limit),
|
||||
"growth has to be amortized, not a fresh copy of the whole tier per write")
|
||||
|
||||
@@ -51,7 +51,8 @@ func runTaskSafely(ctx context.Context, t Task, resolver file.Resolver, s sbomsy
|
||||
// handle individual cataloger panics
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
err = fmt.Errorf("%v at:\n%s", e, string(debug.Stack()))
|
||||
log.WithFields("task", t.Name()).Debugf("task panic stack:\n%s", debug.Stack())
|
||||
err = fmt.Errorf("panic in task %q: %v", t.Name(), e)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -8,8 +8,12 @@ import (
|
||||
"github.com/wagoodman/go-progress"
|
||||
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/event/monitor"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func Test_TaskExecutor_PanicHandling(t *testing.T) {
|
||||
@@ -21,6 +25,20 @@ func Test_TaskExecutor_PanicHandling(t *testing.T) {
|
||||
Manual: progress.NewManual(-1),
|
||||
})
|
||||
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), "executor_test.go")
|
||||
require.EqualError(t, err, `panic in task "panicking-cataloger": something bad happened`)
|
||||
}
|
||||
|
||||
func Test_RunTask_GenericParserPanicBecomesUnknown(t *testing.T) {
|
||||
c := generic.NewCataloger("panicking-cataloger").
|
||||
WithParserByPath(func(context.Context, file.Resolver, *generic.Environment, file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
panic("boom")
|
||||
}, "executor_test.go")
|
||||
s := &sbom.SBOM{}
|
||||
|
||||
err := RunTask(context.Background(), NewPackageTask(DefaultCatalogingFactoryConfig(), c), file.NewMockResolverForPaths("executor_test.go"), sbomsync.NewBuilder(s), &monitor.TaskProgress{
|
||||
Manual: progress.NewManual(-1),
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Len(t, s.Artifacts.Unknowns, 1)
|
||||
}
|
||||
|
||||
@@ -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,31 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// MeasureAlloc reports the bytes allocated while fn ran.
|
||||
//
|
||||
// This exists for guards whose whole point is "a small input cannot make us reserve a large buffer".
|
||||
// Only a byte count states that property: asserting that an error comes back would keep passing if the
|
||||
// allocation were hoisted above the check, which is the bug these guards were written for.
|
||||
//
|
||||
// Two things to know when using it:
|
||||
//
|
||||
// - TotalAlloc is process-wide, so a test calling this must not call t.Parallel. Another test's
|
||||
// allocations would land in the measurement.
|
||||
// - a budget assertion is only meaningful if the fixture would really blow past it unguarded. Prefer
|
||||
// measuring the unguarded path too and asserting it exceeds the declared size, so a fixture that
|
||||
// quietly stops being a bomb fails instead of passing.
|
||||
func MeasureAlloc(t testing.TB, fn func()) uint64 {
|
||||
t.Helper()
|
||||
|
||||
var before, after runtime.MemStats
|
||||
runtime.GC()
|
||||
runtime.ReadMemStats(&before)
|
||||
fn()
|
||||
runtime.ReadMemStats(&after)
|
||||
|
||||
return after.TotalAlloc - before.TotalAlloc
|
||||
}
|
||||
@@ -183,6 +183,10 @@ func visitErrors(err error, fn func(error) error) error {
|
||||
// unwrap singly wrapped errors
|
||||
if e, ok := err.(interface{ Unwrap() error }); ok {
|
||||
wrapped := e.Unwrap()
|
||||
if wrapped == nil {
|
||||
// nothing is wrapped (e.g. a sync.PanicError with a non-error value), so this is a leaf
|
||||
return fn(err)
|
||||
}
|
||||
got := visitErrors(wrapped, fn)
|
||||
if got == nil {
|
||||
return nil
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/go-sync"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
@@ -234,3 +235,11 @@ func Test_Append(t *testing.T) {
|
||||
e3 = Appendf(e2, file.NewLocation("l3"), "%s", "e3")
|
||||
require.Equal(t, "l1: e1\nl3: e3", e3.Error())
|
||||
}
|
||||
|
||||
func Test_ExtractCoordinateErrors_keepsNilUnwrap(t *testing.T) {
|
||||
// sync.PanicError unwraps to nil when the panic value is not an error
|
||||
err := sync.PanicError{Value: "x"}
|
||||
unknowns, remaining := ExtractCoordinateErrors(err)
|
||||
require.Empty(t, unknowns)
|
||||
require.Equal(t, err, remaining)
|
||||
}
|
||||
|
||||
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"
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"debug/macho"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
|
||||
"github.com/bmatcuk/doublestar/v4"
|
||||
@@ -89,7 +90,7 @@ func (i *Cataloger) CatalogCtx(ctx context.Context, resolver file.Resolver) (map
|
||||
return results, errs
|
||||
}
|
||||
|
||||
func processExecutableLocation(loc file.Location, resolver file.Resolver) (*file.Executable, error) {
|
||||
func processExecutableLocation(loc file.Location, resolver file.Resolver) (exec *file.Executable, err error) {
|
||||
reader, err := resolver.FileContentsByLocation(loc)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", loc.RealPath).Debug("unable to get file contents")
|
||||
@@ -97,6 +98,15 @@ func processExecutableLocation(loc file.Location, resolver file.Resolver) (*file
|
||||
}
|
||||
defer internal.CloseAndLogError(reader, loc.RealPath)
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// a malformed binary must not take down the whole SBOM; the caller records this as an unknown
|
||||
log.WithFields("path", loc.RealPath, "panic", r).Debug("recovered from panic while reading executable")
|
||||
log.Tracef("executable panic stack:\n%s", debug.Stack())
|
||||
exec, err = nil, fmt.Errorf("recovered from panic while reading executable: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
uReader, err := unionreader.GetUnionReader(reader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", loc.RealPath).Debug("unable to get union reader")
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package executable
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
type panicReader struct{}
|
||||
|
||||
func (panicReader) Read([]byte) (int, error) { panic("boom") }
|
||||
|
||||
type panickingContentsResolver struct {
|
||||
*file.MockResolver
|
||||
}
|
||||
|
||||
func (r panickingContentsResolver) FilesByMIMEType(...string) ([]file.Location, error) {
|
||||
return r.FilesByPath("cataloger_test.go")
|
||||
}
|
||||
|
||||
func (r panickingContentsResolver) FileContentsByLocation(file.Location) (io.ReadCloser, error) {
|
||||
return io.NopCloser(panicReader{}), nil
|
||||
}
|
||||
|
||||
func TestCatalogCtx_panicBecomesUnknown(t *testing.T) {
|
||||
resolver := panickingContentsResolver{file.NewMockResolverForPaths("cataloger_test.go")}
|
||||
|
||||
results, err := NewCataloger(DefaultConfig()).CatalogCtx(context.Background(), resolver)
|
||||
|
||||
require.Empty(t, results)
|
||||
unknowns, remaining := unknown.ExtractCoordinateErrors(err)
|
||||
require.NoError(t, remaining)
|
||||
require.Len(t, unknowns, 1)
|
||||
require.ErrorContains(t, unknowns[0].Reason, "recovered from panic while reading executable: boom")
|
||||
}
|
||||
@@ -156,32 +156,12 @@ func hasElfDynFlag1(f *elf.File, flag elf.DynFlag1) bool {
|
||||
}
|
||||
|
||||
func hasElfDynTag(f *elf.File, tag elf.DynTag) bool {
|
||||
// source https://github.com/golang/go/blob/9b4b3e5acca2dabe107fa2c3ed963097d78a4562/src/cmd/cgo/internal/testshared/shared_test.go#L280
|
||||
|
||||
ds := f.SectionByType(elf.SHT_DYNAMIC)
|
||||
if ds == nil {
|
||||
return false
|
||||
}
|
||||
d, err := ds.Data()
|
||||
vals, err := f.DynValue(tag)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to read dynamic section from elf file")
|
||||
return false
|
||||
}
|
||||
|
||||
for len(d) > 0 {
|
||||
var t elf.DynTag
|
||||
switch f.Class {
|
||||
case elf.ELFCLASS32:
|
||||
t = elf.DynTag(f.ByteOrder.Uint32(d[0:4]))
|
||||
d = d[8:]
|
||||
case elf.ELFCLASS64:
|
||||
t = elf.DynTag(f.ByteOrder.Uint64(d[0:8]))
|
||||
d = d[16:]
|
||||
}
|
||||
if t == tag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return len(vals) > 0
|
||||
}
|
||||
|
||||
func isELFPIE(f *elf.File) bool {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package executable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/elf"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
@@ -226,3 +228,89 @@ func Test_elfHasExports(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_hasElfDynTag(t *testing.T) {
|
||||
dynEntry := func(class elf.Class, tag elf.DynTag) []byte {
|
||||
if class == elf.ELFCLASS32 {
|
||||
return binary.LittleEndian.AppendUint32(binary.LittleEndian.AppendUint32(nil, uint32(tag)), 0)
|
||||
}
|
||||
return binary.LittleEndian.AppendUint64(binary.LittleEndian.AppendUint64(nil, uint64(tag)), 0)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
class elf.Class
|
||||
dynamic []byte
|
||||
want bool
|
||||
}{
|
||||
{
|
||||
name: "32-bit tag present",
|
||||
class: elf.ELFCLASS32,
|
||||
dynamic: dynEntry(elf.ELFCLASS32, elf.DT_BIND_NOW),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "64-bit tag present",
|
||||
class: elf.ELFCLASS64,
|
||||
dynamic: dynEntry(elf.ELFCLASS64, elf.DT_BIND_NOW),
|
||||
want: true,
|
||||
},
|
||||
{
|
||||
name: "64-bit tag absent",
|
||||
class: elf.ELFCLASS64,
|
||||
dynamic: dynEntry(elf.ELFCLASS64, elf.DT_NULL),
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "32-bit truncated dynamic section",
|
||||
class: elf.ELFCLASS32,
|
||||
dynamic: dynEntry(elf.ELFCLASS32, elf.DT_BIND_NOW)[:4],
|
||||
want: false,
|
||||
},
|
||||
{
|
||||
name: "64-bit truncated dynamic section",
|
||||
class: elf.ELFCLASS64,
|
||||
dynamic: dynEntry(elf.ELFCLASS64, elf.DT_BIND_NOW)[:12],
|
||||
want: false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
f, err := elf.NewFile(bytes.NewReader(elfWithDynamicSection(t, tt.class, tt.dynamic)))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, hasElfDynTag(f, elf.DT_BIND_NOW))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// elfWithDynamicSection builds a little-endian ELF holding only a null section and a SHT_DYNAMIC section.
|
||||
func elfWithDynamicSection(t *testing.T, class elf.Class, dynamic []byte) []byte {
|
||||
t.Helper()
|
||||
ident := [elf.EI_NIDENT]byte{0x7f, 'E', 'L', 'F', byte(class), byte(elf.ELFDATA2LSB), byte(elf.EV_CURRENT)}
|
||||
var buf bytes.Buffer
|
||||
write := func(v any) {
|
||||
require.NoError(t, binary.Write(&buf, binary.LittleEndian, v))
|
||||
}
|
||||
|
||||
if class == elf.ELFCLASS32 {
|
||||
ehsize, shentsize := binary.Size(elf.Header32{}), binary.Size(elf.Section32{})
|
||||
dataOff := ehsize + 2*shentsize
|
||||
write(elf.Header32{
|
||||
Ident: ident, Type: uint16(elf.ET_DYN), Machine: uint16(elf.EM_386), Version: uint32(elf.EV_CURRENT),
|
||||
Shoff: uint32(ehsize), Ehsize: uint16(ehsize), Shentsize: uint16(shentsize), Shnum: 2,
|
||||
})
|
||||
write(elf.Section32{})
|
||||
write(elf.Section32{Type: uint32(elf.SHT_DYNAMIC), Off: uint32(dataOff), Size: uint32(len(dynamic)), Addralign: 1})
|
||||
} else {
|
||||
ehsize, shentsize := binary.Size(elf.Header64{}), binary.Size(elf.Section64{})
|
||||
dataOff := ehsize + 2*shentsize
|
||||
write(elf.Header64{
|
||||
Ident: ident, Type: uint16(elf.ET_DYN), Machine: uint16(elf.EM_X86_64), Version: uint32(elf.EV_CURRENT),
|
||||
Shoff: uint64(ehsize), Ehsize: uint16(ehsize), Shentsize: uint16(shentsize), Shnum: 2,
|
||||
})
|
||||
write(elf.Section64{})
|
||||
write(elf.Section64{Type: uint32(elf.SHT_DYNAMIC), Off: uint64(dataOff), Size: uint64(len(dynamic)), Addralign: 1})
|
||||
}
|
||||
buf.Write(dynamic)
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
|
||||
@@ -87,7 +88,7 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
|
||||
return results, err
|
||||
}
|
||||
|
||||
func (i *Cataloger) catalogLocation(ctx context.Context, resolver file.Resolver, location file.Location) ([]file.Digest, error) {
|
||||
func (i *Cataloger) catalogLocation(ctx context.Context, resolver file.Resolver, location file.Location) (digests []file.Digest, err error) {
|
||||
meta, err := resolver.FileMetadataByLocation(location)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -104,7 +105,16 @@ func (i *Cataloger) catalogLocation(ctx context.Context, resolver file.Resolver,
|
||||
}
|
||||
defer internal.CloseAndLogError(contentReader, location.AccessPath)
|
||||
|
||||
digests, err := intFile.NewDigestsFromFile(ctx, contentReader, i.hashes)
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// the caller records this as an unknown for the location instead of failing the whole SBOM
|
||||
log.WithFields("path", location.RealPath, "panic", r).Debug("recovered from panic while digesting file")
|
||||
log.Tracef("file digest panic stack:\n%s", debug.Stack())
|
||||
digests, err = nil, fmt.Errorf("recovered from panic while digesting file: %v", r)
|
||||
}
|
||||
}()
|
||||
|
||||
digests, err = intFile.NewDigestsFromFile(ctx, contentReader, i.hashes)
|
||||
if err != nil {
|
||||
return nil, internal.ErrPath{Context: "digests-cataloger", Path: location.RealPath, Err: err}
|
||||
}
|
||||
|
||||
@@ -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/unknown"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
@@ -202,3 +203,27 @@ func TestFileDigestCataloger_GivenCoordinates(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type panicReader struct{}
|
||||
|
||||
func (panicReader) Read([]byte) (int, error) { panic("boom") }
|
||||
|
||||
type panickingContentsResolver struct {
|
||||
*file.MockResolver
|
||||
}
|
||||
|
||||
func (r panickingContentsResolver) FileContentsByLocation(file.Location) (io.ReadCloser, error) {
|
||||
return io.NopCloser(panicReader{}), nil
|
||||
}
|
||||
|
||||
func TestDigestsCataloger_panicBecomesUnknown(t *testing.T) {
|
||||
resolver := panickingContentsResolver{file.NewMockResolverForPaths("cataloger_test.go")}
|
||||
|
||||
results, err := NewCataloger([]crypto.Hash{crypto.SHA256}).Catalog(context.Background(), resolver, file.NewLocation("cataloger_test.go").Coordinates)
|
||||
|
||||
require.Empty(t, results)
|
||||
unknowns, remaining := unknown.ExtractCoordinateErrors(err)
|
||||
require.NoError(t, remaining)
|
||||
require.Len(t, unknowns, 1)
|
||||
require.ErrorContains(t, unknowns[0].Reason, "recovered from panic while digesting file: boom")
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"path"
|
||||
@@ -179,7 +180,8 @@ func toRootRelationships(rootPackage *spdx.Package, packages []*spdx.Package) (o
|
||||
func toRootPackage(s source.Description) *spdx.Package {
|
||||
var prefix string
|
||||
|
||||
name := s.Name
|
||||
// the SPDX package name is mandatory, and a source decoded from another SBOM may not carry one
|
||||
name := cmp.Or(s.Name, imageRepoName(s.Metadata), s.ID, helpers.DocumentName(s))
|
||||
version := s.Version
|
||||
|
||||
var purl *packageurl.PackageURL
|
||||
@@ -210,7 +212,7 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
checksums = append(checksums, *c)
|
||||
purl = &packageurl.PackageURL{
|
||||
Type: "oci",
|
||||
Name: s.Name,
|
||||
Name: name,
|
||||
Version: m.ManifestDigest,
|
||||
Qualifiers: qualifiers,
|
||||
}
|
||||
@@ -240,7 +242,7 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
checksums = append(checksums, *c)
|
||||
purl = &packageurl.PackageURL{
|
||||
Type: "oci",
|
||||
Name: s.Name,
|
||||
Name: name,
|
||||
Version: m.ManifestDigest,
|
||||
Qualifiers: qualifiers,
|
||||
}
|
||||
@@ -275,10 +277,6 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
default:
|
||||
prefix = prefixUnknown
|
||||
purpose = spdxPrimaryPurposeOther
|
||||
|
||||
if name == "" {
|
||||
name = s.ID
|
||||
}
|
||||
}
|
||||
|
||||
p := &spdx.Package{
|
||||
@@ -308,6 +306,25 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
return p
|
||||
}
|
||||
|
||||
// imageRepoName returns the repository name from an image or model user input, the same way the image source names
|
||||
// itself, so a source decoded from another SBOM (which may only carry the user input) gets a name without the tag.
|
||||
func imageRepoName(metadata any) string {
|
||||
var userInput string
|
||||
switch m := metadata.(type) {
|
||||
case source.ImageMetadata:
|
||||
userInput = m.UserInput
|
||||
case source.OCIModelMetadata:
|
||||
userInput = m.UserInput
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
ref, _ := reference.Parse(userInput)
|
||||
if named, ok := ref.(reference.Named); ok {
|
||||
return named.Name()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func toSPDXID(identifiable artifact.Identifiable) spdx.ElementID {
|
||||
id := string(identifiable.ID())
|
||||
if after, ok := strings.CutPrefix(id, "SPDXRef-"); ok {
|
||||
@@ -581,10 +598,13 @@ func toPackageChecksums(p pkg.Package) ([]spdx.Checksum, bool) {
|
||||
})
|
||||
case pkg.OpamPackage:
|
||||
for _, checksum := range meta.Checksums {
|
||||
parts := strings.Split(checksum, "=")
|
||||
algorithm, value, ok := strings.Cut(checksum, "=")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
checksums = append(checksums, spdx.Checksum{
|
||||
Algorithm: spdx.ChecksumAlgorithm(strings.ToUpper(parts[0])),
|
||||
Value: parts[1],
|
||||
Algorithm: spdx.ChecksumAlgorithm(strings.ToUpper(algorithm)),
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,6 +516,20 @@ func Test_toPackageChecksums(t *testing.T) {
|
||||
},
|
||||
filesAnalyzed: false,
|
||||
},
|
||||
{
|
||||
name: "Opam Package with a checksum missing its algorithm separator",
|
||||
pkg: pkg.Package{
|
||||
Name: "test",
|
||||
Version: "1.0.0",
|
||||
Metadata: pkg.OpamPackage{
|
||||
Checksums: []string{"f5f1c0b4", "md5=abc"},
|
||||
},
|
||||
},
|
||||
expected: []spdx.Checksum{
|
||||
{Algorithm: "MD5", Value: "abc"},
|
||||
},
|
||||
filesAnalyzed: false,
|
||||
},
|
||||
{
|
||||
name: "Package with no metadata type",
|
||||
pkg: pkg.Package{
|
||||
@@ -1131,3 +1145,49 @@ func Test_otherLicenses(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toRootPackage_name(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
src source.Description
|
||||
wantName string
|
||||
wantPurl string
|
||||
}{
|
||||
{
|
||||
name: "source name wins",
|
||||
src: source.Description{Name: "named", ID: "id", Metadata: source.ImageMetadata{UserInput: "alpine:3.18"}},
|
||||
wantName: "named",
|
||||
},
|
||||
{
|
||||
// a CycloneDX container component decodes to an image source with only the user input set
|
||||
name: "image user input without a source name",
|
||||
src: source.Description{Metadata: source.ImageMetadata{
|
||||
UserInput: "alpine:3.18",
|
||||
ManifestDigest: "sha256:0e4b2e0d6b2f3c2b1d3bb3a1c8f1c4d9e6a9b1e8c6f4d2b3a1f0e9d8c7b6a5f4",
|
||||
}},
|
||||
wantName: "alpine",
|
||||
wantPurl: "pkg:oci/alpine@sha256%3A0e4b2e0d6b2f3c2b1d3bb3a1c8f1c4d9e6a9b1e8c6f4d2b3a1f0e9d8c7b6a5f4?arch=&tag=3.18",
|
||||
},
|
||||
{
|
||||
name: "id without a source name",
|
||||
src: source.Description{ID: "some-id"},
|
||||
wantName: "some-id",
|
||||
},
|
||||
{
|
||||
name: "nothing to name it by",
|
||||
src: source.Description{Metadata: source.DirectoryMetadata{}},
|
||||
wantName: "unknown",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := toRootPackage(tt.src)
|
||||
assert.Equal(t, tt.wantName, p.PackageName)
|
||||
var purl string
|
||||
if len(p.PackageExternalReferences) > 0 {
|
||||
purl = p.PackageExternalReferences[0].Locator
|
||||
}
|
||||
assert.Equal(t, tt.wantPurl, purl)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ func ToSyftModel(doc *spdx.Document) (*sbom.SBOM, error) {
|
||||
return nil, errors.New("cannot convert SPDX document to Syft model because document is nil")
|
||||
}
|
||||
|
||||
removeNilElements(doc)
|
||||
|
||||
spdxIDMap := make(map[string]any)
|
||||
|
||||
s := &sbom.SBOM{
|
||||
@@ -55,6 +57,28 @@ func ToSyftModel(doc *spdx.Document) (*sbom.SBOM, error) {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// removeNilElements drops null entries from the document's lists. A JSON null (or an element the SPDX version
|
||||
// converter cannot map) decodes to a nil pointer, and the rest of the conversion dereferences these elements.
|
||||
func removeNilElements(doc *spdx.Document) {
|
||||
doc.Packages = withoutNil(doc.Packages)
|
||||
for _, p := range doc.Packages {
|
||||
p.Files = withoutNil(p.Files)
|
||||
p.PackageExternalReferences = withoutNil(p.PackageExternalReferences)
|
||||
}
|
||||
doc.Files = withoutNil(doc.Files)
|
||||
doc.Relationships = withoutNil(doc.Relationships)
|
||||
}
|
||||
|
||||
func withoutNil[T any](items []*T) []*T {
|
||||
var out []*T
|
||||
for _, item := range items {
|
||||
if item != nil {
|
||||
out = append(out, item)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func isDirectory(name string) bool {
|
||||
if name == "." || name == ".." || strings.HasSuffix(name, "/") || !strings.Contains(path.Base(name), ".") {
|
||||
return true
|
||||
@@ -123,12 +147,24 @@ func extractSource(spdxIDMap map[string]any, doc *spdx.Document) source.Descript
|
||||
}
|
||||
|
||||
p := rootPackages[0]
|
||||
rootType := syftRootType(p.PackageSPDXIdentifier)
|
||||
|
||||
switch p.PrimaryPackagePurpose {
|
||||
purpose := p.PrimaryPackagePurpose
|
||||
if purpose == "" {
|
||||
// SPDX 2.1 and 2.2 have no primary package purpose, so fall back to the source type syft puts in the root's ID
|
||||
purpose = syftRootPurpose(rootType)
|
||||
}
|
||||
|
||||
switch purpose {
|
||||
case spdxPrimaryPurposeContainer:
|
||||
src = containerSource(p)
|
||||
case spdxPrimaryPurposeFile:
|
||||
src = fileSource(p)
|
||||
case spdxPrimaryPurposeOther:
|
||||
// syft writes a root package even for a source it knows nothing about, which is not a package in the SBOM
|
||||
if rootType != prefixUnknown {
|
||||
return src
|
||||
}
|
||||
default:
|
||||
return src
|
||||
}
|
||||
@@ -141,6 +177,30 @@ func extractSource(spdxIDMap map[string]any, doc *spdx.Document) source.Descript
|
||||
return src
|
||||
}
|
||||
|
||||
// syftRootType returns the source type syft encodes in a root package ID ("DocumentRoot-<type>-<name>"), or "" for
|
||||
// any other ID.
|
||||
func syftRootType(id spdx.ElementID) string {
|
||||
rest, ok := strings.CutPrefix(string(id), "DocumentRoot-")
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
rootType, _, _ := strings.Cut(rest, "-")
|
||||
return rootType
|
||||
}
|
||||
|
||||
// syftRootPurpose returns the primary package purpose syft writes for a root package of the given source type.
|
||||
func syftRootPurpose(rootType string) string {
|
||||
switch rootType {
|
||||
case prefixImage, prefixOCIModel, prefixSnap:
|
||||
return spdxPrimaryPurposeContainer
|
||||
case prefixDirectory, prefixFile:
|
||||
return spdxPrimaryPurposeFile
|
||||
case prefixUnknown:
|
||||
return spdxPrimaryPurposeOther
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func containerSource(p *spdx.Package) source.Description {
|
||||
id := string(p.PackageSPDXIdentifier)
|
||||
|
||||
@@ -201,7 +261,7 @@ func fileSource(p *spdx.Package) source.Description {
|
||||
}
|
||||
|
||||
supplier := ""
|
||||
if p.PackageSupplier.Supplier != helpers.NOASSERTION {
|
||||
if p.PackageSupplier != nil && p.PackageSupplier.Supplier != helpers.NOASSERTION {
|
||||
supplier = p.PackageSupplier.Supplier
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
syftinternal "github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/format/internal"
|
||||
@@ -61,7 +62,7 @@ func toSyftModel(r io.Reader) (*sbom.SBOM, error) {
|
||||
var errs []error
|
||||
pkgs := pkg.NewCollection()
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner := syftinternal.NewLineScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
@@ -86,6 +87,9 @@ func toSyftModel(r io.Reader) (*sbom.SBOM, error) {
|
||||
p.SetID()
|
||||
pkgs.Add(p)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return &sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
|
||||
@@ -93,7 +93,7 @@ func encode(out map[string]string, value reflect.Value, prefix string, fn FieldN
|
||||
typ := value.Type()
|
||||
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
case reflect.Pointer:
|
||||
if value.IsNil() {
|
||||
return
|
||||
}
|
||||
@@ -158,7 +158,7 @@ func fieldName(f reflect.StructField, prefix string, fn FieldName) (string, bool
|
||||
// Decode based on the given type, applies all values to hydrate a new instance
|
||||
func Decode(typ reflect.Type, values map[string]string, prefix string, fn FieldName) any {
|
||||
isPtr := false
|
||||
for typ.Kind() == reflect.Ptr {
|
||||
for typ.Kind() == reflect.Pointer {
|
||||
typ = typ.Elem()
|
||||
isPtr = true
|
||||
}
|
||||
@@ -188,7 +188,7 @@ func Decode(typ reflect.Type, values map[string]string, prefix string, fn FieldN
|
||||
func DecodeInto(obj any, values map[string]string, prefix string, fn FieldName) {
|
||||
value := reflect.ValueOf(obj)
|
||||
|
||||
for value.Type().Kind() == reflect.Ptr {
|
||||
for value.Type().Kind() == reflect.Pointer {
|
||||
value = value.Elem()
|
||||
}
|
||||
|
||||
@@ -205,7 +205,7 @@ func decode(vals map[string]string, value reflect.Value, prefix string, fn Field
|
||||
|
||||
incoming, valid := vals[prefix]
|
||||
switch typ.Kind() {
|
||||
case reflect.Ptr:
|
||||
case reflect.Pointer:
|
||||
t := typ.Elem()
|
||||
v := value
|
||||
if v.IsNil() {
|
||||
@@ -269,13 +269,13 @@ func decode(vals map[string]string, value reflect.Value, prefix string, fn Field
|
||||
str := fmt.Sprintf("%s:%d", prefix, idx)
|
||||
// create new placeholder and decode values
|
||||
newType := t
|
||||
if t.Kind() == reflect.Ptr {
|
||||
if t.Kind() == reflect.Pointer {
|
||||
newType = t.Elem()
|
||||
}
|
||||
v := reflect.New(newType)
|
||||
if decode(vals, v.Elem(), str, fn) {
|
||||
// append to slice
|
||||
if t.Kind() != reflect.Ptr {
|
||||
if t.Kind() != reflect.Pointer {
|
||||
v = v.Elem()
|
||||
}
|
||||
slice = reflect.Append(slice, v)
|
||||
@@ -304,7 +304,7 @@ func decode(vals map[string]string, value reflect.Value, prefix string, fn Field
|
||||
keyVals[key] = after
|
||||
// create new placeholder and decode key
|
||||
newKeyType := keyType
|
||||
if keyType.Kind() == reflect.Ptr {
|
||||
if keyType.Kind() == reflect.Pointer {
|
||||
newKeyType = keyType.Elem()
|
||||
}
|
||||
k := reflect.New(newKeyType)
|
||||
@@ -312,18 +312,18 @@ func decode(vals map[string]string, value reflect.Value, prefix string, fn Field
|
||||
log.Debugf("unable to decode key for: %s", key)
|
||||
continue
|
||||
}
|
||||
if keyType.Kind() != reflect.Ptr {
|
||||
if keyType.Kind() != reflect.Pointer {
|
||||
k = k.Elem()
|
||||
}
|
||||
|
||||
// create new placeholder and decode value
|
||||
newValueType := valueType
|
||||
if valueType.Kind() == reflect.Ptr {
|
||||
if valueType.Kind() == reflect.Pointer {
|
||||
newValueType = valueType.Elem()
|
||||
}
|
||||
v := reflect.New(newValueType)
|
||||
if decode(vals, v.Elem(), key, fn) {
|
||||
if valueType.Kind() != reflect.Ptr {
|
||||
if valueType.Kind() != reflect.Pointer {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
@@ -367,7 +367,7 @@ func PtrToStruct(ptr any) any {
|
||||
return nil
|
||||
}
|
||||
switch v.Type().Kind() {
|
||||
case reflect.Ptr:
|
||||
case reflect.Pointer:
|
||||
return PtrToStruct(v.Elem().Interface())
|
||||
case reflect.Interface:
|
||||
return PtrToStruct(v.Elem().Interface())
|
||||
|
||||
@@ -9,16 +9,21 @@ func DocumentName(src source.Description) string {
|
||||
return src.Name
|
||||
}
|
||||
|
||||
var name string
|
||||
switch metadata := src.Metadata.(type) {
|
||||
case source.ImageMetadata:
|
||||
return metadata.UserInput
|
||||
name = metadata.UserInput
|
||||
case source.OCIModelMetadata:
|
||||
return metadata.UserInput
|
||||
name = metadata.UserInput
|
||||
case source.DirectoryMetadata:
|
||||
return metadata.Path
|
||||
name = metadata.Path
|
||||
case source.FileMetadata:
|
||||
return metadata.Path
|
||||
default:
|
||||
name = metadata.Path
|
||||
}
|
||||
|
||||
// the SPDX document name is mandatory, and a source read from another SBOM may not have one
|
||||
if name == "" {
|
||||
return "unknown"
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package helpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -73,11 +71,23 @@ func Test_DocumentName(t *testing.T) {
|
||||
},
|
||||
expected: "some/name",
|
||||
},
|
||||
{
|
||||
name: "no name or metadata",
|
||||
srcMetadata: source.Description{},
|
||||
expected: "unknown",
|
||||
},
|
||||
{
|
||||
name: "metadata without a name",
|
||||
srcMetadata: source.Description{
|
||||
Metadata: source.ImageMetadata{},
|
||||
},
|
||||
expected: "unknown",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual := DocumentName(test.srcMetadata)
|
||||
assert.True(t, strings.HasPrefix(actual, test.expected), fmt.Sprintf("actual name %q", actual))
|
||||
assert.Equal(t, test.expected, actual)
|
||||
|
||||
// track each scheme tested (passed or not)
|
||||
tracker.Tested(t, test.srcMetadata.Metadata)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
)
|
||||
|
||||
//nolint:goconst // fixture data: the repeated names and versions are what the golden files assert against, so they read better spelled out than behind constants
|
||||
func DirectoryInput(t testing.TB, dir string) sbom.SBOM {
|
||||
catalog := newDirectoryCatalog()
|
||||
|
||||
@@ -97,6 +98,7 @@ func DirectoryInputWithAuthorField(t testing.TB) sbom.SBOM {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:goconst // fixture data: the repeated names and versions are what the golden files assert against, so they read better spelled out than behind constants
|
||||
func newDirectoryCatalog() *pkg.Collection {
|
||||
catalog := pkg.NewCollection()
|
||||
ctx := context.TODO()
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
)
|
||||
|
||||
//nolint:goconst // fixture data: the repeated names and versions are what the golden files assert against, so they read better spelled out than behind constants
|
||||
func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBOM {
|
||||
t.Helper()
|
||||
catalog := pkg.NewCollection()
|
||||
@@ -95,6 +96,7 @@ func changeToDirectoryWithGoldenFixture(t testing.TB, testImage string) func() {
|
||||
return fn
|
||||
}
|
||||
|
||||
//nolint:goconst // fixture data: the repeated names and versions are what the golden files assert against, so they read better spelled out than behind constants
|
||||
func populateImageCatalog(catalog *pkg.Collection, img *image.Image) {
|
||||
// TODO: this helper function is coupled to the image-simple fixture, which seems like a bad idea
|
||||
_, ref1, _ := img.SquashedTree().File("/somefile-1.txt", filetree.FollowBasenameLinks)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
syftinternal "github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/format/internal"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -61,7 +62,7 @@ func toSyftModel(r io.Reader) (*sbom.SBOM, error) {
|
||||
var errs []error
|
||||
pkgs := pkg.NewCollection()
|
||||
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner := syftinternal.NewLineScanner(r)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
|
||||
@@ -84,6 +85,9 @@ func toSyftModel(r io.Reader) (*sbom.SBOM, error) {
|
||||
p.SetID()
|
||||
pkgs.Add(p)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return &sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -183,3 +184,37 @@ func TestDecoder_Identify(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoder_DecodeNullElements(t *testing.T) {
|
||||
// a JSON null in these lists (or an SPDX 2.1 package the version converter cannot map) decodes to a nil pointer.
|
||||
// a null entry directly in "packages" is not covered: for SPDX 2.2 and 2.3 it panics inside spdx/tools-golang
|
||||
for _, doc := range []string{
|
||||
`{"spdxVersion":"SPDX-2.1","packages":[{}]}`,
|
||||
`{"spdxVersion":"SPDX-2.3","files":[null]}`,
|
||||
`{"spdxVersion":"SPDX-2.3","packages":[{"SPDXID":"SPDXRef-a","name":"a","files":[null]}]}`,
|
||||
`{"spdxVersion":"SPDX-2.3","packages":[{"SPDXID":"SPDXRef-a","name":"a","externalRefs":[null]}]}`,
|
||||
} {
|
||||
t.Run(doc, func(t *testing.T) {
|
||||
assert.NotPanics(t, func() {
|
||||
_, _, _, _ = NewFormatDecoder().Decode(strings.NewReader(doc))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoder_DecodeFileRootWithoutSupplier(t *testing.T) {
|
||||
// the package supplier is optional in SPDX, including on the package the document describes
|
||||
f, err := os.Open("testdata/spdx/file-root-without-supplier.spdx.json")
|
||||
require.NoError(t, err)
|
||||
defer f.Close()
|
||||
|
||||
s, _, _, err := NewFormatDecoder().Decode(f)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "app.tar.gz", s.Source.Name)
|
||||
|
||||
var names []string
|
||||
for _, p := range s.Artifacts.Packages.Sorted() {
|
||||
names = append(names, p.Name)
|
||||
}
|
||||
assert.Equal(t, []string{"requests"}, names)
|
||||
}
|
||||
|
||||
@@ -236,23 +236,6 @@ func TestSupportedVersions(t *testing.T) {
|
||||
subject := testutil.DirectoryInput(t, t.TempDir())
|
||||
dec := NewFormatDecoder()
|
||||
|
||||
relationshipOffsetPerVersion := map[string]int{
|
||||
// the package representing the source gets a relationship from the source package to all other packages found
|
||||
// these relationships cannot be removed until the primaryPackagePurpose info is available in 2.3
|
||||
"2.1": 2,
|
||||
"2.2": 2,
|
||||
// the source-to-package relationships can be removed since the primaryPackagePurpose info is available in 2.3
|
||||
"2.3": 0,
|
||||
"3.0": 0,
|
||||
}
|
||||
|
||||
pkgCountOffsetPerVersion := map[string]int{
|
||||
"2.1": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.2": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.3": 0, // the source package can be removed since the primaryPackagePurpose info is available
|
||||
"3.0": 0,
|
||||
}
|
||||
|
||||
for _, enc := range encs {
|
||||
t.Run(enc.Version(), func(t *testing.T) {
|
||||
require.Contains(t, versions, enc.Version())
|
||||
@@ -274,13 +257,9 @@ func TestSupportedVersions(t *testing.T) {
|
||||
|
||||
require.NotEmpty(t, s.Artifacts.Packages.PackageCount())
|
||||
|
||||
offset := relationshipOffsetPerVersion[enc.Version()]
|
||||
assert.Equal(t, len(subject.Relationships), len(s.Relationships), "mismatched relationship count")
|
||||
|
||||
assert.Equal(t, len(subject.Relationships)+offset, len(s.Relationships), "mismatched relationship count")
|
||||
|
||||
offset = pkgCountOffsetPerVersion[enc.Version()]
|
||||
|
||||
if !assert.Equal(t, subject.Artifacts.Packages.PackageCount()+offset, s.Artifacts.Packages.PackageCount(), "mismatched package count") {
|
||||
if !assert.Equal(t, subject.Artifacts.Packages.PackageCount(), s.Artifacts.Packages.PackageCount(), "mismatched package count") {
|
||||
t.Logf("expected: %d", subject.Artifacts.Packages.PackageCount())
|
||||
for _, p := range subject.Artifacts.Packages.Sorted() {
|
||||
t.Logf(" - %s", p.String())
|
||||
|
||||
+5
-5
@@ -47,13 +47,13 @@
|
||||
"copyrightText": "NOASSERTION"
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"SPDXID": "SPDXRef-DocumentRoot-Unknown-unknown",
|
||||
"copyrightText": "NOASSERTION",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"filesAnalyzed": false,
|
||||
"licenseConcluded": "NOASSERTION",
|
||||
"licenseDeclared": "NOASSERTION",
|
||||
"name": "",
|
||||
"name": "unknown",
|
||||
"supplier": "NOASSERTION"
|
||||
}
|
||||
],
|
||||
@@ -82,18 +82,18 @@
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-unknown",
|
||||
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-false-7d37ba9d2f7c574b",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"spdxElementId": "SPDXRef-DocumentRoot-Unknown-unknown",
|
||||
"relatedSpdxElement": "SPDXRef-Package-files-analyzed-true-035066c2086b8bb4",
|
||||
"relationshipType": "CONTAINS"
|
||||
},
|
||||
{
|
||||
"spdxElementId": "SPDXRef-DOCUMENT",
|
||||
"relatedSpdxElement": "SPDXRef-DocumentRoot-Unknown-",
|
||||
"relatedSpdxElement": "SPDXRef-DocumentRoot-Unknown-unknown",
|
||||
"relationshipType": "DESCRIBES"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"spdxVersion": "SPDX-2.3",
|
||||
"dataLicense": "CC0-1.0",
|
||||
"SPDXID": "SPDXRef-DOCUMENT",
|
||||
"name": "app.tar.gz",
|
||||
"documentNamespace": "https://example.com/spdx/app-1.0",
|
||||
"creationInfo": { "created": "2026-09-24T00:00:00Z", "creators": ["Tool: example-1.0"] },
|
||||
"packages": [
|
||||
{
|
||||
"SPDXID": "SPDXRef-app",
|
||||
"name": "app.tar.gz",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"filesAnalyzed": false,
|
||||
"primaryPackagePurpose": "FILE"
|
||||
},
|
||||
{
|
||||
"SPDXID": "SPDXRef-requests",
|
||||
"name": "requests",
|
||||
"versionInfo": "2.31.0",
|
||||
"downloadLocation": "NOASSERTION",
|
||||
"filesAnalyzed": false,
|
||||
"externalRefs": [{ "referenceCategory": "PACKAGE-MANAGER", "referenceType": "purl", "referenceLocator": "pkg:pypi/requests@2.31.0" }]
|
||||
}
|
||||
],
|
||||
"relationships": [
|
||||
{ "spdxElementId": "SPDXRef-DOCUMENT", "relationshipType": "DESCRIBES", "relatedSpdxElement": "SPDXRef-app" },
|
||||
{ "spdxElementId": "SPDXRef-app", "relationshipType": "CONTAINS", "relatedSpdxElement": "SPDXRef-requests" }
|
||||
]
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package spdxtagvalue
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -143,21 +144,6 @@ func TestSupportedVersions(t *testing.T) {
|
||||
subject := testutil.DirectoryInput(t, t.TempDir())
|
||||
dec := NewFormatDecoder()
|
||||
|
||||
relationshipOffsetPerVersion := map[string]int{
|
||||
// the package representing the source gets a relationship from the source package to all other packages found
|
||||
// these relationships cannot be removed until the primaryPackagePurpose info is available in 2.3
|
||||
"2.1": 2,
|
||||
"2.2": 2,
|
||||
// the source-to-package relationships can be removed since the primaryPackagePurpose info is available in 2.3
|
||||
"2.3": 0,
|
||||
}
|
||||
|
||||
pkgCountOffsetPerVersion := map[string]int{
|
||||
"2.1": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.2": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.3": 0, // the source package can be removed since the primaryPackagePurpose info is available
|
||||
}
|
||||
|
||||
for _, enc := range encs {
|
||||
t.Run(enc.Version(), func(t *testing.T) {
|
||||
require.Contains(t, versions, enc.Version())
|
||||
@@ -178,13 +164,9 @@ func TestSupportedVersions(t *testing.T) {
|
||||
|
||||
require.NotEmpty(t, s.Artifacts.Packages.PackageCount())
|
||||
|
||||
offset := relationshipOffsetPerVersion[enc.Version()]
|
||||
assert.Equal(t, len(subject.Relationships), len(s.Relationships), "mismatched relationship count")
|
||||
|
||||
assert.Equal(t, len(subject.Relationships)+offset, len(s.Relationships), "mismatched relationship count")
|
||||
|
||||
offset = pkgCountOffsetPerVersion[enc.Version()]
|
||||
|
||||
if !assert.Equal(t, subject.Artifacts.Packages.PackageCount()+offset, s.Artifacts.Packages.PackageCount(), "mismatched package count") {
|
||||
if !assert.Equal(t, subject.Artifacts.Packages.PackageCount(), s.Artifacts.Packages.PackageCount(), "mismatched package count") {
|
||||
t.Logf("expected: %d", subject.Artifacts.Packages.PackageCount())
|
||||
for _, p := range subject.Artifacts.Packages.Sorted() {
|
||||
t.Logf(" - %s", p.String())
|
||||
@@ -209,3 +191,39 @@ func defaultFormatEncoders() []sbom.FormatEncoder {
|
||||
}
|
||||
return encs
|
||||
}
|
||||
|
||||
func TestEncodeDecodeSourceWithoutName(t *testing.T) {
|
||||
// a source read from another SBOM (a purl list, a CycloneDX document without metadata.component) may have no name,
|
||||
// but the SPDX package name is mandatory and the tag-value decoder needs it to start the root package section
|
||||
p := pkg.Package{Name: "left-pad", Version: "1.3.0", Type: pkg.NpmPkg}
|
||||
p.SetID()
|
||||
|
||||
for _, src := range []source.Description{
|
||||
{},
|
||||
{Metadata: source.FileMetadata{}},
|
||||
{Metadata: source.DirectoryMetadata{}},
|
||||
{Metadata: source.ImageMetadata{}},
|
||||
{Metadata: source.OCIModelMetadata{}},
|
||||
{Metadata: source.SnapMetadata{}},
|
||||
} {
|
||||
for _, enc := range defaultFormatEncoders() {
|
||||
t.Run(fmt.Sprintf("%T/%s", src.Metadata, enc.Version()), func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, enc.Encode(&buf, sbom.SBOM{
|
||||
Source: src,
|
||||
Artifacts: sbom.Artifacts{Packages: pkg.NewCollection(p)},
|
||||
}))
|
||||
|
||||
s, _, _, err := NewFormatDecoder().Decode(bytes.NewReader(buf.Bytes()))
|
||||
require.NoError(t, err, "encoded document:\n%s", buf.String())
|
||||
|
||||
var names []string
|
||||
for _, decoded := range s.Artifacts.Packages.Sorted() {
|
||||
names = append(names, decoded.Name)
|
||||
}
|
||||
// the root package describes the source, so it must not come back as a package of the SBOM
|
||||
assert.Equal(t, []string{"left-pad"}, names)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/format/syftjson"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// TestEncodersHandleUntypedMetadataValues pushes awkward values through the untyped metadata fields
|
||||
// allowlisted in internal/packagemetadata (see untypedFieldAllowlist) and asserts that SetID and every
|
||||
// encoder neither panic nor fail. A new allowlist entry should get a case here too.
|
||||
func TestEncodersHandleUntypedMetadataValues(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
value any
|
||||
// encoding/json rejects non-finite floats, so syft-json returns an error for them. Producers
|
||||
// have to replace these before they reach the model (the GGUF cataloger does); the case stays
|
||||
// here so the expectation flips visibly if syft-json ever learns to encode them.
|
||||
syftJSONRejects bool
|
||||
}{
|
||||
{name: "NaN", value: math.NaN(), syftJSONRejects: true},
|
||||
{name: "+Inf float32", value: float32(math.Inf(1)), syftJSONRejects: true},
|
||||
{name: "-Inf", value: math.Inf(-1), syftJSONRejects: true},
|
||||
{name: "NaN in array", value: []any{1.0, math.NaN()}, syftJSONRejects: true},
|
||||
{name: "invalid utf-8", value: "\xff\xfe"},
|
||||
{name: "NUL and control characters", value: "a\x00b\x1b"},
|
||||
{name: "max uint64", value: uint64(math.MaxUint64)},
|
||||
{name: "deep array", value: nested(2000, func(v any) any { return []any{v} })},
|
||||
{name: "deep map", value: nested(2000, func(v any) any { return map[string]any{"k": v} })},
|
||||
{name: "large array", value: make([]any, 100_000)},
|
||||
}
|
||||
|
||||
for _, tt := range cases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
p := pkg.Package{
|
||||
Name: "model",
|
||||
Version: "1.0",
|
||||
Type: pkg.ModelPkg,
|
||||
Metadata: pkg.GGUFFileHeader{
|
||||
RemainingKeyValues: map[string]any{"general.value": tt.value},
|
||||
},
|
||||
}
|
||||
if !noPanic(t, "SetID", p.SetID) {
|
||||
return
|
||||
}
|
||||
|
||||
s := sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{Packages: pkg.NewCollection(p)},
|
||||
Source: source.Description{Metadata: source.DirectoryMetadata{Path: "/models"}},
|
||||
}
|
||||
for _, enc := range Encoders() {
|
||||
id := fmt.Sprintf("%s@%s", enc.ID(), enc.Version())
|
||||
var buf bytes.Buffer
|
||||
noPanic(t, id, func() {
|
||||
err := enc.Encode(&buf, s)
|
||||
switch {
|
||||
case tt.syftJSONRejects && enc.ID() == syftjson.ID:
|
||||
if err == nil {
|
||||
t.Errorf("%s: expected an encode error for a non-finite float", id)
|
||||
}
|
||||
case err != nil:
|
||||
t.Errorf("%s: encode failed: %v", id, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func nested(depth int, wrap func(any) any) any {
|
||||
var v any = "leaf"
|
||||
for range depth {
|
||||
v = wrap(v)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func noPanic(t *testing.T, what string, fn func()) (ok bool) {
|
||||
t.Helper()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
t.Errorf("%s: panic: %v", what, r)
|
||||
ok = false
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
return true
|
||||
}
|
||||
@@ -7,11 +7,12 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
)
|
||||
|
||||
// section describes one section to place in a fixture. The compression headers are deliberately
|
||||
@@ -543,13 +544,10 @@ func TestNewFile_BombDoesNotAllocate(t *testing.T) {
|
||||
{name: ".strtab", typ: elf.SHT_STRTAB},
|
||||
}, buildOpts{})
|
||||
|
||||
var before, after runtime.MemStats
|
||||
runtime.GC()
|
||||
runtime.ReadMemStats(&before)
|
||||
|
||||
_, err := NewFile(bytes.NewReader(data))
|
||||
|
||||
runtime.ReadMemStats(&after)
|
||||
var err error
|
||||
allocated := testutils.MeasureAlloc(t, func() {
|
||||
_, err = NewFile(bytes.NewReader(data))
|
||||
})
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "over the")
|
||||
@@ -557,7 +555,7 @@ func TestNewFile_BombDoesNotAllocate(t *testing.T) {
|
||||
// the guard has to reject before the allocation, not after it. The margin is loose on purpose: this
|
||||
// is asserting "nothing near the declared size was allocated", not a precise budget.
|
||||
const margin = 32 * 1024 * 1024
|
||||
assert.Less(t, after.TotalAlloc-before.TotalAlloc, uint64(margin),
|
||||
assert.Less(t, allocated, uint64(margin),
|
||||
"parsing allocated far more than the input warrants")
|
||||
}
|
||||
|
||||
|
||||
@@ -216,5 +216,8 @@ func (r ChrootContext) ToChrootPath(nativePath string) string {
|
||||
responsePath = strings.TrimPrefix(responsePath, prefix)
|
||||
}
|
||||
|
||||
return responsePath
|
||||
// chroot paths are posix regardless of host (these end up as location paths in the SBOM). This uses
|
||||
// filepath (not path) since responsePath is native here (FromPosix above, then trimmed of a native prefix),
|
||||
// and only filepath.ToSlash knows the host separator to convert.
|
||||
return filepath.ToSlash(responsePath)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package fileresolver
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -10,6 +11,9 @@ import (
|
||||
)
|
||||
|
||||
func Test_ChrootContext_RequestResponse(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("fixture relies on symlinks, which git does not check out as links on windows by default")
|
||||
}
|
||||
// /
|
||||
// somewhere/
|
||||
// outside.txt
|
||||
@@ -563,6 +567,9 @@ func TestNewChrootContext_BaseValidation(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestToNativeGlob(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("chroot context and expectations are posix paths")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
chrootContext ChrootContext
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
@@ -18,6 +17,7 @@ import (
|
||||
"github.com/wagoodman/go-progress"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/syft/internal/windows"
|
||||
)
|
||||
|
||||
type indexerMock struct {
|
||||
@@ -138,6 +138,9 @@ func TestDirectoryIndexer_handleFileAccessErr(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_IncludeRootPathInIndex(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("indexes the posix filesystem root")
|
||||
}
|
||||
filterFn := func(_, path string, _ os.FileInfo, _ error) error {
|
||||
if path != "/" {
|
||||
return fs.SkipDir
|
||||
@@ -206,11 +209,7 @@ func TestDirectoryIndexer_index(t *testing.T) {
|
||||
info, err := os.Stat(test.path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// note: the index uses absolute paths, so assertions MUST keep this in mind
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
p := file.Path(path.Join(cwd, test.path))
|
||||
p := indexedPath(t, test.path)
|
||||
assert.Equal(t, true, tree.HasPath(p))
|
||||
exists, ref, err := tree.File(p)
|
||||
assert.Equal(t, true, exists)
|
||||
@@ -226,6 +225,9 @@ func TestDirectoryIndexer_index(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_index_for_AncestorSymlinks(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("fixture relies on symlinks, which git does not check out as links on windows by default")
|
||||
}
|
||||
// note: this test is testing the effects from NewFromDirectory, indexTree, and addPathToIndex
|
||||
_, filename, _, ok := runtime.Caller(0)
|
||||
require.True(t, ok)
|
||||
@@ -256,11 +258,7 @@ func TestDirectoryIndexer_index_for_AncestorSymlinks(t *testing.T) {
|
||||
info, err := os.Stat(test.path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// note: the index uses absolute paths, so assertions MUST keep this in mind
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
p := file.Path(path.Join(cwd, test.path))
|
||||
p := indexedPath(t, test.path)
|
||||
assert.Equal(t, true, tree.HasPath(p))
|
||||
exists, ref, err := tree.File(p)
|
||||
assert.Equal(t, true, exists)
|
||||
@@ -287,6 +285,9 @@ func TestDirectoryIndexer_index_survive_badSymlink(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_index_survive_inaccessibleSymlinkTarget(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("relies on symlinks and posix permissions")
|
||||
}
|
||||
// a symlink that resolves into a directory we don't have permission to traverse should be skipped
|
||||
// with a warning like any other inaccessible path, not abort the entire scan (see #3286).
|
||||
if os.Geteuid() == 0 {
|
||||
@@ -323,6 +324,9 @@ func TestDirectoryIndexer_index_survive_inaccessibleSymlinkTarget(t *testing.T)
|
||||
}
|
||||
|
||||
func TestDirectoryIndexer_SkipsAlreadyVisitedLinkDestinations(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("fixture relies on symlinks, which git does not check out as links on windows by default")
|
||||
}
|
||||
var observedPaths []string
|
||||
pathObserver := func(_, p string, _ os.FileInfo, _ error) error {
|
||||
fields := strings.Split(p, "testdata/symlinks-prune-indexing")
|
||||
@@ -430,6 +434,11 @@ func TestDirectoryIndexer_IndexesAllTypes(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_allContainedPaths(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// ponytail: production passes native windows paths here, which these posix cases don't represent.
|
||||
// Ancestor indexing for a root reached through a symlink is likely broken on windows; fix separately.
|
||||
t.Skip("cases are posix paths")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -485,6 +494,9 @@ func Test_allContainedPaths(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_relativePath(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("exercises a posix-only test helper")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
basePath string
|
||||
@@ -548,3 +560,17 @@ func relativePath(basePath, givenPath string) string {
|
||||
|
||||
return relPath
|
||||
}
|
||||
|
||||
// indexedPath returns how the given cwd-relative path is keyed in the index (absolute and always posix)
|
||||
func indexedPath(t *testing.T, rel string) file.Path {
|
||||
t.Helper()
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
// filepath (not path): cwd is a native host path, so it must be joined with the host separator before
|
||||
// being converted to the posix form the index is keyed on
|
||||
p := filepath.Join(cwd, rel)
|
||||
if windows.HostRunningOnWindows() {
|
||||
p = windows.ToPosix(p)
|
||||
}
|
||||
return file.Path(p)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package fileresolver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
)
|
||||
|
||||
// a location handed out by the resolver should be resolvable again by its own path. This is what the file
|
||||
// digest cataloger relies on, and it silently found nothing on windows (see #5325).
|
||||
func TestDirectory_LocationPathsRoundTrip(t *testing.T) {
|
||||
// resolve symlinks up front (e.g. /var -> /private/var on macOS) so the cwd prefix matches the indexed paths
|
||||
root, err := filepath.EvalSymlinks(t.TempDir())
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(root, "sub", "dir"), 0o755))
|
||||
for _, p := range []string{"top.txt", filepath.Join("sub", "mid.txt"), filepath.Join("sub", "dir", "deep.txt")} {
|
||||
require.NoError(t, os.WriteFile(filepath.Join(root, p), []byte(p), 0o600))
|
||||
}
|
||||
|
||||
// mirror `syft dir:.` from within the scanned directory
|
||||
t.Chdir(root)
|
||||
|
||||
resolver, err := NewFromDirectory(".", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
var regular int
|
||||
for loc := range resolver.AllLocations(context.Background()) {
|
||||
md, err := resolver.FileMetadataByLocation(loc)
|
||||
require.NoError(t, err)
|
||||
if md.Type != stereoscopeFile.TypeRegular {
|
||||
continue
|
||||
}
|
||||
regular++
|
||||
|
||||
assert.NotContains(t, loc.RealPath, `\`, "location paths should be posix")
|
||||
|
||||
found, err := resolver.FilesByPath(loc.RealPath)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, found, 1, "unable to resolve %q by its own path", loc.RealPath)
|
||||
}
|
||||
assert.Equal(t, 3, regular)
|
||||
}
|
||||
@@ -1,126 +0,0 @@
|
||||
package fileresolver
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_windowsToPosix(t *testing.T) {
|
||||
type args struct {
|
||||
windowsPath string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantPosixPath string
|
||||
}{
|
||||
{
|
||||
name: "basic case",
|
||||
args: args{
|
||||
windowsPath: `C:\some\windows\place`,
|
||||
},
|
||||
wantPosixPath: "/c/some/windows/place",
|
||||
},
|
||||
{
|
||||
name: "escaped case",
|
||||
args: args{
|
||||
windowsPath: `C:\\some\\windows\\place`,
|
||||
},
|
||||
wantPosixPath: "/c/some/windows/place",
|
||||
},
|
||||
{
|
||||
name: "forward slash",
|
||||
args: args{
|
||||
windowsPath: `C:/foo/bar`,
|
||||
},
|
||||
wantPosixPath: "/c/foo/bar",
|
||||
},
|
||||
{
|
||||
name: "mix slash",
|
||||
args: args{
|
||||
windowsPath: `C:\foo/bar\`,
|
||||
},
|
||||
wantPosixPath: "/c/foo/bar",
|
||||
},
|
||||
{
|
||||
name: "case sensitive case",
|
||||
args: args{
|
||||
windowsPath: `C:\Foo/bAr\`,
|
||||
},
|
||||
wantPosixPath: "/c/Foo/bAr",
|
||||
},
|
||||
{
|
||||
name: "special char case",
|
||||
args: args{
|
||||
windowsPath: `C:\ふー\バー`,
|
||||
},
|
||||
wantPosixPath: "/c/ふー/バー",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotPosixPath := windowsToPosix(tt.args.windowsPath); gotPosixPath != tt.wantPosixPath {
|
||||
t.Errorf("windowsToPosix() = %v, want %v", gotPosixPath, tt.wantPosixPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_posixToWindows(t *testing.T) {
|
||||
type args struct {
|
||||
posixPath string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantWindowsPath string
|
||||
}{
|
||||
{
|
||||
name: "basic case",
|
||||
args: args{
|
||||
posixPath: "/c/some/windows/place",
|
||||
},
|
||||
wantWindowsPath: `C:\some\windows\place`,
|
||||
},
|
||||
{
|
||||
name: "escaped case",
|
||||
args: args{
|
||||
posixPath: "/c/some/windows/place",
|
||||
},
|
||||
wantWindowsPath: `C:\\some\\windows\\place`,
|
||||
},
|
||||
{
|
||||
name: "forward slash",
|
||||
args: args{
|
||||
posixPath: "/c/foo/bar",
|
||||
},
|
||||
wantWindowsPath: `C:/foo/bar`,
|
||||
},
|
||||
{
|
||||
name: "mix slash",
|
||||
args: args{
|
||||
posixPath: "/c/foo/bar",
|
||||
},
|
||||
wantWindowsPath: `C:\foo/bar\`,
|
||||
},
|
||||
{
|
||||
name: "case sensitive case",
|
||||
args: args{
|
||||
posixPath: "/c/Foo/bAr",
|
||||
},
|
||||
wantWindowsPath: `C:\Foo/bAr\`,
|
||||
},
|
||||
{
|
||||
name: "special char case",
|
||||
args: args{
|
||||
posixPath: "/c/ふー/バー",
|
||||
},
|
||||
wantWindowsPath: `C:\ふー\バー`,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotWindowsPath := posixToWindows(tt.args.posixPath); gotWindowsPath != tt.wantWindowsPath {
|
||||
t.Errorf("posixToWindows() = %v, want %v", gotWindowsPath, tt.wantWindowsPath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -29,11 +29,15 @@ func newFileIndexer(path, base string, visitors ...PathIndexVisitor) *fileIndexe
|
||||
base: base,
|
||||
tree: filetree.New(),
|
||||
index: filetree.NewIndex(),
|
||||
// Unlike the directory indexer there is no skipPathsByMountTypeAndName here: it exists
|
||||
// to keep a directory walk out of /proc, /sys and similar, and to do so it reads the
|
||||
// whole mount table on construction. A file source indexes exactly one file and its
|
||||
// parent, so there is nothing to walk into, and the mount table read is paid for
|
||||
// every file source with nothing in return.
|
||||
pathIndexVisitors: append(
|
||||
[]PathIndexVisitor{
|
||||
requireFileInfo,
|
||||
disallowByFileType,
|
||||
skipPathsByMountTypeAndName(path),
|
||||
},
|
||||
visitors...,
|
||||
),
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
package fileresolver
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/moby/sys/mountinfo"
|
||||
)
|
||||
|
||||
// BenchmarkNewFromFile is the cost of building a resolver for a single-file source, which is
|
||||
// paid once per file by callers that catalog files individually. The default case is
|
||||
// NewFromFile as shipped. The with-mount-skipper case attaches skipPathsByMountTypeAndName
|
||||
// the way the file indexer used to, so the difference is what reading the mount table for
|
||||
// every file source cost. That cost scales with the mount table, so its size is reported as
|
||||
// the mounts metric: expect a bigger gap on a container host than on a laptop.
|
||||
//
|
||||
// go test ./syft/internal/fileresolver/ -run '^$' -bench NewFromFile -benchmem -count 6 | tee bench.txt
|
||||
// benchstat -col /variant bench.txt
|
||||
func BenchmarkNewFromFile(b *testing.B) {
|
||||
path := filepath.Join(b.TempDir(), "file.txt")
|
||||
if err := os.WriteFile(path, []byte("hello\n"), 0o644); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
mounts, err := mountinfo.GetMounts(nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
||||
b.Run("variant=default", func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
if _, err := NewFromFile(path); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
b.ReportMetric(float64(len(mounts)), "mounts")
|
||||
})
|
||||
|
||||
b.Run("variant=with-mount-skipper", func(b *testing.B) {
|
||||
for b.Loop() {
|
||||
r, err := newFromFileWithoutIndex(path, skipPathsByMountTypeAndName(path))
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
if err := r.buildIndex(); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
b.ReportMetric(float64(len(mounts)), "mounts")
|
||||
})
|
||||
}
|
||||
@@ -3,13 +3,11 @@ package fileresolver
|
||||
import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/file"
|
||||
)
|
||||
|
||||
// - Verify that both the parent and the path are indexed
|
||||
@@ -38,11 +36,7 @@ func Test_index(t *testing.T) {
|
||||
info, err := os.Stat(test.path)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// note: the index uses absolute paths, so assertions MUST keep this in mind
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
|
||||
p := file.Path(path.Join(cwd, test.path))
|
||||
p := indexedPath(t, test.path)
|
||||
assert.Equal(t, true, tree.HasPath(p))
|
||||
exists, ref, err := tree.File(p)
|
||||
assert.Equal(t, true, exists)
|
||||
@@ -71,7 +65,9 @@ func Test_ignoresPathIfFiltered(t *testing.T) {
|
||||
testPath := "testdata/system_paths/target/home/place"
|
||||
cwd, cwdErr := os.Getwd()
|
||||
require.NoError(t, cwdErr)
|
||||
ignorePath := path.Join(cwd, testPath)
|
||||
// filepath (not path): filters see the native absolute path (before posix normalization), so the
|
||||
// expected value must be joined with the host separator to match on windows
|
||||
ignorePath := filepath.Join(cwd, testPath)
|
||||
filterFn := func(_, path string, _ os.FileInfo, _ error) error {
|
||||
if path == ignorePath {
|
||||
return ErrSkipPath
|
||||
@@ -91,7 +87,8 @@ func Test_ignoresPathIfParentFiltered(t *testing.T) {
|
||||
|
||||
cwd, cwdErr := os.Getwd()
|
||||
require.NoError(t, cwdErr)
|
||||
ignorePath := path.Join(cwd, parentPath)
|
||||
// filepath (not path): see Test_ignoresPathIfFiltered
|
||||
ignorePath := filepath.Join(cwd, parentPath)
|
||||
filterFn := func(_, path string, _ os.FileInfo, _ error) error {
|
||||
if path == ignorePath {
|
||||
return fs.SkipDir
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/filetree"
|
||||
@@ -34,8 +35,17 @@ func nativeOSFileOpener(ref stereoscopeFile.Reference) (io.ReadCloser, error) {
|
||||
return stereoscopeFile.NewLazyReadCloser(filePath), nil
|
||||
}
|
||||
|
||||
// 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) {
|
||||
return r.Chroot.ToNativePath(userPath)
|
||||
nativePath, err := r.Chroot.ToNativePath(userPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if windows.HostRunningOnWindows() {
|
||||
return windows.ToPosix(nativePath), nil
|
||||
}
|
||||
return nativePath, nil
|
||||
}
|
||||
|
||||
// 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.
|
||||
@@ -85,10 +95,6 @@ func (r FiletreeResolver) FilesByPath(userPaths ...string) ([]file.Location, err
|
||||
continue
|
||||
}
|
||||
|
||||
if windows.HostRunningOnWindows() {
|
||||
userStrPath = windows.ToPosix(userStrPath)
|
||||
}
|
||||
|
||||
if ref.HasReference() {
|
||||
references = append(references,
|
||||
file.NewVirtualLocationFromDirectory(
|
||||
@@ -103,8 +109,19 @@ func (r FiletreeResolver) FilesByPath(userPaths ...string) ([]file.Location, err
|
||||
return references, nil
|
||||
}
|
||||
|
||||
// requestGlob is the glob equivalent of requestPath.
|
||||
func (r FiletreeResolver) requestGlob(pattern string) (string, error) {
|
||||
return r.Chroot.ToNativeGlob(pattern)
|
||||
glob, err := r.Chroot.ToNativeGlob(pattern)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// only a glob that was anchored to the root comes back native. Globs that start with a wildcard
|
||||
// (e.g. **/foo) are returned untouched and are already posix, and ToPosix would root them at "/".
|
||||
// This uses filepath (not path) since we are asking whether this is a native absolute path.
|
||||
if windows.HostRunningOnWindows() && filepath.IsAbs(glob) {
|
||||
return windows.ToPosix(glob), nil
|
||||
}
|
||||
return glob, nil
|
||||
}
|
||||
|
||||
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
|
||||
|
||||
@@ -22,6 +22,15 @@ func SkipWhitespace(data []byte, i *int) {
|
||||
}
|
||||
}
|
||||
|
||||
// MaxDepth bounds nesting in the recursive hand-written parsers. Real files nest a handful of levels; without a
|
||||
// bound, deep input overflows the goroutine stack, which is fatal rather than a recoverable panic.
|
||||
const MaxDepth = 1000
|
||||
|
||||
// errorContext is how many bytes of each echoed line PrintError keeps around the error column, so an error in a
|
||||
// huge single-line input stays small (and does not copy the file into the SBOM as an unknown).
|
||||
const errorContext = 80
|
||||
|
||||
// PrintError describes where in data offset i is, echoing up to two lines of context clipped around the column.
|
||||
func PrintError(data []byte, i int) string {
|
||||
line := 1
|
||||
char := 1
|
||||
@@ -57,15 +66,23 @@ func PrintError(data []byte, i int) string {
|
||||
|
||||
sep := ": "
|
||||
|
||||
lines := ""
|
||||
if len(prev) > 1 {
|
||||
lines += fmt.Sprintf("%s%s%s\n", l1, sep, prev[len(prev)-2])
|
||||
}
|
||||
if len(prev) > 0 {
|
||||
lines += fmt.Sprintf("%s%s%s\n", l2, sep, prev[len(prev)-1])
|
||||
start := max(0, char-1-errorContext/2)
|
||||
clip := func(s string) string {
|
||||
if start >= len(s) {
|
||||
return ""
|
||||
}
|
||||
return s[start:min(len(s), start+errorContext)]
|
||||
}
|
||||
|
||||
pointer := strings.Repeat(" ", len(l2)+len(sep)+char-1) + "^"
|
||||
lines := ""
|
||||
if len(prev) > 1 {
|
||||
lines += fmt.Sprintf("%s%s%s\n", l1, sep, clip(prev[len(prev)-2]))
|
||||
}
|
||||
if len(prev) > 0 {
|
||||
lines += fmt.Sprintf("%s%s%s\n", l2, sep, clip(prev[len(prev)-1]))
|
||||
}
|
||||
|
||||
pointer := strings.Repeat(" ", len(l2)+len(sep)+char-1-start) + "^"
|
||||
|
||||
return fmt.Sprintf("line: %v, char: %v\n%s%s", line, char, lines, pointer)
|
||||
}
|
||||
|
||||
@@ -35,7 +35,15 @@ func ToPosix(windowsPath string) (posixPath string) {
|
||||
func FromPosix(posixPath string) (windowsPath string) {
|
||||
// decode the volume (e.g. /c/<path> --> C:\\) - There should always be a volume name.
|
||||
// The volume may be a UNC path (e.g. /\\localhost\C$\ --> \\localhost\C$\)
|
||||
if !strings.HasPrefix(posixPath, "/") {
|
||||
// not a volume-encoded posix path (e.g. relative or already native), so there is nothing to decode
|
||||
return posixPath
|
||||
}
|
||||
pathFields := strings.Split(posixPath, "/")
|
||||
if len(pathFields) < 2 {
|
||||
// no separator means no volume to decode
|
||||
return filepath.Clean(posixPath)
|
||||
}
|
||||
rootPath := strings.ToUpper(pathFields[1])
|
||||
volumeName := AppendRootTerminator(rootPath)
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package windows
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestToPosix(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
`C:\some\windows\place`: "/c/some/windows/place",
|
||||
`C:\\some\\windows\\place`: "/c/some/windows/place",
|
||||
`C:/foo/bar`: "/c/foo/bar",
|
||||
`C:\foo/bar\`: "/c/foo/bar",
|
||||
`C:\Foo/bAr\`: "/c/Foo/bAr",
|
||||
`C:\ふー\バー`: "/c/ふー/バー",
|
||||
}
|
||||
for in, want := range tests {
|
||||
assert.Equal(t, want, ToPosix(in), in)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFromPosix(t *testing.T) {
|
||||
tests := map[string]string{
|
||||
"/c/some/windows/place": `C:\some\windows\place`,
|
||||
"/c/Foo/bAr": `C:\Foo\bAr`,
|
||||
"/c/ふー/バー": `C:\ふー\バー`,
|
||||
// non volume-encoded input is left alone
|
||||
"relative/path": "relative/path",
|
||||
`D:\already\native`: `D:\already\native`,
|
||||
}
|
||||
for in, want := range tests {
|
||||
assert.Equal(t, want, FromPosix(in), in)
|
||||
}
|
||||
}
|
||||
@@ -156,8 +156,8 @@ var busyboxVersionMatcher = regexp.MustCompile(`BusyBox v[\d.]+`)
|
||||
func parseBusyBox(contents string) (*Release, error) {
|
||||
matches := busyboxVersionMatcher.FindAllString(contents, -1)
|
||||
for _, match := range matches {
|
||||
parts := strings.Split(match, " ")
|
||||
version := strings.ReplaceAll(parts[1], "v", "")
|
||||
_, version, _ := strings.Cut(match, " ")
|
||||
version = strings.ReplaceAll(version, "v", "")
|
||||
|
||||
return simpleRelease(match, "busybox", version, ""), nil
|
||||
}
|
||||
|
||||
@@ -11,8 +11,12 @@ import (
|
||||
|
||||
const (
|
||||
catalogerName = "gguf-cataloger"
|
||||
ggufLayerMediaType = "application/vnd.docker.ai*"
|
||||
safeTensorsCatalogerName = "safetensors-cataloger"
|
||||
|
||||
// dockerAIGGUFMediaType mirrors ggufLayerMediaType in
|
||||
// syft/source/ocimodelsource/registry_client.go. It is matched exactly: the
|
||||
// other application/vnd.docker.ai.* layers are not GGUF.
|
||||
dockerAIGGUFMediaType = "application/vnd.docker.ai.gguf.v3"
|
||||
)
|
||||
|
||||
// NewGGUFCataloger returns a new cataloger instance for GGUF model files.
|
||||
@@ -21,7 +25,7 @@ const (
|
||||
func NewGGUFCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger(catalogerName).
|
||||
WithParserByGlobs(parseGGUFModel, "**/*.gguf").
|
||||
WithParserByMediaType(parseGGUFModel, ggufLayerMediaType).
|
||||
WithParserByMediaType(parseGGUFModel, dockerAIGGUFMediaType).
|
||||
WithProcessors(ggufMergeProcessor)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/tmpdir"
|
||||
"github.com/anchore/syft/syft/internal/fileresolver"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
@@ -138,3 +145,42 @@ func TestGGUFCataloger(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGGUFCataloger_ociLayers(t *testing.T) {
|
||||
// a GGUF OCI artifact is merged into one named package, while the other
|
||||
// application/vnd.docker.ai.* layers (config, model.file, license and
|
||||
// safetensors weights) never reach the GGUF parser.
|
||||
named := newTestGGUFBuilder().
|
||||
withStringKV("general.architecture", "llama").
|
||||
withStringKV("general.name", "llama3-8b").
|
||||
build()
|
||||
part := newTestGGUFBuilder().withStringKV("general.architecture", "llama").build()
|
||||
|
||||
dir := t.TempDir()
|
||||
layers := map[string]fileresolver.LayerInfo{}
|
||||
for digest, l := range map[string]struct {
|
||||
mediaType string
|
||||
content []byte
|
||||
}{
|
||||
"sha256:named": {dockerAIGGUFMediaType, named},
|
||||
"sha256:part": {dockerAIGGUFMediaType, part},
|
||||
"sha256:config": {dockerAIModelConfigMediaTypes[0], []byte("not a gguf file")},
|
||||
"sha256:file": {dockerAIModelFileMediaType, []byte("not a gguf file")},
|
||||
"sha256:license": {dockerAILicenseMediaType, []byte("not a gguf file")},
|
||||
"sha256:weights": {dockerAISafeTensorsMediaType, []byte("not a gguf file")},
|
||||
} {
|
||||
p := filepath.Join(dir, digest[len("sha256:"):])
|
||||
require.NoError(t, os.WriteFile(p, l.content, 0o600))
|
||||
layers[digest] = fileresolver.LayerInfo{TempPath: p, MediaType: l.mediaType}
|
||||
}
|
||||
resolver := fileresolver.NewContainerImageModel(dir, layers, "example/model:latest")
|
||||
ctx := tmpdir.WithValue(context.Background(), tmpdir.FromPath(t.TempDir()))
|
||||
|
||||
pkgs, _, err := NewGGUFCataloger().Catalog(ctx, resolver)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pkgs, 1)
|
||||
assert.Equal(t, "llama3-8b", pkgs[0].Name)
|
||||
header, ok := pkgs[0].Metadata.(pkg.GGUFFileHeader)
|
||||
require.True(t, ok)
|
||||
assert.Len(t, header.Parts, 1)
|
||||
}
|
||||
|
||||
@@ -8,17 +8,17 @@ import (
|
||||
// ggufMergeProcessor consolidates multiple GGUF packages into a single package
|
||||
// representing the AI model. When scanning OCI images with multiple layers,
|
||||
// each layer may produce a separate package. This processor finds the package
|
||||
// with a name and merges metadata from nameless packages into its GGUFFileParts field.
|
||||
// with a name and merges metadata from nameless packages into its Parts field.
|
||||
// Only packages with a non-empty name are returned in the final result.
|
||||
func ggufMergeProcessor(pkgs []pkg.Package, rels []artifact.Relationship, err error) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
if err != nil {
|
||||
return pkgs, rels, err
|
||||
}
|
||||
// don't early-return on err: it aggregates per-file failures, and skipping the
|
||||
// merge would drop every nameless layer and the named package's Parts along
|
||||
// with it (see safeTensorsMergeProcessor).
|
||||
if len(pkgs) == 0 {
|
||||
return pkgs, rels, err
|
||||
}
|
||||
|
||||
// Separate packages with names from those without
|
||||
// separate packages with names from those without
|
||||
var namedPkgs []pkg.Package
|
||||
var namelessHeaders []pkg.GGUFFileHeader
|
||||
|
||||
@@ -27,14 +27,14 @@ func ggufMergeProcessor(pkgs []pkg.Package, rels []artifact.Relationship, err er
|
||||
namedPkgs = append(namedPkgs, p)
|
||||
} else {
|
||||
if header, ok := p.Metadata.(pkg.GGUFFileHeader); ok {
|
||||
// We do not want a kv hash for nameless headers
|
||||
// we do not want a kv hash for nameless headers
|
||||
header.MetadataKeyValuesHash = ""
|
||||
namelessHeaders = append(namelessHeaders, header)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there are no named packages, return nothing
|
||||
// if there are no named packages, return nothing
|
||||
if len(namedPkgs) == 0 {
|
||||
return nil, rels, err
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package ai
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
"go.yaml.in/yaml/v3"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -15,6 +15,12 @@ type hfConfig struct {
|
||||
NameOrPath string `json:"_name_or_path"`
|
||||
}
|
||||
|
||||
// looksLikeHF reports whether the config carries any field we use. Other JSON
|
||||
// (generation_config.json, unrelated app configs) is ignored.
|
||||
func (c hfConfig) looksLikeHF() bool {
|
||||
return len(c.Architectures) > 0 || c.NameOrPath != ""
|
||||
}
|
||||
|
||||
func applyHFConfig(md *pkg.SafeTensorsModelInfo, cfg *hfConfig) {
|
||||
if md.Architecture == "" && len(cfg.Architectures) > 0 {
|
||||
md.Architecture = cfg.Architectures[0]
|
||||
@@ -23,8 +29,8 @@ func applyHFConfig(md *pkg.SafeTensorsModelInfo, cfg *hfConfig) {
|
||||
|
||||
// readmeFrontmatter holds the subset of YAML frontmatter fields we extract.
|
||||
type readmeFrontmatter struct {
|
||||
License string `yaml:"license"`
|
||||
BaseModel []string `yaml:"base_model"`
|
||||
Licenses []string
|
||||
BaseModel []string
|
||||
}
|
||||
|
||||
type licenseFrontmatter struct {
|
||||
@@ -32,21 +38,33 @@ type licenseFrontmatter struct {
|
||||
}
|
||||
|
||||
// extractFrontmatterBlock returns the YAML bytes between the first and second
|
||||
// "---" delimiters of a file
|
||||
// "---" delimiter lines of a file
|
||||
func extractFrontmatterBlock(buf []byte) []byte {
|
||||
trimmed := bytes.TrimLeft(buf, "\xef\xbb\xbf \t\r\n")
|
||||
if !bytes.HasPrefix(trimmed, []byte("---")) {
|
||||
first, rest, _ := bytes.Cut(trimmed, []byte("\n"))
|
||||
if !isFrontmatterDelimiter(first) {
|
||||
return nil
|
||||
}
|
||||
rest := trimmed[3:]
|
||||
if i := bytes.IndexByte(rest, '\n'); i >= 0 {
|
||||
rest = rest[i+1:]
|
||||
for off := 0; off < len(rest); {
|
||||
line, _, found := bytes.Cut(rest[off:], []byte("\n"))
|
||||
if isFrontmatterDelimiter(line) {
|
||||
if off == 0 || off > maxFrontmatterSize {
|
||||
return nil
|
||||
}
|
||||
return rest[:off]
|
||||
}
|
||||
if !found {
|
||||
break
|
||||
}
|
||||
off += len(line) + 1
|
||||
}
|
||||
block, _, found := bytes.Cut(rest, []byte("\n---"))
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
return block
|
||||
return nil
|
||||
}
|
||||
|
||||
// isFrontmatterDelimiter reports whether a line is exactly "---", ignoring
|
||||
// trailing whitespace and \r.
|
||||
func isFrontmatterDelimiter(line []byte) bool {
|
||||
return bytes.Equal(bytes.TrimRight(line, " \t\r"), []byte("---"))
|
||||
}
|
||||
|
||||
// parseFrontmatter decodes a Hugging Face model card YAML frontmatter block
|
||||
@@ -57,8 +75,9 @@ func parseFrontmatter(buf []byte) *readmeFrontmatter {
|
||||
return nil
|
||||
}
|
||||
|
||||
// both fields may be a scalar or a list, so decode them as nodes
|
||||
var raw struct {
|
||||
License string `yaml:"license"`
|
||||
License yaml.Node `yaml:"license"`
|
||||
BaseModel yaml.Node `yaml:"base_model"`
|
||||
}
|
||||
if err := yaml.Unmarshal(block, &raw); err != nil {
|
||||
@@ -66,16 +85,37 @@ func parseFrontmatter(buf []byte) *readmeFrontmatter {
|
||||
return nil
|
||||
}
|
||||
|
||||
fm := readmeFrontmatter{License: raw.License}
|
||||
switch raw.BaseModel.Kind {
|
||||
return &readmeFrontmatter{
|
||||
Licenses: yamlStrings(raw.License),
|
||||
BaseModel: yamlStrings(raw.BaseModel),
|
||||
}
|
||||
}
|
||||
|
||||
// yamlStrings returns the non-empty, non-null values of a scalar or a sequence
|
||||
// of scalars, keeping at most maxFrontmatterValues. Anything else yields nil.
|
||||
func yamlStrings(n yaml.Node) []string {
|
||||
switch n.Kind {
|
||||
case yaml.ScalarNode:
|
||||
if raw.BaseModel.Value != "" {
|
||||
fm.BaseModel = []string{raw.BaseModel.Value}
|
||||
if isYAMLString(&n) {
|
||||
return []string{n.Value}
|
||||
}
|
||||
case yaml.SequenceNode:
|
||||
_ = raw.BaseModel.Decode(&fm.BaseModel)
|
||||
var out []string
|
||||
for _, c := range n.Content {
|
||||
if len(out) == maxFrontmatterValues {
|
||||
break
|
||||
}
|
||||
if c.Kind == yaml.ScalarNode && isYAMLString(c) {
|
||||
out = append(out, c.Value)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
return &fm
|
||||
return nil
|
||||
}
|
||||
|
||||
func isYAMLString(n *yaml.Node) bool {
|
||||
return n.Value != "" && n.ShortTag() != "!!null"
|
||||
}
|
||||
|
||||
// parseLicenseFrontmatter returns the producer-declared SPDX identifier
|
||||
|
||||
@@ -3,7 +3,6 @@ package ai
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"path"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
@@ -13,42 +12,42 @@ import (
|
||||
)
|
||||
|
||||
// resolveSafeTensorsDirIdentity handles the directory-scan case for safe tensors
|
||||
// find config.json beside the model files (walking up parent directories to the
|
||||
// scanned source root if no sibling exists) and a sibling README.md. It returns
|
||||
// find config.json beside the model files (or one directory up, for the shard
|
||||
// subdirectory layout) and a sibling README.md. It returns
|
||||
// the group's name candidates, resolved licenses, and supporting evidence.
|
||||
func resolveSafeTensorsDirIdentity(ctx context.Context, resolver file.Resolver, dir string, md *pkg.SafeTensorsModelInfo) safeTensorsIdentity {
|
||||
id := safeTensorsIdentity{fallbackName: safeTensorsDirName(dir)}
|
||||
|
||||
if loc, cfg := findDirHFConfig(resolver, dir); cfg != nil {
|
||||
applyHFConfig(md, cfg)
|
||||
id.nameOrPath = cfg.NameOrPath
|
||||
id.nameOrPath = sanitizeModelName(cfg.NameOrPath)
|
||||
id.supporting = append(id.supporting, *loc)
|
||||
}
|
||||
|
||||
if loc, fm := readDirReadmeFrontmatter(resolver, path.Join(dir, "README.md")); fm != nil {
|
||||
if fm.License != "" {
|
||||
id.licenses = pkg.NewLicensesFromValuesWithContext(ctx, fm.License)
|
||||
if len(fm.Licenses) > 0 {
|
||||
id.licenses = pkg.NewLicensesFromValuesWithContext(ctx, fm.Licenses...)
|
||||
}
|
||||
if id.nameOrPath == "" && len(fm.BaseModel) > 0 {
|
||||
id.nameOrPath = fm.BaseModel[0]
|
||||
if id.nameOrPath == "" {
|
||||
id.nameOrPath = firstModelName(fm.BaseModel)
|
||||
}
|
||||
id.supporting = append(id.supporting, *loc)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// findDirHFConfig looks for a config.json beside the model files
|
||||
// findDirHFConfig looks for an HF config.json beside the model files, then in
|
||||
// the parent directory (covers weights kept in a subdirectory of the model). It
|
||||
// does not walk further up, so an unrelated config.json higher in the tree can't
|
||||
// name every model below it.
|
||||
func findDirHFConfig(resolver file.Resolver, dir string) (*file.Location, *hfConfig) {
|
||||
for {
|
||||
if loc, cfg := readDirHFConfig(resolver, path.Join(dir, "config.json")); cfg != nil {
|
||||
return loc, cfg
|
||||
}
|
||||
parent := path.Dir(dir)
|
||||
if parent == dir {
|
||||
return nil, nil // reached the source root
|
||||
}
|
||||
dir = parent
|
||||
if loc, cfg := readDirHFConfig(resolver, path.Join(dir, "config.json")); cfg != nil {
|
||||
return loc, cfg
|
||||
}
|
||||
if parent := path.Dir(dir); parent != dir {
|
||||
return readDirHFConfig(resolver, path.Join(parent, "config.json"))
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func readDirHFConfig(resolver file.Resolver, p string) (*file.Location, *hfConfig) {
|
||||
@@ -62,11 +61,19 @@ func readDirHFConfig(resolver file.Resolver, p string) (*file.Location, *hfConfi
|
||||
}
|
||||
defer internal.CloseAndLogError(rc, p)
|
||||
|
||||
buf, err := readBounded(rc, maxHFConfigSize)
|
||||
if err != nil {
|
||||
log.Debugf("failed to read %s: %v", p, err)
|
||||
return nil, nil
|
||||
}
|
||||
var cfg hfConfig
|
||||
if err := json.NewDecoder(rc).Decode(&cfg); err != nil {
|
||||
if err := json.Unmarshal(buf, &cfg); err != nil {
|
||||
log.Debugf("failed to decode %s: %v", p, err)
|
||||
return nil, nil
|
||||
}
|
||||
if !cfg.looksLikeHF() {
|
||||
return nil, nil
|
||||
}
|
||||
return &locations[0], &cfg
|
||||
}
|
||||
|
||||
@@ -81,8 +88,9 @@ func readDirReadmeFrontmatter(resolver file.Resolver, p string) (*file.Location,
|
||||
}
|
||||
defer internal.CloseAndLogError(rc, p)
|
||||
|
||||
buf, err := io.ReadAll(io.LimitReader(rc, 1024*1024))
|
||||
buf, err := readPrefix(rc, maxReadmePrefixSize)
|
||||
if err != nil {
|
||||
log.Debugf("failed to read %s: %v", p, err)
|
||||
return nil, nil
|
||||
}
|
||||
fm := parseFrontmatter(buf)
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"path"
|
||||
"sort"
|
||||
|
||||
gcrname "github.com/google/go-containerregistry/pkg/name"
|
||||
|
||||
@@ -28,9 +28,18 @@ func resolveSafeTensorsOCIIdentity(ctx context.Context, resolver file.Resolver,
|
||||
log.Debugf("failed to list docker AI model-file layers: %v", err)
|
||||
}
|
||||
|
||||
// Collect config / readme candidates separately so the layer-iteration order
|
||||
// returned by the resolver doesn't decide the precedence.
|
||||
var configName, readmeName, readmeLicense string
|
||||
// the resolver returns layers in map order, and the first config / README
|
||||
// seen wins below, so sort for a stable result across runs. For OCI
|
||||
// artifacts every RealPath is "/", so in practice this is digest order.
|
||||
sort.Slice(modelFileLocs, func(i, j int) bool {
|
||||
if modelFileLocs[i].RealPath != modelFileLocs[j].RealPath {
|
||||
return modelFileLocs[i].RealPath < modelFileLocs[j].RealPath
|
||||
}
|
||||
return modelFileLocs[i].FileSystemID < modelFileLocs[j].FileSystemID
|
||||
})
|
||||
|
||||
var configName, readmeName string
|
||||
var readmeLicenses []string
|
||||
var supporting []file.Location
|
||||
for _, loc := range modelFileLocs {
|
||||
cfg, fm := classifyOCIModelFileLayer(resolver, loc)
|
||||
@@ -38,15 +47,15 @@ func resolveSafeTensorsOCIIdentity(ctx context.Context, resolver file.Resolver,
|
||||
case cfg != nil:
|
||||
applyHFConfig(md, cfg)
|
||||
if configName == "" {
|
||||
configName = cfg.NameOrPath
|
||||
configName = sanitizeModelName(cfg.NameOrPath)
|
||||
}
|
||||
supporting = append(supporting, loc)
|
||||
case fm != nil:
|
||||
if readmeLicense == "" {
|
||||
readmeLicense = fm.License
|
||||
if len(readmeLicenses) == 0 {
|
||||
readmeLicenses = fm.Licenses
|
||||
}
|
||||
if readmeName == "" && len(fm.BaseModel) > 0 {
|
||||
readmeName = fm.BaseModel[0]
|
||||
if readmeName == "" {
|
||||
readmeName = firstModelName(fm.BaseModel)
|
||||
}
|
||||
supporting = append(supporting, loc)
|
||||
}
|
||||
@@ -64,7 +73,7 @@ func resolveSafeTensorsOCIIdentity(ctx context.Context, resolver file.Resolver,
|
||||
supporting: supporting,
|
||||
}
|
||||
|
||||
// License precedence: a dedicated vnd.docker.ai.license layer is a
|
||||
// License precedence: a dedicated vnd.docker.ai.license layer
|
||||
// outranks the free-text license field in a model card's README frontmatter.
|
||||
licLocs, err := ociResolver.FilesByMediaType(dockerAILicenseMediaType)
|
||||
if err != nil {
|
||||
@@ -74,8 +83,8 @@ func resolveSafeTensorsOCIIdentity(ctx context.Context, resolver file.Resolver,
|
||||
case len(licLocs) > 0:
|
||||
id.licenses = identifyLicenseLayers(ctx, resolver, licLocs)
|
||||
id.supporting = append(id.supporting, licLocs...)
|
||||
case readmeLicense != "":
|
||||
id.licenses = pkg.NewLicensesFromValuesWithContext(ctx, readmeLicense)
|
||||
case len(readmeLicenses) > 0:
|
||||
id.licenses = pkg.NewLicensesFromValuesWithContext(ctx, readmeLicenses...)
|
||||
}
|
||||
|
||||
return id
|
||||
@@ -108,10 +117,11 @@ func ociImageRefBasename(resolver file.Resolver) string {
|
||||
return path.Base(parsed.Context().RepositoryStr())
|
||||
}
|
||||
|
||||
// classifyOCIModelFileLayer reads up to 4 MiB of a model.file layer and decodes
|
||||
// it as either an HF config.json or a README model card's YAML frontmatter,
|
||||
// based on its leading bytes. It returns whichever it recognized; both are nil
|
||||
// when the layer is neither (or fails to decode). The caller owns precedence and
|
||||
// classifyOCIModelFileLayer reads a model.file layer (up to maxModelFileLayerSize)
|
||||
// and decodes it as either an HF config.json or a README model card's YAML
|
||||
// frontmatter, based on its leading bytes. It returns whichever it recognized;
|
||||
// both are nil when the layer is neither, fails to decode, or is JSON that
|
||||
// doesn't look like an HF config (e.g. generation_config.json). The caller owns precedence and
|
||||
// metadata enrichment.
|
||||
func classifyOCIModelFileLayer(resolver file.Resolver, loc file.Location) (*hfConfig, *readmeFrontmatter) {
|
||||
rc, err := resolver.FileContentsByLocation(loc)
|
||||
@@ -120,17 +130,18 @@ func classifyOCIModelFileLayer(resolver file.Resolver, loc file.Location) (*hfCo
|
||||
}
|
||||
defer internal.CloseAndLogError(rc, loc.RealPath)
|
||||
|
||||
buf, err := io.ReadAll(io.LimitReader(rc, 4*1024*1024))
|
||||
buf, err := readBounded(rc, maxModelFileLayerSize)
|
||||
if err != nil {
|
||||
log.Debugf("failed to read docker AI model-file layer: %v", err)
|
||||
return nil, nil
|
||||
}
|
||||
trimmed := bytes.TrimLeft(buf, "\xef\xbb\xbf \t\r\n")
|
||||
switch {
|
||||
case bytes.HasPrefix(trimmed, []byte("---")):
|
||||
return nil, parseFrontmatter(buf)
|
||||
return nil, parseFrontmatter(trimmed)
|
||||
case bytes.HasPrefix(trimmed, []byte("{")):
|
||||
var cfg hfConfig
|
||||
if err := json.Unmarshal(buf, &cfg); err != nil {
|
||||
if err := json.Unmarshal(trimmed, &cfg); err != nil || !cfg.looksLikeHF() {
|
||||
return nil, nil
|
||||
}
|
||||
return &cfg, nil
|
||||
@@ -165,8 +176,9 @@ func readLicenseSPDXIDFromFrontmatter(resolver file.Resolver, loc file.Location)
|
||||
}
|
||||
defer internal.CloseAndLogError(rc, loc.RealPath)
|
||||
|
||||
buf, err := io.ReadAll(io.LimitReader(rc, 64*1024))
|
||||
buf, err := readPrefix(rc, maxLicensePrefixSize)
|
||||
if err != nil {
|
||||
log.Debugf("failed to read docker AI license layer: %v", err)
|
||||
return ""
|
||||
}
|
||||
return parseLicenseFrontmatter(buf)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
)
|
||||
|
||||
// size limits for model companion files and headers. JSON reads that hit a
|
||||
// limit fail (see readBounded) instead of parsing a truncated document.
|
||||
// Frontmatter-only reads (README, license) take a prefix instead, since the
|
||||
// frontmatter sits at the top and is itself capped by maxFrontmatterSize.
|
||||
const (
|
||||
maxHFConfigSize = 4 * 1024 * 1024
|
||||
maxReadmePrefixSize = 1 * 1024 * 1024 // dir scans; OCI README layers use maxModelFileLayerSize
|
||||
maxModelFileLayerSize = 4 * 1024 * 1024
|
||||
maxLicensePrefixSize = 64 * 1024
|
||||
maxModelConfigBlobSize = 1 * 1024 * 1024
|
||||
maxFrontmatterSize = 256 * 1024
|
||||
maxFrontmatterValues = 32
|
||||
maxModelNameLength = 256
|
||||
|
||||
// maxSafeTensorsHeaderSize caps the header JSON (excluding its 8-byte length
|
||||
// prefix). It is shared with the OCI source so dir and OCI scans accept the
|
||||
// same headers.
|
||||
maxSafeTensorsHeaderSize = internal.MaxSafeTensorsHeaderSize
|
||||
)
|
||||
|
||||
// readPrefix reads at most limit bytes. It is for frontmatter-only reads, where
|
||||
// the rest of the file is never needed.
|
||||
func readPrefix(r io.Reader, limit int64) ([]byte, error) {
|
||||
return io.ReadAll(io.LimitReader(r, limit))
|
||||
}
|
||||
|
||||
// readBounded reads at most limit bytes and reports overflow instead of silently truncating.
|
||||
func readBounded(r io.Reader, limit int64) ([]byte, error) {
|
||||
b, err := io.ReadAll(io.LimitReader(r, limit+1))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if int64(len(b)) > limit {
|
||||
return nil, fmt.Errorf("content exceeds %d bytes", limit)
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
@@ -75,7 +75,7 @@ func mergeShardsInto(merged *pkg.SafeTensorsModelInfo, shards []pkg.SafeTensorsM
|
||||
seenKV := map[string]bool{}
|
||||
for _, s := range shards {
|
||||
shardTensorTotal += s.TensorCount
|
||||
merged.Parameters += s.Parameters
|
||||
merged.Parameters = saturatingAdd(merged.Parameters, s.Parameters)
|
||||
firstNonEmpty(&merged.Quantization, s.Quantization)
|
||||
for _, kv := range s.UserMetadata {
|
||||
if seenKV[kv.Key] {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -164,3 +165,10 @@ func TestRollupHash(t *testing.T) {
|
||||
assert.NotEqual(t, "a", ab)
|
||||
assert.NotEqual(t, "b", ab)
|
||||
}
|
||||
|
||||
func TestMergeShardsInto_parametersSaturate(t *testing.T) {
|
||||
var merged pkg.SafeTensorsModelInfo
|
||||
shards := []pkg.SafeTensorsModelInfo{{Parameters: math.MaxUint64 - 1}, {Parameters: 2}}
|
||||
mergeShardsInto(&merged, shards)
|
||||
assert.Equal(t, uint64(math.MaxUint64), merged.Parameters)
|
||||
}
|
||||
|
||||
@@ -1,15 +1,44 @@
|
||||
package ai
|
||||
|
||||
import "path"
|
||||
import (
|
||||
"path"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// pickSafeTensorsName implements the documented naming precedence chain:
|
||||
func pickSafeTensorsName(nameOrPath, fallbackName string) string {
|
||||
if nameOrPath != "" {
|
||||
return path.Base(nameOrPath)
|
||||
if name := sanitizeModelName(nameOrPath); name != "" {
|
||||
return name
|
||||
}
|
||||
return fallbackName
|
||||
}
|
||||
|
||||
// firstModelName returns the first value that sanitizes to a usable name.
|
||||
func firstModelName(values []string) string {
|
||||
for _, v := range values {
|
||||
if name := sanitizeModelName(v); name != "" {
|
||||
return name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// sanitizeModelName reduces a producer-supplied name or path (_name_or_path,
|
||||
// base_model) to its last path element, capped at maxModelNameLength runes. It
|
||||
// returns "" when nothing usable is left.
|
||||
func sanitizeModelName(nameOrPath string) string {
|
||||
base := path.Base(strings.ReplaceAll(nameOrPath, `\`, "/"))
|
||||
switch base {
|
||||
case "/", ".", "..", "":
|
||||
return ""
|
||||
}
|
||||
if utf8.RuneCountInString(base) > maxModelNameLength {
|
||||
base = string([]rune(base)[:maxModelNameLength])
|
||||
}
|
||||
return base
|
||||
}
|
||||
|
||||
// safeTensorsDirName returns the directory-scan naming fallback: the base name
|
||||
// of the group's parent directory (the group key is already that directory).
|
||||
func safeTensorsDirName(directory string) string {
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func newGGUFPackage(metadata *pkg.GGUFFileHeader, modelName, version, license string, locations ...file.Location) pkg.Package {
|
||||
func newGGUFPackage(ctx context.Context, metadata *pkg.GGUFFileHeader, modelName, version, license string, locations ...file.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: modelName,
|
||||
Version: version,
|
||||
Locations: file.NewLocationSet(locations...),
|
||||
Type: pkg.ModelPkg,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValues(license)...),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromValuesWithContext(ctx, license)...),
|
||||
Metadata: *metadata,
|
||||
// NOTE: PURL is intentionally not set as the package-url spec
|
||||
// has not yet finalized support for ML model packages
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -103,6 +104,7 @@ func TestNewGGUFPackage(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
actual := newGGUFPackage(
|
||||
context.Background(),
|
||||
tt.metadata,
|
||||
tt.input.modelName,
|
||||
tt.input.version,
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
package ai
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
gguf_parser "github.com/gpustack/gguf-parser-go"
|
||||
)
|
||||
@@ -12,12 +17,41 @@ import (
|
||||
const (
|
||||
ggufMagicNumber = 0x46554747 // "GGUF" in little-endian
|
||||
maxHeaderSize = 50 * 1024 * 1024 // 50MB for large tokenizer vocabularies
|
||||
|
||||
// maxGGUFArrayDepth allows arrays of arrays but nothing deeper.
|
||||
maxGGUFArrayDepth = 2
|
||||
// maxGGUFDecodedItems bounds the items, across the whole header, of arrays that gguf-parser-go
|
||||
// always decodes in full. Real files hold a few per-layer arrays.
|
||||
maxGGUFDecodedItems = 65536
|
||||
// maxGGUFSplitTensors bounds split.tensors.count, which the library preallocates tensor infos for.
|
||||
// Shards declare the total across all splits, so this can't be the header tensor count.
|
||||
maxGGUFSplitTensors = 1 << 17
|
||||
// maxGGUFKeyLength is the key length limit from the GGUF spec.
|
||||
maxGGUFKeyLength = 65535
|
||||
// maxGGUFKVCount bounds the metadata KV count. Real files hold hundreds; vocabularies live in arrays.
|
||||
maxGGUFKVCount = 65536
|
||||
// maxGGUFReadStringBytes bounds, across the header, the key and string bytes the library reads in
|
||||
// full and syft then hashes and encodes (chat templates are the largest, at tens of KB).
|
||||
maxGGUFReadStringBytes = 8 << 20
|
||||
// maxGGUFSkippedStrings bounds, across the header, the strings the library skips one at a time
|
||||
// inside arrays (vocabularies run to a few hundred thousand tokens and merges).
|
||||
maxGGUFSkippedStrings = 1 << 22
|
||||
)
|
||||
|
||||
// copyHeader copies the GGUF header from the reader to the writer.
|
||||
// ggufDecodedArraySuffixes are the keys gguf-parser-go decodes in full even with SkipLargeMetadata
|
||||
// (hardcoded in its ReadArray as of v0.26.3; re-check on upgrade).
|
||||
var ggufDecodedArraySuffixes = []string{
|
||||
".feed_forward_length", ".attention.head_count", ".attention.head_count_kv", ".attention.sliding_window_pattern",
|
||||
}
|
||||
|
||||
// ggufScalarSizes is the encoded size of each value type, indexed by type (0 for strings and arrays).
|
||||
var ggufScalarSizes = [...]int{1, 1, 2, 2, 4, 4, 4, 1, 0, 0, 8, 8, 8}
|
||||
|
||||
// copyHeader copies at most maxHeaderSize bytes of the GGUF header from the reader to the writer.
|
||||
// It validates the magic number first, then copies the rest of the data.
|
||||
// The reader should be wrapped with io.LimitedReader to prevent OOM issues.
|
||||
func copyHeader(w io.Writer, r io.Reader) error {
|
||||
r = io.LimitReader(r, maxHeaderSize)
|
||||
|
||||
// Read initial chunk to validate magic number
|
||||
// GGUF format: magic(4) + version(4) + tensor_count(8) + metadata_kv_count(8) + metadata_kvs + tensors_info
|
||||
initialBuf := make([]byte, 24) // Enough for magic, version, tensor count, and kv count
|
||||
@@ -37,7 +71,6 @@ func copyHeader(w io.Writer, r io.Reader) error {
|
||||
}
|
||||
|
||||
// Copy the rest of the header from reader to writer
|
||||
// The LimitedReader will return EOF once maxHeaderSize is reached
|
||||
if _, err := io.Copy(w, r); err != nil {
|
||||
return fmt.Errorf("failed to copy GGUF header: %w", err)
|
||||
}
|
||||
@@ -45,7 +78,269 @@ func copyHeader(w io.Writer, r io.Reader) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Helper to convert gguf_parser metadata to simpler types
|
||||
// validateGGUFHeader walks the metadata KV section and rejects values that gguf-parser-go would panic
|
||||
// on, recurse on without bound, or overallocate for. It reads without storing anything.
|
||||
// ponytail: tensor infos are not checked here (zero block size types, dimension overflow); those
|
||||
// panics are recoverable and are left to the per-file panic recovery in the generic cataloger.
|
||||
func validateGGUFHeader(r io.Reader) error {
|
||||
v := &ggufHeaderValidator{
|
||||
r: bufio.NewReader(r),
|
||||
decodedBudget: maxGGUFDecodedItems,
|
||||
readStringBudget: maxGGUFReadStringBytes,
|
||||
skippedStringBudget: maxGGUFSkippedStrings,
|
||||
}
|
||||
var prefix struct {
|
||||
Magic, Version uint32
|
||||
}
|
||||
if err := binary.Read(v.r, binary.LittleEndian, &prefix); err != nil {
|
||||
return fmt.Errorf("reading header prefix: %w", err)
|
||||
}
|
||||
v.v1 = prefix.Version <= 1
|
||||
// the tensor count is validated by the library
|
||||
if _, err := v.readLen(); err != nil {
|
||||
return fmt.Errorf("reading tensor count: %w", err)
|
||||
}
|
||||
kvCount, err := v.readLen()
|
||||
if err != nil {
|
||||
return fmt.Errorf("reading metadata count: %w", err)
|
||||
}
|
||||
if kvCount > maxGGUFKVCount {
|
||||
return fmt.Errorf("metadata count %d exceeds %d", kvCount, maxGGUFKVCount)
|
||||
}
|
||||
|
||||
for i := uint64(0); i < kvCount; i++ {
|
||||
key, err := v.readKey()
|
||||
if err != nil {
|
||||
return fmt.Errorf("metadata key %d: %w", i, err)
|
||||
}
|
||||
var vt uint32
|
||||
if err := binary.Read(v.r, binary.LittleEndian, &vt); err != nil {
|
||||
return fmt.Errorf("metadata key %q: %w", key, err)
|
||||
}
|
||||
if err := v.checkKey(key, gguf_parser.GGUFMetadataValueType(vt)); err != nil {
|
||||
return fmt.Errorf("metadata key %q: %w", key, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ggufHeaderValidator struct {
|
||||
r *bufio.Reader
|
||||
v1 bool // v1 uses 32-bit lengths
|
||||
decodedBudget uint64
|
||||
readStringBudget uint64
|
||||
skippedStringBudget uint64
|
||||
}
|
||||
|
||||
func (v *ggufHeaderValidator) checkKey(key string, vt gguf_parser.GGUFMetadataValueType) error {
|
||||
decoded := slices.ContainsFunc(ggufDecodedArraySuffixes, func(s string) bool { return strings.HasSuffix(key, s) })
|
||||
n, err := v.readValue(vt, decoded, 1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// these are read with typed accessors that panic on a type mismatch
|
||||
switch key {
|
||||
case "general.architecture", "general.name", "general.license", "general.type", "general.author",
|
||||
"general.url", "general.description", "controlvector.model_hint":
|
||||
if vt != gguf_parser.GGUFMetadataValueTypeString {
|
||||
return fmt.Errorf("expected string, got %v", vt)
|
||||
}
|
||||
case "general.quantization_version", "general.file_type":
|
||||
if !vt.IsNumeric() {
|
||||
return fmt.Errorf("expected number, got %v", vt)
|
||||
}
|
||||
case "general.alignment":
|
||||
if vt != gguf_parser.GGUFMetadataValueTypeUint32 || n == 0 {
|
||||
return fmt.Errorf("expected non-zero uint32, got %v %v", vt, n)
|
||||
}
|
||||
case "split.tensors.count":
|
||||
if !vt.IsNumeric() {
|
||||
return fmt.Errorf("expected number, got %v", vt)
|
||||
}
|
||||
if math.IsNaN(n) || n < 0 || n > maxGGUFSplitTensors {
|
||||
return fmt.Errorf("unreasonable split tensor count %.0f", n)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readValue consumes one value, returning scalar numbers as float64 for the key checks.
|
||||
func (v *ggufHeaderValidator) readValue(vt gguf_parser.GGUFMetadataValueType, decoded bool, depth int) (float64, error) {
|
||||
if uint32(vt) >= uint32(len(ggufScalarSizes)) {
|
||||
return 0, fmt.Errorf("invalid value type %d", vt)
|
||||
}
|
||||
switch vt {
|
||||
case gguf_parser.GGUFMetadataValueTypeString:
|
||||
// only strings inside skipped arrays are skipped by the library, the rest are read in full
|
||||
return 0, v.skipString(depth > 1 && !decoded)
|
||||
case gguf_parser.GGUFMetadataValueTypeArray:
|
||||
return 0, v.readArray(decoded, depth)
|
||||
}
|
||||
size := ggufScalarSizes[vt]
|
||||
b, err := v.r.Peek(size)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
var buf [8]byte
|
||||
copy(buf[:], b)
|
||||
if _, err := v.r.Discard(size); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
u := binary.LittleEndian.Uint64(buf[:])
|
||||
switch vt {
|
||||
case gguf_parser.GGUFMetadataValueTypeInt8:
|
||||
return float64(int8(u)), nil
|
||||
case gguf_parser.GGUFMetadataValueTypeInt16:
|
||||
return float64(int16(u)), nil
|
||||
case gguf_parser.GGUFMetadataValueTypeInt32:
|
||||
return float64(int32(u)), nil
|
||||
case gguf_parser.GGUFMetadataValueTypeInt64:
|
||||
return float64(int64(u)), nil
|
||||
case gguf_parser.GGUFMetadataValueTypeFloat32:
|
||||
return float64(math.Float32frombits(uint32(u))), nil
|
||||
case gguf_parser.GGUFMetadataValueTypeFloat64:
|
||||
return math.Float64frombits(u), nil
|
||||
}
|
||||
return float64(u), nil
|
||||
}
|
||||
|
||||
func (v *ggufHeaderValidator) readArray(decoded bool, depth int) error {
|
||||
var itemType uint32
|
||||
if err := binary.Read(v.r, binary.LittleEndian, &itemType); err != nil {
|
||||
return err
|
||||
}
|
||||
n, err := v.readLen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
it := gguf_parser.GGUFMetadataValueType(itemType)
|
||||
switch {
|
||||
case itemType >= uint32(len(ggufScalarSizes)):
|
||||
return fmt.Errorf("invalid array item type %d", itemType)
|
||||
case decoded && n > v.decodedBudget:
|
||||
return fmt.Errorf("decoded arrays exceed %d items", maxGGUFDecodedItems)
|
||||
case it == gguf_parser.GGUFMetadataValueTypeArray && (!decoded || depth >= maxGGUFArrayDepth):
|
||||
// the library can only skip over arrays of scalars
|
||||
return errors.New("unsupported nested array")
|
||||
case n > maxHeaderSize:
|
||||
return fmt.Errorf("array length %d exceeds header size", n)
|
||||
case !decoded && ggufScalarSizes[itemType] > 0:
|
||||
// skipped arrays of fixed size items can be passed over in one go
|
||||
_, err := v.r.Discard(int(n) * ggufScalarSizes[itemType])
|
||||
return err
|
||||
}
|
||||
if decoded {
|
||||
v.decodedBudget -= n
|
||||
}
|
||||
for i := uint64(0); i < n; i++ {
|
||||
if _, err := v.readValue(it, decoded, depth+1); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *ggufHeaderValidator) readLen() (uint64, error) {
|
||||
if v.v1 {
|
||||
var n uint32
|
||||
err := binary.Read(v.r, binary.LittleEndian, &n)
|
||||
return uint64(n), err
|
||||
}
|
||||
var n uint64
|
||||
err := binary.Read(v.r, binary.LittleEndian, &n)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// readKey reads a key the way gguf-parser-go will see it, whitespace trimmed.
|
||||
func (v *ggufHeaderValidator) readKey() (string, error) {
|
||||
n, err := v.readLen()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if n == 0 || n > maxGGUFKeyLength {
|
||||
return "", fmt.Errorf("invalid key length %d", n)
|
||||
}
|
||||
if err := v.spendReadString(n); err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf := make([]byte, n)
|
||||
if _, err := io.ReadFull(v.r, buf); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(string(buf)), nil
|
||||
}
|
||||
|
||||
func (v *ggufHeaderValidator) skipString(allowEmpty bool) error {
|
||||
n, err := v.readLen()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// gguf-parser-go reads an empty string as a full pool buffer, so everything after it would be
|
||||
// parsed out of step with this walk
|
||||
if n == 0 && !allowEmpty {
|
||||
return errors.New("empty string")
|
||||
}
|
||||
if n > maxHeaderSize {
|
||||
return fmt.Errorf("string length %d exceeds header size", n)
|
||||
}
|
||||
if allowEmpty {
|
||||
if v.skippedStringBudget == 0 {
|
||||
return fmt.Errorf("skipped strings exceed %d", maxGGUFSkippedStrings)
|
||||
}
|
||||
v.skippedStringBudget--
|
||||
} else if err := v.spendReadString(n); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = v.r.Discard(int(n))
|
||||
return err
|
||||
}
|
||||
|
||||
func (v *ggufHeaderValidator) spendReadString(n uint64) error {
|
||||
if n > v.readStringBudget {
|
||||
return fmt.Errorf("strings exceed %d bytes", maxGGUFReadStringBytes)
|
||||
}
|
||||
v.readStringBudget -= n
|
||||
return nil
|
||||
}
|
||||
|
||||
// sanitizeGGUFValue replaces non-finite floats, which JSON can not encode, leaving everything else as is.
|
||||
func sanitizeGGUFValue(v any) any {
|
||||
switch t := v.(type) {
|
||||
case float32:
|
||||
if f := float64(t); math.IsNaN(f) || math.IsInf(f, 0) {
|
||||
return nonFiniteString(f)
|
||||
}
|
||||
case float64:
|
||||
if math.IsNaN(t) || math.IsInf(t, 0) {
|
||||
return nonFiniteString(t)
|
||||
}
|
||||
case gguf_parser.GGUFMetadataKVArrayValue:
|
||||
// keep the library shape as is; items are only present for the few keys the library
|
||||
// decodes in full, which the pre-scan keeps small and shallow
|
||||
if len(t.Array) > 0 {
|
||||
items := make([]any, len(t.Array))
|
||||
for i, item := range t.Array {
|
||||
items[i] = sanitizeGGUFValue(item)
|
||||
}
|
||||
t.Array = items
|
||||
}
|
||||
return t
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func nonFiniteString(f float64) string {
|
||||
switch {
|
||||
case math.IsInf(f, 1):
|
||||
return "+Inf"
|
||||
case math.IsInf(f, -1):
|
||||
return "-Inf"
|
||||
}
|
||||
return "NaN"
|
||||
}
|
||||
|
||||
// helper to convert gguf_parser metadata to simpler types
|
||||
func convertGGUFMetadataKVs(kvs gguf_parser.GGUFMetadataKVs) map[string]any {
|
||||
result := make(map[string]any)
|
||||
|
||||
@@ -56,7 +351,7 @@ func convertGGUFMetadataKVs(kvs gguf_parser.GGUFMetadataKVs) map[string]any {
|
||||
"general.version", "general.parameter_count", "general.quantization":
|
||||
continue
|
||||
}
|
||||
result[kv.Key] = kv.Value
|
||||
result[kv.Key] = sanitizeGGUFValue(kv.Value)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -13,9 +13,7 @@ import (
|
||||
gguf_parser "github.com/gpustack/gguf-parser-go"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/tmpdir"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -38,14 +36,20 @@ func parseGGUFModel(ctx context.Context, _ file.Resolver, _ *generic.Environment
|
||||
defer cleanup()
|
||||
tempPath := tempFile.Name()
|
||||
|
||||
// Copy and validate the GGUF file header using LimitedReader to prevent OOM
|
||||
// We use LimitedReader to cap reads at maxHeaderSize (50MB)
|
||||
limitedReader := &io.LimitedReader{R: reader, N: maxHeaderSize}
|
||||
if err := copyHeader(tempFile, limitedReader); err != nil {
|
||||
// copy and validate the GGUF file header (bounded to maxHeaderSize)
|
||||
if err := copyHeader(tempFile, reader); err != nil {
|
||||
tempFile.Close()
|
||||
return nil, nil, fmt.Errorf("failed to copy GGUF header: %w", err)
|
||||
}
|
||||
if _, err := tempFile.Seek(0, io.SeekStart); err != nil {
|
||||
tempFile.Close()
|
||||
return nil, nil, fmt.Errorf("failed to rewind GGUF header: %w", err)
|
||||
}
|
||||
err = validateGGUFHeader(tempFile)
|
||||
tempFile.Close()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("invalid GGUF header: %w", err)
|
||||
}
|
||||
|
||||
// Parse using gguf-parser-go with options to skip unnecessary data
|
||||
ggufFile, err := gguf_parser.ParseGGUFFile(tempPath,
|
||||
@@ -61,6 +65,11 @@ func parseGGUFModel(ctx context.Context, _ file.Resolver, _ *generic.Environment
|
||||
// Extract version separately (will be set on Package.Version)
|
||||
modelVersion := extractVersion(ggufFile.Header.MetadataKV)
|
||||
|
||||
metadataHash, err := computeKVMetadataHash(ggufFile.Header.MetadataKV)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Convert to syft metadata structure
|
||||
syftMetadata := &pkg.GGUFFileHeader{
|
||||
Architecture: metadata.Architecture,
|
||||
@@ -69,7 +78,7 @@ func parseGGUFModel(ctx context.Context, _ file.Resolver, _ *generic.Environment
|
||||
GGUFVersion: uint32(ggufFile.Header.Version),
|
||||
TensorCount: ggufFile.Header.TensorCount,
|
||||
RemainingKeyValues: convertGGUFMetadataKVs(ggufFile.Header.MetadataKV),
|
||||
MetadataKeyValuesHash: computeKVMetadataHash(ggufFile.Header.MetadataKV),
|
||||
MetadataKeyValuesHash: metadataHash,
|
||||
}
|
||||
|
||||
// If model name is not in metadata, use filename
|
||||
@@ -79,6 +88,7 @@ func parseGGUFModel(ctx context.Context, _ file.Resolver, _ *generic.Environment
|
||||
|
||||
// Create package from metadata
|
||||
p := newGGUFPackage(
|
||||
ctx,
|
||||
syftMetadata,
|
||||
metadata.Name,
|
||||
modelVersion,
|
||||
@@ -86,14 +96,17 @@ func parseGGUFModel(ctx context.Context, _ file.Resolver, _ *generic.Environment
|
||||
reader.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
)
|
||||
|
||||
return []pkg.Package{p}, nil, unknown.IfEmptyf([]pkg.Package{p}, "unable to parse GGUF file")
|
||||
return []pkg.Package{p}, nil, nil
|
||||
}
|
||||
|
||||
// computeKVMetadataHash computes a stable hash of the KV metadata for use as a global identifier
|
||||
func computeKVMetadataHash(metadata gguf_parser.GGUFMetadataKVs) string {
|
||||
// computeKVMetadataHash computes a stable hash of the sanitized KV metadata for use as a global identifier
|
||||
func computeKVMetadataHash(metadata gguf_parser.GGUFMetadataKVs) (string, error) {
|
||||
// Sort the KV pairs by key for stable hashing
|
||||
sortedKVs := make([]gguf_parser.GGUFMetadataKV, len(metadata))
|
||||
copy(sortedKVs, metadata)
|
||||
for i, kv := range metadata {
|
||||
kv.Value = sanitizeGGUFValue(kv.Value)
|
||||
sortedKVs[i] = kv
|
||||
}
|
||||
sort.Slice(sortedKVs, func(i, j int) bool {
|
||||
return sortedKVs[i].Key < sortedKVs[j].Key
|
||||
})
|
||||
@@ -101,13 +114,12 @@ func computeKVMetadataHash(metadata gguf_parser.GGUFMetadataKVs) string {
|
||||
// Marshal sorted KVs to JSON for stable hashing
|
||||
jsonBytes, err := json.Marshal(sortedKVs)
|
||||
if err != nil {
|
||||
log.Debugf("failed to marshal metadata for hashing: %v", err)
|
||||
return ""
|
||||
return "", fmt.Errorf("failed to marshal GGUF metadata for hashing: %w", err)
|
||||
}
|
||||
|
||||
// Compute xxhash
|
||||
hash := xxhash.Sum64(jsonBytes)
|
||||
return fmt.Sprintf("%016x", hash) // 16 hex chars (64 bits)
|
||||
return fmt.Sprintf("%016x", hash), nil // 16 hex chars (64 bits)
|
||||
}
|
||||
|
||||
// extractVersion attempts to extract version from metadata KV pairs
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user