mirror of
https://github.com/anchore/syft.git
synced 2026-10-29 21:42:26 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b15c5dbfe2 | ||
|
|
35d56bfb99 | ||
|
|
abf6d78dfc | ||
|
|
fe42bcec38 | ||
|
|
fea4a50124 | ||
|
|
5eefd73ac7 | ||
|
|
684c7018be | ||
|
|
f827f91ec1 | ||
|
|
e9af7d218c | ||
|
|
506ad5d6a7 | ||
|
|
1f4f9332c5 | ||
|
|
52a4c3b594 | ||
|
|
9c321691d4 | ||
|
|
0e8d6deabe | ||
|
|
deb2fd92ef | ||
|
|
80d3b62de4 | ||
|
|
b71afc87fc | ||
|
|
efe3174b5f | ||
|
|
58e4dbbf01 | ||
|
|
b70fa899cb | ||
|
|
951fbd454a | ||
|
|
92ae4d44c5 | ||
|
|
8d48a8b8c2 | ||
|
|
cff5a05681 | ||
|
|
00ca43d24a | ||
|
|
6a27678036 | ||
|
|
89773c0a12 | ||
|
|
b08d3c2970 | ||
|
|
63232bf725 | ||
|
|
908eb57890 | ||
|
|
c5c423ab37 | ||
|
|
d4496b05aa | ||
|
|
adc55cdb3a | ||
|
|
00d0bb59cc | ||
|
|
f474308783 | ||
|
|
bf67072246 | ||
|
|
9673f867e5 | ||
|
|
a4fb2c0396 | ||
|
|
cf2ce643c3 | ||
|
|
524a44b70d | ||
|
|
4e86715c1a | ||
|
|
e8c6b7151e | ||
|
|
0fb8762f41 | ||
|
|
58ddf74140 | ||
|
|
b5d828ee14 | ||
|
|
1c4394fed0 | ||
|
|
f5c1a0befc | ||
|
|
b1287d45d8 | ||
|
|
d97216ff70 | ||
|
|
c09a009bda | ||
|
|
d61af0abab | ||
|
|
89cda82263 | ||
|
|
ee6ace36d1 | ||
|
|
e2e5e223ab | ||
|
|
4579d11abc | ||
|
|
07ae2ca08d | ||
|
|
36969bdeff | ||
|
|
dfb6011083 | ||
|
|
997a486e22 | ||
|
|
4f64fbc004 | ||
|
|
87d6a288d7 | ||
|
|
20987d30d0 | ||
|
|
e2007d9bf2 | ||
|
|
4f0e32ab51 | ||
|
|
605391114c | ||
|
|
1caf243d29 | ||
|
|
48e91312e8 | ||
|
|
d81df67493 | ||
|
|
47cda2b5ef | ||
|
|
ae711963d1 | ||
|
|
f878197150 | ||
|
|
514efb03e0 | ||
|
|
1e4f424f09 | ||
|
|
b5f0877967 | ||
|
|
8cb78ce40c | ||
|
|
3b046b3787 | ||
|
|
05cc8ee5f4 | ||
|
|
3562dab445 | ||
|
|
014a4c9c59 | ||
|
|
3cb838eacf | ||
|
|
758324b3e8 |
+7
-104
@@ -1,67 +1,10 @@
|
||||
# only pull in version updates that were released more than a week ago (low-pass filter for quickly-retracted releases)
|
||||
cooldown: 7d
|
||||
|
||||
# Most tools (binny, chronicle, cosign, golangci-lint, goreleaser, gosimports,
|
||||
# bouncer, quill, syft, task, gh) are inherited from anchore/go-make's embedded
|
||||
# .binny.yaml — see https://github.com/anchore/go-make. Only syft-specific tools
|
||||
# or version overrides should live here.
|
||||
tools:
|
||||
## internal tools ############################################################################
|
||||
|
||||
# we want to use a pinned version of binny to manage the toolchain (so binny manages itself!)
|
||||
- name: binny
|
||||
version:
|
||||
want: v0.13.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: anchore/binny
|
||||
|
||||
# used to produce SBOMs during release
|
||||
- name: syft
|
||||
version:
|
||||
want: v1.42.3
|
||||
method: github-release
|
||||
with:
|
||||
repo: anchore/syft
|
||||
|
||||
# used to sign mac binaries at release
|
||||
- name: quill
|
||||
version:
|
||||
want: v0.7.1
|
||||
method: github-release
|
||||
with:
|
||||
repo: anchore/quill
|
||||
|
||||
# used at release to generate the changelog
|
||||
- name: chronicle
|
||||
version:
|
||||
want: v0.8.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: anchore/chronicle
|
||||
|
||||
## external tools ############################################################################
|
||||
|
||||
# used for linting
|
||||
- name: golangci-lint
|
||||
version:
|
||||
want: v2.11.4
|
||||
method: github-release
|
||||
with:
|
||||
repo: golangci/golangci-lint
|
||||
|
||||
# used for showing the changelog at release
|
||||
- name: glow
|
||||
version:
|
||||
want: v2.1.1
|
||||
method: github-release
|
||||
with:
|
||||
repo: charmbracelet/glow
|
||||
|
||||
# used for signing the checksums file at release
|
||||
- name: cosign
|
||||
version:
|
||||
want: v3.0.5
|
||||
method: github-release
|
||||
with:
|
||||
repo: sigstore/cosign
|
||||
|
||||
# used in integration tests to verify JSON schemas
|
||||
- name: yajsv
|
||||
version:
|
||||
@@ -70,58 +13,18 @@ tools:
|
||||
with:
|
||||
repo: neilpa/yajsv
|
||||
|
||||
# used to release all artifacts
|
||||
- name: goreleaser
|
||||
version:
|
||||
want: v2.15.2
|
||||
method: github-release
|
||||
with:
|
||||
repo: goreleaser/goreleaser
|
||||
|
||||
# used for organizing imports during static analysis
|
||||
- name: gosimports
|
||||
version:
|
||||
want: v0.3.8
|
||||
method: github-release
|
||||
with:
|
||||
repo: rinchsan/gosimports
|
||||
|
||||
# used during static analysis for license compliance
|
||||
- name: bouncer
|
||||
version:
|
||||
want: v0.4.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: wagoodman/go-bouncer
|
||||
|
||||
# used for running all local and CI tasks
|
||||
- name: task
|
||||
version:
|
||||
want: v3.49.1
|
||||
method: github-release
|
||||
with:
|
||||
repo: go-task/task
|
||||
|
||||
# used for triggering a release
|
||||
- name: gh
|
||||
version:
|
||||
want: v2.89.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: cli/cli
|
||||
|
||||
# used to upload test fixture cache
|
||||
- name: oras
|
||||
version:
|
||||
want: v1.3.1
|
||||
want: v1.3.2
|
||||
method: github-release
|
||||
with:
|
||||
repo: oras-project/oras
|
||||
|
||||
# used to upload test fixture cache
|
||||
# used to parse JSON/YAML annotations on the fixture cache image
|
||||
- name: yq
|
||||
version:
|
||||
want: v4.52.5
|
||||
want: v4.53.3
|
||||
method: github-release
|
||||
with:
|
||||
repo: mikefarah/yq
|
||||
|
||||
@@ -1,55 +1,45 @@
|
||||
name: "Bootstrap"
|
||||
description: "Bootstrap all tools and dependencies"
|
||||
description: "Bootstrap all syft tools and dependencies on top of go-make's setup action"
|
||||
|
||||
# This action is a thin wrapper around anchore/go-make/.github/actions/setup which
|
||||
# already handles checkout, setup-go, restore-only build/mod cache, and tool cache.
|
||||
# We add the syft-specific extras here: apt packages and the test fixture cache.
|
||||
|
||||
inputs:
|
||||
go-version:
|
||||
description: "Go version to install"
|
||||
description: "Go version to install (passed to go-make/setup)"
|
||||
required: true
|
||||
default: "1.26.2"
|
||||
go-dependencies:
|
||||
description: "Download go dependencies"
|
||||
cache-key-prefix:
|
||||
description: "Prefix all cache keys with this value (passed to go-make/setup)"
|
||||
required: true
|
||||
default: "v1"
|
||||
cache-enabled:
|
||||
description: "Enable build/mod and tool caching (passed to go-make/setup)"
|
||||
required: true
|
||||
default: "true"
|
||||
cache-key-prefix:
|
||||
description: "Prefix all cache keys with this value"
|
||||
required: true
|
||||
default: "53ac821810"
|
||||
download-test-fixture-cache:
|
||||
description: "Download test fixture cache from OCI and github actions"
|
||||
required: true
|
||||
default: "false"
|
||||
tools:
|
||||
description: "whether to install tools"
|
||||
default: "true"
|
||||
bootstrap-apt-packages:
|
||||
description: "Space delimited list of tools to install via apt"
|
||||
default: "libxml2-utils"
|
||||
|
||||
runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
# note: go mod and build is automatically cached on default with v4+
|
||||
- uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
if: inputs.go-version != ''
|
||||
- name: Setup go + go-make tooling
|
||||
uses: anchore/go-make/.github/actions/setup@39fe5f71112d4dceb3ff0a92a40f272f067fc457 # v0.6.0
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
check-latest: true
|
||||
- name: Restore tool cache
|
||||
if: inputs.tools == 'true'
|
||||
id: tool-cache
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tool
|
||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
|
||||
- name: Install project tools
|
||||
cache-key-prefix: ${{ inputs.cache-key-prefix }}
|
||||
cache-enabled: ${{ inputs.cache-enabled }}
|
||||
|
||||
- name: Install binny-managed tools
|
||||
shell: bash
|
||||
if: inputs.tools == 'true'
|
||||
run: |
|
||||
make tools
|
||||
.tool/binny list
|
||||
.tool/binny check
|
||||
- name: Install go dependencies
|
||||
if: inputs.go-dependencies == 'true'
|
||||
shell: bash
|
||||
run: make ci-bootstrap-go
|
||||
run: make binny:install
|
||||
|
||||
- name: Install apt packages
|
||||
if: inputs.bootstrap-apt-packages != ''
|
||||
shell: bash
|
||||
@@ -58,12 +48,23 @@ runs:
|
||||
run: |
|
||||
IFS=' ' read -ra packages <<< "$APT_PACKAGES"
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt update && sudo -E apt install -y "${packages[@]}"
|
||||
|
||||
# ORAS cache: restore-only on non-default branches / forks
|
||||
- name: Restore ORAS cache from github actions
|
||||
if: inputs.download-test-fixture-cache == 'true'
|
||||
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
|
||||
if: ${{ inputs.download-test-fixture-cache == 'true' && (github.ref != format('refs/heads/{0}', github.event.repository.default_branch) || github.event.repository.fork == true) }}
|
||||
uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp/oras-cache
|
||||
key: ${{ inputs.cache-key-prefix }}-oras-cache
|
||||
|
||||
# ORAS cache: restore + save on the default branch of the canonical repo only.
|
||||
- name: Restore and save ORAS cache from github actions
|
||||
if: ${{ inputs.download-test-fixture-cache == 'true' && github.ref == format('refs/heads/{0}', github.event.repository.default_branch) && github.event.repository.fork == false }}
|
||||
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp/oras-cache
|
||||
key: ${{ inputs.cache-key-prefix }}-oras-cache
|
||||
|
||||
- name: Download test fixture cache
|
||||
if: inputs.download-test-fixture-cache == 'true'
|
||||
shell: bash
|
||||
|
||||
+3
-26
@@ -1,20 +1,13 @@
|
||||
# Dependabot configuration
|
||||
#
|
||||
# Grouping behavior (see inline comments for details):
|
||||
# - Minor + patch updates: grouped into a single PR per ecosystem
|
||||
# - Major version bumps: individual PR per dependency
|
||||
# - Security updates: individual PR per dependency
|
||||
#
|
||||
# Note: "patch" refers to semver version bumps (1.2.3 -> 1.2.4), not security fixes.
|
||||
# Security updates are identified separately via GitHub's Advisory Database and
|
||||
# can be any version bump (patch, minor, or major) that fixes a known CVE.
|
||||
|
||||
version: 2
|
||||
|
||||
updates:
|
||||
|
||||
- package-ecosystem: gomod
|
||||
directory: "/"
|
||||
directories:
|
||||
- "/"
|
||||
- "/.make"
|
||||
cooldown:
|
||||
default-days: 7
|
||||
schedule:
|
||||
@@ -28,14 +21,6 @@ updates:
|
||||
- dependency-name: "github.com/aquasecurity/go-version"
|
||||
- dependency-name: "github.com/knqyf263/go-apk-version"
|
||||
- dependency-name: "github.com/knqyf263/go-deb-version"
|
||||
groups:
|
||||
go-minor-patch:
|
||||
applies-to: version-updates # security updates get individual PRs
|
||||
patterns:
|
||||
- "*"
|
||||
update-types: # major omitted, gets individual PRs
|
||||
- "minor"
|
||||
- "patch"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directories:
|
||||
@@ -49,11 +34,3 @@ updates:
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- "dependencies"
|
||||
groups:
|
||||
actions-minor-patch:
|
||||
applies-to: version-updates # security updates get individual PRs
|
||||
patterns:
|
||||
- "*"
|
||||
update-types: # major omitted, gets individual PRs
|
||||
- "minor"
|
||||
- "patch"
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
red=$(tput setaf 1)
|
||||
bold=$(tput bold)
|
||||
normal=$(tput sgr0)
|
||||
|
||||
# assert we are running in CI (or die!)
|
||||
if [[ -z "$CI" ]]; then
|
||||
echo "${bold}${red}This step should ONLY be run in CI. Exiting...${normal}"
|
||||
exit 1
|
||||
fi
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import subprocess
|
||||
import sys
|
||||
import shlex
|
||||
|
||||
|
||||
class bcolors:
|
||||
HEADER = '\033[95m'
|
||||
OKBLUE = '\033[94m'
|
||||
OKCYAN = '\033[96m'
|
||||
OKGREEN = '\033[92m'
|
||||
WARNING = '\033[93m'
|
||||
FAIL = '\033[91m'
|
||||
ENDC = '\033[0m'
|
||||
BOLD = '\033[1m'
|
||||
UNDERLINE = '\033[4m'
|
||||
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: coverage.py [threshold] [go-coverage-report]")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
threshold = float(sys.argv[1])
|
||||
report = sys.argv[2]
|
||||
|
||||
|
||||
args = shlex.split(f"go tool cover -func {report}")
|
||||
p = subprocess.run(args, capture_output=True, text=True)
|
||||
|
||||
percent_coverage = float(p.stdout.splitlines()[-1].split()[-1].replace("%", ""))
|
||||
print(f"{bcolors.BOLD}Coverage: {percent_coverage}%{bcolors.ENDC}")
|
||||
|
||||
if percent_coverage < threshold:
|
||||
print(f"{bcolors.BOLD}{bcolors.FAIL}Coverage below threshold of {threshold}%{bcolors.ENDC}")
|
||||
sys.exit(1)
|
||||
@@ -1,35 +1,45 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import glob
|
||||
import sys
|
||||
import json
|
||||
import hashlib
|
||||
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
IGNORED_PREFIXES = []
|
||||
|
||||
|
||||
def find_fingerprints_and_check_dirs(base_dir):
|
||||
all_fingerprints = set(glob.glob(os.path.join(base_dir, '**', 'test*', '**', '*.fingerprint'), recursive=True))
|
||||
all_fingerprints = set(
|
||||
glob.glob(
|
||||
os.path.join(base_dir, "**", "test*", "**", "*.fingerprint"), recursive=True
|
||||
)
|
||||
)
|
||||
|
||||
all_fingerprints = {os.path.relpath(fp) for fp in all_fingerprints
|
||||
if not any(fp.startswith(prefix) for prefix in IGNORED_PREFIXES)}
|
||||
all_fingerprints = {
|
||||
os.path.relpath(fp)
|
||||
for fp in all_fingerprints
|
||||
if not any(fp.startswith(prefix) for prefix in IGNORED_PREFIXES)
|
||||
}
|
||||
|
||||
if not all_fingerprints:
|
||||
show("No .fingerprint files or cache directories found.")
|
||||
exit(1)
|
||||
|
||||
missing_content = []
|
||||
orphan_fingerprints = []
|
||||
empty_content = []
|
||||
valid_paths = set()
|
||||
fingerprint_contents = []
|
||||
|
||||
for fingerprint in all_fingerprints:
|
||||
path = fingerprint.replace('.fingerprint', '')
|
||||
path = fingerprint.replace(".fingerprint", "")
|
||||
|
||||
if not os.path.exists(path):
|
||||
missing_content.append(path)
|
||||
# paired content path is entirely missing — the .fingerprint is likely
|
||||
# leftover from a moved/deleted source (testdata trees are git-ignored,
|
||||
# so they persist locally across rename refactors)
|
||||
orphan_fingerprints.append(fingerprint)
|
||||
continue
|
||||
|
||||
if not os.path.isdir(path):
|
||||
@@ -39,13 +49,13 @@ def find_fingerprints_and_check_dirs(base_dir):
|
||||
if os.listdir(path):
|
||||
valid_paths.add(path)
|
||||
else:
|
||||
missing_content.append(path)
|
||||
empty_content.append(path)
|
||||
|
||||
with open(fingerprint, 'r') as f:
|
||||
with open(fingerprint, "r") as f:
|
||||
content = f.read().strip()
|
||||
fingerprint_contents.append((fingerprint, content))
|
||||
|
||||
return sorted(valid_paths), missing_content, fingerprint_contents
|
||||
return sorted(valid_paths), empty_content, orphan_fingerprints, fingerprint_contents
|
||||
|
||||
|
||||
def parse_fingerprint_contents(fingerprint_content):
|
||||
@@ -59,7 +69,9 @@ def parse_fingerprint_contents(fingerprint_content):
|
||||
def calculate_sha256(fingerprint_contents):
|
||||
sorted_fingerprint_contents = sorted(fingerprint_contents, key=lambda x: x[0])
|
||||
|
||||
concatenated_contents = ''.join(content for _, content in sorted_fingerprint_contents)
|
||||
concatenated_contents = "".join(
|
||||
content for _, content in sorted_fingerprint_contents
|
||||
)
|
||||
|
||||
sha256_hash = hashlib.sha256(concatenated_contents.encode()).hexdigest()
|
||||
|
||||
@@ -68,7 +80,7 @@ def calculate_sha256(fingerprint_contents):
|
||||
|
||||
def calculate_file_sha256(file_path):
|
||||
sha256_hash = hashlib.sha256()
|
||||
with open(file_path, 'rb') as f:
|
||||
with open(file_path, "rb") as f:
|
||||
for byte_block in iter(lambda: f.read(4096), b""):
|
||||
sha256_hash.update(byte_block)
|
||||
return sha256_hash.hexdigest()
|
||||
@@ -79,17 +91,28 @@ def show(*s: str):
|
||||
|
||||
|
||||
def main(file_path: str | None):
|
||||
base_dir = '.'
|
||||
valid_paths, missing_content, fingerprint_contents = find_fingerprints_and_check_dirs(base_dir)
|
||||
base_dir = "."
|
||||
valid_paths, empty_content, orphan_fingerprints, fingerprint_contents = (
|
||||
find_fingerprints_and_check_dirs(base_dir)
|
||||
)
|
||||
|
||||
if missing_content:
|
||||
show("The following paths are missing or have no content, but have corresponding .fingerprint files:")
|
||||
for path in sorted(missing_content):
|
||||
if empty_content:
|
||||
show(
|
||||
"The following paths exist but are empty, and have corresponding .fingerprint files:"
|
||||
)
|
||||
for path in sorted(empty_content):
|
||||
show(f"- {path}")
|
||||
# when adding new cache directories there is a time where it is not possible to have this directory without
|
||||
# running the tests first... but this step is a prerequisite for running the tests. We should not block on this.
|
||||
# show("Please ensure these paths exist and have content if they are directories.")
|
||||
# exit(1)
|
||||
|
||||
if orphan_fingerprints:
|
||||
show(
|
||||
"The following .fingerprint files reference paths that no longer exist "
|
||||
"(likely leftover from a moved/deleted cataloger — safe to delete, "
|
||||
"or run `task prune-orphan-fingerprints`):"
|
||||
)
|
||||
for fp in sorted(orphan_fingerprints):
|
||||
show(f"- {fp}")
|
||||
|
||||
sha256_hash = calculate_sha256(fingerprint_contents)
|
||||
|
||||
@@ -101,30 +124,24 @@ def main(file_path: str | None):
|
||||
file_digest = calculate_file_sha256(fingerprint_file)
|
||||
|
||||
# Parse the fingerprint file to get the digest/path tuples
|
||||
with open(fingerprint_file, 'r') as f:
|
||||
with open(fingerprint_file, "r") as f:
|
||||
fingerprint_content = f.read().strip()
|
||||
input_map = parse_fingerprint_contents(fingerprint_content)
|
||||
|
||||
paths_with_digests.append({
|
||||
"path": path,
|
||||
"digest": file_digest,
|
||||
"input": input_map
|
||||
})
|
||||
paths_with_digests.append(
|
||||
{"path": path, "digest": file_digest, "input": input_map}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
show(f"Error processing {fingerprint_file}: {e}")
|
||||
raise e
|
||||
|
||||
|
||||
output = {
|
||||
"digest": sha256_hash,
|
||||
"paths": paths_with_digests
|
||||
}
|
||||
output = {"digest": sha256_hash, "paths": paths_with_digests}
|
||||
|
||||
content = json.dumps(output, indent=2, sort_keys=True)
|
||||
|
||||
if file_path:
|
||||
with open(file_path, 'w') as f:
|
||||
with open(file_path, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
print(content)
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
ORIGINAL_STATE_DIR=$(mktemp -d "TEMP-original-state-XXXXXXXXX")
|
||||
TIDY_STATE_DIR=$(mktemp -d "TEMP-tidy-state-XXXXXXXXX")
|
||||
|
||||
trap "cp -p ${ORIGINAL_STATE_DIR}/* ./ && git update-index -q --refresh && rm -fR ${ORIGINAL_STATE_DIR} ${TIDY_STATE_DIR}" EXIT
|
||||
|
||||
# capturing original state of files...
|
||||
cp go.mod go.sum "${ORIGINAL_STATE_DIR}"
|
||||
|
||||
# capturing state of go.mod and go.sum after running go mod tidy...
|
||||
go mod tidy
|
||||
cp go.mod go.sum "${TIDY_STATE_DIR}"
|
||||
|
||||
set +e
|
||||
|
||||
# detect difference between the git HEAD state and the go mod tidy state
|
||||
DIFF_MOD=$(diff -u "${ORIGINAL_STATE_DIR}/go.mod" "${TIDY_STATE_DIR}/go.mod")
|
||||
DIFF_SUM=$(diff -u "${ORIGINAL_STATE_DIR}/go.sum" "${TIDY_STATE_DIR}/go.sum")
|
||||
|
||||
if [[ -n "${DIFF_MOD}" || -n "${DIFF_SUM}" ]]; then
|
||||
echo "go.mod diff:"
|
||||
echo "${DIFF_MOD}"
|
||||
echo "go.sum diff:"
|
||||
echo "${DIFF_SUM}"
|
||||
echo ""
|
||||
printf "FAILED! go.mod and/or go.sum are NOT tidy; please run 'go mod tidy'.\n\n"
|
||||
exit 1
|
||||
fi
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Remove orphan *.fingerprint files left behind by moved/deleted catalogers.
|
||||
|
||||
A fingerprint is considered orphaned when:
|
||||
1. its paired content path (the fingerprint path with `.fingerprint` stripped)
|
||||
does not exist, AND
|
||||
2. the nearest ancestor `testdata/` directory has no `Makefile` claiming
|
||||
responsibility for generating that path.
|
||||
|
||||
The second condition is the safety check: if there is a Makefile, the
|
||||
fingerprint is "live" and might just be waiting for fixtures to be built —
|
||||
leave it alone. Without a Makefile, nothing in-repo will ever regenerate
|
||||
the content, so the fingerprint is dead weight that triggers spurious
|
||||
"missing path" warnings.
|
||||
|
||||
Empty parent directories are also pruned after removing the fingerprint.
|
||||
|
||||
Use --dry-run to preview without deleting.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def find_ancestor_testdata(path: str) -> str | None:
|
||||
d = os.path.dirname(path)
|
||||
while d and d not in (".", os.sep):
|
||||
if os.path.basename(d) == "testdata":
|
||||
return d
|
||||
d = os.path.dirname(d)
|
||||
return None
|
||||
|
||||
|
||||
def is_orphan(fingerprint: str) -> bool:
|
||||
paired = fingerprint[: -len(".fingerprint")]
|
||||
if os.path.exists(paired):
|
||||
return False
|
||||
|
||||
testdata_dir = find_ancestor_testdata(fingerprint)
|
||||
if testdata_dir and os.path.isfile(os.path.join(testdata_dir, "Makefile")):
|
||||
# a Makefile exists that may regenerate this — not safe to prune
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def prune_empty_parents(start: str, stop_at: str = ".") -> list[str]:
|
||||
removed = []
|
||||
d = os.path.dirname(start)
|
||||
stop_at = os.path.abspath(stop_at)
|
||||
while d and os.path.abspath(d) != stop_at:
|
||||
try:
|
||||
if not os.listdir(d):
|
||||
os.rmdir(d)
|
||||
removed.append(d)
|
||||
d = os.path.dirname(d)
|
||||
else:
|
||||
break
|
||||
except OSError:
|
||||
break
|
||||
return removed
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument(
|
||||
"--dry-run",
|
||||
action="store_true",
|
||||
help="Show what would be removed without deleting anything",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
all_fingerprints = glob.glob("**/test*/**/*.fingerprint", recursive=True)
|
||||
orphans = sorted(fp for fp in all_fingerprints if is_orphan(fp))
|
||||
|
||||
if not orphans:
|
||||
print("no orphan fingerprints found")
|
||||
return 0
|
||||
|
||||
verb = "would remove" if args.dry_run else "removing"
|
||||
print(f"{verb} {len(orphans)} orphan fingerprint(s):")
|
||||
for fp in orphans:
|
||||
print(f"- {fp}")
|
||||
if args.dry_run:
|
||||
continue
|
||||
try:
|
||||
os.remove(fp)
|
||||
except OSError as e:
|
||||
print(f" ! failed to remove: {e}", file=sys.stderr)
|
||||
continue
|
||||
for d in prune_empty_parents(fp):
|
||||
print(f" (also removed empty dir {d})")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,57 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
|
||||
bold=$(tput bold)
|
||||
normal=$(tput sgr0)
|
||||
|
||||
GH_CLI=.tool/gh
|
||||
|
||||
if ! [ -x "$(command -v $GH_CLI)" ]; then
|
||||
echo "The GitHub CLI could not be found. run: make bootstrap"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
$GH_CLI auth status
|
||||
|
||||
# set the default repo in cases where multiple remotes are defined
|
||||
$GH_CLI repo set-default anchore/syft
|
||||
|
||||
export GITHUB_TOKEN="${GITHUB_TOKEN-"$($GH_CLI auth token)"}"
|
||||
|
||||
# we need all of the git state to determine the next version. Since tagging is done by
|
||||
# the release pipeline it is possible to not have all of the tags from previous releases.
|
||||
git fetch --tags
|
||||
|
||||
# populates the CHANGELOG.md and VERSION files
|
||||
echo "${bold}Generating changelog...${normal}"
|
||||
make changelog 2> /dev/null
|
||||
|
||||
NEXT_VERSION=$(cat VERSION)
|
||||
|
||||
if [[ "$NEXT_VERSION" == "" || "${NEXT_VERSION}" == "(Unreleased)" ]]; then
|
||||
echo "Could not determine the next version to release. Exiting..."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
while true; do
|
||||
read -p "${bold}Do you want to trigger a release for version '${NEXT_VERSION}'?${normal} [y/n] " yn
|
||||
case $yn in
|
||||
[Yy]* ) echo; break;;
|
||||
[Nn]* ) echo; echo "Cancelling release..."; exit;;
|
||||
* ) echo "Please answer yes or no.";;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "${bold}Kicking off release for ${NEXT_VERSION}${normal}..."
|
||||
echo
|
||||
$GH_CLI workflow run release.yaml -f version=${NEXT_VERSION}
|
||||
|
||||
echo
|
||||
echo "${bold}Waiting for release to start...${normal}"
|
||||
sleep 10
|
||||
|
||||
set +e
|
||||
|
||||
echo "${bold}Head to the release workflow to monitor the release:${normal} $($GH_CLI run list --workflow=release.yaml --limit=1 --json url --jq '.[].url')"
|
||||
id=$($GH_CLI run list --workflow=release.yaml --limit=1 --json databaseId --jq '.[].databaseId')
|
||||
$GH_CLI run watch $id --exit-status || (echo ; echo "${bold}Logs of failed step:${normal}" && GH_PAGER="" $GH_CLI run view $id --log-failed)
|
||||
@@ -1,6 +1,3 @@
|
||||
# CodeQL scans for security vulnerabilities and coding errors across all
|
||||
# languages in this repo. Results appear in the "Security" tab under
|
||||
# "Code scanning alerts" and are enforced by branch protection rules.
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
@@ -8,74 +5,17 @@ on:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
# Weekly scheduled scan catches newly disclosed vulnerabilities in
|
||||
# existing code, not just changes introduced by PRs.
|
||||
schedule:
|
||||
- cron: '38 11 * * 3'
|
||||
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze (${{ matrix.language }})
|
||||
runs-on: ubuntu-latest
|
||||
name: Analyze
|
||||
uses: anchore/workflows/.github/workflows/codeql.yaml@b0c30a80409130d329aaa356fd64a34d8c0b3375 # v0.7.2
|
||||
permissions:
|
||||
# Required to upload SARIF results to the "Security" tab.
|
||||
security-events: write
|
||||
# Required to fetch internal or private CodeQL packs.
|
||||
packages: read
|
||||
# Only required for workflows in private repositories.
|
||||
actions: read
|
||||
contents: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# GitHub Actions workflow linting — no build needed.
|
||||
- language: actions
|
||||
build-mode: none
|
||||
|
||||
# Go uses "manual" build mode so we control exactly what gets
|
||||
# compiled. The default "autobuild" finds the Makefile and runs
|
||||
# the full CI pipeline (lint, test, snapshot release, etc.),
|
||||
# which is far more work than CodeQL needs. All it requires is
|
||||
# compiled Go source so it can build a type-resolved code graph
|
||||
# for analysis.
|
||||
- language: go
|
||||
build-mode: manual
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# Pin the Go toolchain to whatever go.mod declares so CodeQL
|
||||
# analyzes with the same version the project actually uses.
|
||||
# Only runs for the Go matrix entry.
|
||||
- name: Setup Go
|
||||
if: matrix.language == 'go'
|
||||
uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
|
||||
# Minimal build for Go: compile all packages so CodeQL gets a full
|
||||
# type-resolved code graph for analysis.
|
||||
- name: Build (Go)
|
||||
if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
run: go build ./...
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@c10b8064de6f491fea524254123dbe5e09572f13 # v4.35.1
|
||||
with:
|
||||
# The category tag lets GitHub associate SARIF results with the
|
||||
# correct language when branch protection checks for required
|
||||
# code scanning results.
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
name: Dependabot Automation
|
||||
on:
|
||||
pull_request:
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
run:
|
||||
uses: anchore/workflows/.github/workflows/dependabot-automation.yaml@main
|
||||
@@ -1,64 +0,0 @@
|
||||
# Note: this workflow has been disabled manually in the UI and will be replaced in short order
|
||||
|
||||
name: "Detect schema changes"
|
||||
|
||||
on:
|
||||
# IMPORTANT! This workflow is triggered by the `pull_request_target` event
|
||||
# which means that forked PRs will run with access secrets from the repo
|
||||
# it's forked from (the "target" repo).
|
||||
#
|
||||
# For this reason we only NEVER checkout the code from the pull request
|
||||
# (e.g. "ref: ${{ github.event.pull_request.head.sha }}") to prevent
|
||||
# accidentally running potentially untrusted code.
|
||||
#
|
||||
# By default the checkout will be:
|
||||
# - GITHUB_SHA: Last commit on the PR base branch
|
||||
# - GITHUB_REF: PR base branch
|
||||
#
|
||||
# ...unlike a typical PR where:
|
||||
# - GITHUB_SHA: Last merge commit on the GITHUB_REF branch
|
||||
# - GITHUB_REF: PR merge branch refs/pull/:prNumber/merge
|
||||
pull_request_target:
|
||||
|
||||
env:
|
||||
# note: this is used within hashFiles() so must be within the GITHUB_WORKSPACE path (or will silently fail)
|
||||
CI_COMMENT_FILE: .tmp/labeler-comment.txt
|
||||
# needs to be any string to uniquely identify the comment on a PR across multiple runs
|
||||
COMMENT_HEADER: "label-commentary"
|
||||
|
||||
jobs:
|
||||
label:
|
||||
name: "Label changes"
|
||||
runs-on: ubuntu-22.04
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
issues: write
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
repository: anchore/syft # IMPORTANT! An additional protection that this is checking out code from the expected repository
|
||||
ref: main # IMPORTANT! It is CRITICAL that this only ever considers the code from main and NEVER EVER from a fork.
|
||||
|
||||
- run: python .github/scripts/labeler.py
|
||||
env:
|
||||
# note: this token has write access to the repo
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_PR_NUMBER: ${{ github.event.number }}
|
||||
|
||||
- name: Delete existing comment
|
||||
if: ${{ hashFiles( env.CI_COMMENT_FILE ) == '' }}
|
||||
uses: marocchino/sticky-pull-request-comment@d4d6b0936434b21bc8345ad45a440c5f7d2c40ff #v3.0.3
|
||||
with:
|
||||
header: ${{ env.COMMENT_HEADER }}
|
||||
hide: true
|
||||
hide_classify: "OUTDATED"
|
||||
|
||||
- name: Add comment
|
||||
if: ${{ hashFiles( env.CI_COMMENT_FILE ) != '' }}
|
||||
uses: marocchino/sticky-pull-request-comment@d4d6b0936434b21bc8345ad45a440c5f7d2c40ff #v3.0.3
|
||||
with:
|
||||
header: ${{ env.COMMENT_HEADER }}
|
||||
path: ${{ env.CI_COMMENT_FILE }}
|
||||
@@ -1,18 +0,0 @@
|
||||
name: Add to OSS board
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- transferred
|
||||
- labeled
|
||||
|
||||
jobs:
|
||||
run:
|
||||
uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main"
|
||||
secrets:
|
||||
token: ${{ secrets.OSS_PROJECT_GH_TOKEN }}
|
||||
+51
-147
@@ -1,7 +1,11 @@
|
||||
name: "Release"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
permissions: {}
|
||||
|
||||
# there should never be two releases in progress at the same time
|
||||
concurrency:
|
||||
group: release
|
||||
cancel-in-progress: false
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -17,118 +21,44 @@ on:
|
||||
options:
|
||||
- "all"
|
||||
- "install-script-only"
|
||||
skip-checks:
|
||||
description: skip the check-gate (release even if checks haven't passed on main)
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
|
||||
jobs:
|
||||
quality-gate:
|
||||
environment: release
|
||||
version-available:
|
||||
if: ${{ github.event.inputs.phase == 'all' }}
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
with:
|
||||
persist-credentials: false
|
||||
permissions:
|
||||
contents: read # required for fetching tags
|
||||
uses: anchore/workflows/.github/workflows/check-version-available.yaml@b0c30a80409130d329aaa356fd64a34d8c0b3375 # v0.7.2
|
||||
with:
|
||||
version: ${{ github.event.inputs.version }}
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Validate Apple notarization credentials
|
||||
run: .tool/quill submission list
|
||||
env:
|
||||
QUILL_NOTARY_ISSUER: ${{ secrets.APPLE_NOTARY_ISSUER }}
|
||||
QUILL_NOTARY_KEY_ID: ${{ secrets.APPLE_NOTARY_KEY_ID }}
|
||||
QUILL_NOTARY_KEY: ${{ secrets.APPLE_NOTARY_KEY }}
|
||||
|
||||
- name: Check if running on main
|
||||
if: github.ref != 'refs/heads/main'
|
||||
# we are using the following flag when running `cosign blob-verify` for checksum signature verification:
|
||||
# --certificate-identity-regexp "https://github.com/anchore/.github/workflows/release.yaml@refs/heads/main"
|
||||
# if we are not on the main branch, the signature will not be verifiable since the suffix requires the main branch
|
||||
# at the time of when the OIDC token was issued on the Github Actions runner.
|
||||
run: echo "This can only be run on the main branch otherwise releases produced will not be verifiable with cosign" && exit 1
|
||||
|
||||
- name: Check if tag already exists
|
||||
# note: this will fail if the tag already exists
|
||||
run: |
|
||||
[[ "$VERSION" == v* ]] || (echo "version '$VERSION' does not have a 'v' prefix" && exit 1)
|
||||
git tag "$VERSION"
|
||||
env:
|
||||
VERSION: ${{ github.event.inputs.version }}
|
||||
|
||||
- name: Check static analysis results
|
||||
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
|
||||
id: static-analysis
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
|
||||
checkName: "Static analysis"
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check unit test results
|
||||
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
|
||||
id: unit
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
|
||||
checkName: "Unit tests"
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check integration test results
|
||||
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
|
||||
id: integration
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
|
||||
checkName: "Integration tests"
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check acceptance test results (linux)
|
||||
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
|
||||
id: acceptance-linux
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
|
||||
checkName: "Acceptance tests (Linux)"
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check acceptance test results (mac)
|
||||
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
|
||||
id: acceptance-mac
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
|
||||
checkName: "Acceptance tests (Mac)"
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Check cli test results (linux)
|
||||
uses: fountainhead/action-wait-for-check@5a908a24814494009c4bb27c242ea38c93c593be # v1.2.0
|
||||
id: cli-linux
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
# This check name is defined as the github action job name (in .github/workflows/testing.yaml)
|
||||
checkName: "CLI tests (Linux)"
|
||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Quality gate
|
||||
if: steps.static-analysis.outputs.conclusion != 'success' || steps.unit.outputs.conclusion != 'success' || steps.integration.outputs.conclusion != 'success' || steps.cli-linux.outputs.conclusion != 'success' || steps.acceptance-linux.outputs.conclusion != 'success' || steps.acceptance-mac.outputs.conclusion != 'success'
|
||||
env:
|
||||
STATIC_ANALYSIS_STATUS: ${{ steps.static-analysis.conclusion }}
|
||||
UNIT_TEST_STATUS: ${{ steps.unit.outputs.conclusion }}
|
||||
INTEGRATION_TEST_STATUS: ${{ steps.integration.outputs.conclusion }}
|
||||
ACCEPTANCE_LINUX_STATUS: ${{ steps.acceptance-linux.outputs.conclusion }}
|
||||
ACCEPTANCE_MAC_STATUS: ${{ steps.acceptance-mac.outputs.conclusion }}
|
||||
CLI_LINUX_STATUS: ${{ steps.cli-linux.outputs.conclusion }}
|
||||
run: |
|
||||
echo "Static Analysis Status: $STATIC_ANALYSIS_STATUS"
|
||||
echo "Unit Test Status: $UNIT_TEST_STATUS"
|
||||
echo "Integration Test Status: $INTEGRATION_TEST_STATUS"
|
||||
echo "Acceptance Test (Linux) Status: $ACCEPTANCE_LINUX_STATUS"
|
||||
echo "Acceptance Test (Mac) Status: $ACCEPTANCE_MAC_STATUS"
|
||||
echo "CLI Test (Linux) Status: $CLI_LINUX_STATUS"
|
||||
false
|
||||
check-gate:
|
||||
if: ${{ github.event.inputs.phase == 'all' && !inputs.skip-checks }}
|
||||
permissions:
|
||||
contents: read
|
||||
checks: read # required for getting the status of specific check names
|
||||
uses: anchore/workflows/.github/workflows/check-gate.yaml@b0c30a80409130d329aaa356fd64a34d8c0b3375 # v0.7.2
|
||||
with:
|
||||
# these are checks that should be run on pull-request and merges to main.
|
||||
# we do NOT want to kick off a release if these have not been verified on main.
|
||||
# Please see the validations.yaml workflow for the names that should be used here.
|
||||
checks: '["Acceptance tests (Linux)", "Acceptance tests (Mac)", "Build snapshot artifacts", "CLI tests (Linux)", "Integration tests", "Static analysis", "Unit tests"]'
|
||||
|
||||
release:
|
||||
needs: [ quality-gate ]
|
||||
if: ${{ github.event.inputs.phase == 'all' }}
|
||||
needs: [check-gate, version-available]
|
||||
# run even when check-gate is skipped, but never when version-available
|
||||
# failed/was skipped, nor when check-gate failed or was cancelled. note:
|
||||
# always() disables the implicit success() gate on ALL needs, so the
|
||||
# version-available requirement must be re-asserted explicitly here.
|
||||
if: >-
|
||||
${{ always()
|
||||
&& needs.version-available.result == 'success'
|
||||
&& !contains(fromJSON('["failure", "cancelled"]'), needs.check-gate.result) }}
|
||||
environment: release
|
||||
# runs-on.com: compute instances for parallel builds
|
||||
# spot disabled: reliability for build workflows (used for releases too)
|
||||
# goreleaser uses parallelism of 12, so we need more CPUs
|
||||
@@ -136,12 +66,11 @@ jobs:
|
||||
# tmpfs: faster io-intensive workflows
|
||||
runs-on: runs-on=${{ github.run_id }}/cpu=16+32/ram=32+128/family=c5+c6+c7+c8/spot=false/extras=s3-cache+tmpfs
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
# required for goreleaser signs section with cosign
|
||||
id-token: write
|
||||
contents: write # required for creating the GitHub release and pushing the version tag
|
||||
packages: write # required for publishing release artifacts to GitHub packages
|
||||
id-token: write # required for keyless signing (cosign/sigstore OIDC)
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
persist-credentials: true
|
||||
@@ -150,31 +79,24 @@ jobs:
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 #v4.1.0
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee #v4.2.0
|
||||
with:
|
||||
username: ${{ secrets.ANCHOREOSSWRITE_DH_USERNAME }}
|
||||
password: ${{ secrets.ANCHOREOSSWRITE_DH_PAT }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 #v4.1.0
|
||||
uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee #v4.2.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Tag release
|
||||
run: |
|
||||
git config --global user.name "anchoreci"
|
||||
git config --global user.email "anchoreci@users.noreply.github.com"
|
||||
git tag -a "$VERSION" -m "Release $VERSION"
|
||||
git push origin --tags
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ github.event.inputs.version }}
|
||||
|
||||
- name: Build & publish release artifacts
|
||||
run: make ci-release
|
||||
env:
|
||||
# for pushing tags (does not inherit workflow permissions)
|
||||
TAG_TOKEN: ${{ secrets.TAG_TOKEN }}
|
||||
RELEASE_VERSION: ${{ github.event.inputs.version }}
|
||||
# for mac signing and notarization...
|
||||
QUILL_SIGN_P12: ${{ secrets.ANCHORE_APPLE_DEVELOPER_ID_CERT_CHAIN }}
|
||||
QUILL_SIGN_PASSWORD: ${{ secrets.ANCHORE_APPLE_DEVELOPER_ID_CERT_PASS }}
|
||||
@@ -192,30 +114,12 @@ jobs:
|
||||
file: go.mod
|
||||
artifact-name: sbom.spdx.json
|
||||
|
||||
- name: Notify Slack of new release
|
||||
uses: slackapi/slack-github-action@af78098f536edbc4de71162a307590698245be95 #v3.0.1
|
||||
continue-on-error: true
|
||||
with:
|
||||
webhook: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
|
||||
webhook-type: incoming-webhook
|
||||
payload: |
|
||||
text: "A new Syft release has been published: https://github.com/anchore/syft/releases/tag/${{ github.event.inputs.version }}"
|
||||
blocks:
|
||||
- type: section
|
||||
text:
|
||||
type: mrkdwn
|
||||
text: |
|
||||
*A new Syft release has been published* :rocket:
|
||||
• Release: <https://github.com/anchore/syft/releases/tag/${{ github.event.inputs.version }}|${{ github.event.inputs.version }}>
|
||||
• Repo: `${{ github.repository }}`
|
||||
• Workflow: `${{ github.workflow }}`
|
||||
• Event: `${{ github.event_name }}`
|
||||
if: ${{ success() }}
|
||||
|
||||
release-install-script:
|
||||
needs: [ release ]
|
||||
needs: [release]
|
||||
if: ${{ always() && (needs.release.result == 'success' || github.event.inputs.phase == 'install-script-only') }}
|
||||
uses: "anchore/workflows/.github/workflows/release-install-script.yaml@main"
|
||||
permissions:
|
||||
contents: read # required for the reusable workflow to check out the repo and publish the install script
|
||||
uses: anchore/workflows/.github/workflows/release-install-script.yaml@b0c30a80409130d329aaa356fd64a34d8c0b3375 # v0.7.2
|
||||
with:
|
||||
tag: ${{ github.event.inputs.version }}
|
||||
secrets:
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
name: "Manage Awaiting Response Label"
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: "anchore/workflows/.github/workflows/remove-awaiting-response-label.yaml@main"
|
||||
secrets:
|
||||
token: ${{ secrets.OSS_PROJECT_GH_TOKEN }}
|
||||
@@ -10,8 +10,7 @@ on:
|
||||
- '.github/workflows/**'
|
||||
- '.github/actions/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
zizmor:
|
||||
@@ -21,12 +20,12 @@ jobs:
|
||||
contents: read
|
||||
security-events: write # for uploading SARIF results
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run zizmor"
|
||||
uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2
|
||||
uses: zizmorcore/zizmor-action@5f14fd08f7cf1cb1609c1e344975f152c7ee938d # v0.5.6
|
||||
with:
|
||||
# there is a pass/fail gate as a repo ruleset (if there is no ruleset configured then the action will pass by default)
|
||||
advanced-security: true
|
||||
|
||||
@@ -13,8 +13,7 @@ on:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
permissions: {}
|
||||
|
||||
jobs:
|
||||
Static-Analysis:
|
||||
@@ -22,11 +21,14 @@ jobs:
|
||||
name: "Static analysis"
|
||||
# runs-on.com: memory & general purpose instances for testing
|
||||
# spot enabled: ok to interrupt non-production workloads
|
||||
# s3-cache: faster actions cache
|
||||
# tmpfs: faster io-intensive workflows
|
||||
runs-on: &test-runner "runs-on=${{ github.run_id }}/cpu=4+8/ram=32+128/family=r5+r6+r7+r8+m4+m5+m6+m7+m8/spot=price-capacity-optimized/extras=s3-cache+tmpfs"
|
||||
# note: s3-cache intentionally omitted -- PR runs are untrusted and must not write to the
|
||||
# shared cache backend that the trusted release workflow reads from (cache poisoning).
|
||||
runs-on: &test-runner "runs-on=${{ github.run_id }}/cpu=4+8/ram=32+128/family=r5+r6+r7+r8+m4+m5+m6+m7+m8/spot=price-capacity-optimized/extras=tmpfs"
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -45,8 +47,10 @@ jobs:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Unit tests"
|
||||
runs-on: *test-runner
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -65,8 +69,10 @@ jobs:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Integration tests"
|
||||
runs-on: *test-runner
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -86,14 +92,14 @@ jobs:
|
||||
# runs-on.com: compute instances for parallel builds
|
||||
# spot disabled: reliability for build workflows (used for releases too)
|
||||
# goreleaser uses parallelism of 12, so we need more CPUs
|
||||
# s3-cache: faster actions cache
|
||||
# tmpfs: faster io-intensive workflows
|
||||
runs-on: "runs-on=${{ github.run_id }}/cpu=16+32/ram=32+128/family=c5+c6+c7+c8/spot=false/extras=s3-cache+tmpfs"
|
||||
# note: s3-cache intentionally omitted -- PR runs are untrusted and must not write to the
|
||||
# shared cache backend that the trusted release workflow reads from (cache poisoning).
|
||||
runs-on: "runs-on=${{ github.run_id }}/cpu=16+32/ram=32+128/family=c5+c6+c7+c8/spot=false/extras=tmpfs"
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
# required for magic-cache from runs-on to function with artifact upload/download (see https://runs-on.com/caching/magic-cache/#actionsupload-artifact-compatibility)
|
||||
- uses: runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0 # v2.1.0
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -120,11 +126,10 @@ jobs:
|
||||
name: "Acceptance tests (Linux)"
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: *test-runner
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
# required for magic-cache from runs-on to function with artifact upload/download (see https://runs-on.com/caching/magic-cache/#actionsupload-artifact-compatibility)
|
||||
- uses: runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0 # v2.1.0
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -162,11 +167,13 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
# note: macos runners aren't supported yet for runs-on managed runners.
|
||||
runs-on: macos-latest
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@cad07c2e89fa2edd6e2d7bab4c1aa38e53f76003 # v4.1.1
|
||||
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6 # v4.1.2
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
@@ -197,11 +204,10 @@ jobs:
|
||||
name: "CLI tests (Linux)"
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: *test-runner
|
||||
permissions:
|
||||
contents: read
|
||||
steps:
|
||||
# required for magic-cache from runs-on to function with artifact upload/download (see https://runs-on.com/caching/magic-cache/#actionsupload-artifact-compatibility)
|
||||
- uses: runs-on/action@742bf56072eb4845a0f94b3394673e4903c90ff0 # v2.1.0
|
||||
|
||||
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #v6.0.2
|
||||
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 #v6.0.3
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
rules:
|
||||
unpinned-uses:
|
||||
config:
|
||||
policies:
|
||||
# anchore/workflows is an internal repository; using @main is acceptable
|
||||
anchore/*: any
|
||||
dangerous-triggers:
|
||||
ignore:
|
||||
# Safe use of pull_request_target - only runs trusted scripts from base repo,
|
||||
|
||||
+4
-2
@@ -3,6 +3,8 @@ go.work
|
||||
go.work.sum
|
||||
.tool-versions
|
||||
.python-version
|
||||
.mise.toml
|
||||
.env
|
||||
|
||||
# app configuration
|
||||
/.syft.yaml
|
||||
@@ -20,6 +22,8 @@ bin/
|
||||
/generate
|
||||
/specs
|
||||
mise.toml
|
||||
.make/.make
|
||||
.conductor
|
||||
|
||||
# changelog generation
|
||||
CHANGELOG.md
|
||||
@@ -76,5 +80,3 @@ cosign.pub
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
*$py.class
|
||||
|
||||
|
||||
|
||||
+8
-2
@@ -70,7 +70,6 @@ linters:
|
||||
# - godot
|
||||
# - godox
|
||||
# - goerr113
|
||||
# - goimports # we're using gosimports now instead to account for extra whitespaces (see https://github.com/golang/go/issues/20818)
|
||||
# - golint # deprecated
|
||||
# - gomnd # this is too aggressive
|
||||
# - interfacer # this is a good idea, but is no longer supported and is prone to false positives
|
||||
@@ -96,11 +95,18 @@ issues:
|
||||
|
||||
formatters:
|
||||
enable:
|
||||
- gci
|
||||
- gofmt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
settings:
|
||||
gci:
|
||||
# See https://golangci-lint.run/docs/formatters/configuration/#gci
|
||||
sections:
|
||||
- standard # Standard section: captures all standard packages.
|
||||
- default # Default section: contains all imports that could not be matched to another section type.
|
||||
- prefix(github.com/anchore)
|
||||
|
||||
+55
-1
@@ -14,7 +14,7 @@ builds:
|
||||
dir: ./cmd/syft
|
||||
binary: syft
|
||||
goos: [linux]
|
||||
goarch: [amd64, arm64, ppc64le, s390x]
|
||||
goarch: [amd64, arm64, ppc64le, riscv64, s390x]
|
||||
mod_timestamp: &build-timestamp '{{ .CommitTimestamp }}'
|
||||
ldflags: &build-ldflags |
|
||||
-w
|
||||
@@ -114,6 +114,20 @@ dockers:
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
- image_templates:
|
||||
- anchore/syft:{{.Tag}}-riscv64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-riscv64
|
||||
goarch: riscv64
|
||||
dockerfile: Dockerfile
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/riscv64"
|
||||
- "--build-arg=DEBIAN_VERSION=13"
|
||||
- "--build-arg=BUILD_DATE={{.Date}}"
|
||||
- "--build-arg=BUILD_VERSION={{.Version}}"
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
- image_templates:
|
||||
- anchore/syft:{{.Tag}}-s390x
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-s390x
|
||||
@@ -167,6 +181,20 @@ dockers:
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
- image_templates:
|
||||
- anchore/syft:{{.Tag}}-nonroot-riscv64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-riscv64
|
||||
goarch: riscv64
|
||||
dockerfile: Dockerfile.nonroot
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/riscv64"
|
||||
- "--build-arg=DEBIAN_VERSION=13"
|
||||
- "--build-arg=BUILD_DATE={{.Date}}"
|
||||
- "--build-arg=BUILD_VERSION={{.Version}}"
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
- image_templates:
|
||||
- anchore/syft:{{.Tag}}-nonroot-s390x
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-s390x
|
||||
@@ -220,6 +248,20 @@ dockers:
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
- image_templates:
|
||||
- anchore/syft:{{.Tag}}-debug-riscv64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-riscv64
|
||||
goarch: riscv64
|
||||
dockerfile: Dockerfile.debug
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--platform=linux/riscv64"
|
||||
- "--build-arg=DEBIAN_VERSION=13"
|
||||
- "--build-arg=BUILD_DATE={{.Date}}"
|
||||
- "--build-arg=BUILD_VERSION={{.Version}}"
|
||||
- "--build-arg=VCS_REF={{.FullCommit}}"
|
||||
- "--build-arg=VCS_URL={{.GitURL}}"
|
||||
|
||||
- image_templates:
|
||||
- anchore/syft:{{.Tag}}-debug-s390x
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
|
||||
@@ -239,6 +281,7 @@ docker_manifests:
|
||||
- anchore/syft:{{.Tag}}-amd64
|
||||
- anchore/syft:{{.Tag}}-arm64v8
|
||||
- anchore/syft:{{.Tag}}-ppc64le
|
||||
- anchore/syft:{{.Tag}}-riscv64
|
||||
- anchore/syft:{{.Tag}}-s390x
|
||||
|
||||
- name_template: ghcr.io/anchore/syft:latest
|
||||
@@ -246,6 +289,7 @@ docker_manifests:
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-amd64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-arm64v8
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-ppc64le
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-riscv64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-s390x
|
||||
|
||||
- name_template: anchore/syft:{{.Tag}}
|
||||
@@ -253,6 +297,7 @@ docker_manifests:
|
||||
- anchore/syft:{{.Tag}}-amd64
|
||||
- anchore/syft:{{.Tag}}-arm64v8
|
||||
- anchore/syft:{{.Tag}}-ppc64le
|
||||
- anchore/syft:{{.Tag}}-riscv64
|
||||
- anchore/syft:{{.Tag}}-s390x
|
||||
|
||||
- name_template: ghcr.io/anchore/syft:{{.Tag}}
|
||||
@@ -260,6 +305,7 @@ docker_manifests:
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-amd64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-arm64v8
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-ppc64le
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-riscv64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-s390x
|
||||
|
||||
# nonroot images...
|
||||
@@ -268,6 +314,7 @@ docker_manifests:
|
||||
- anchore/syft:{{.Tag}}-nonroot-amd64
|
||||
- anchore/syft:{{.Tag}}-nonroot-arm64v8
|
||||
- anchore/syft:{{.Tag}}-nonroot-ppc64le
|
||||
- anchore/syft:{{.Tag}}-nonroot-riscv64
|
||||
- anchore/syft:{{.Tag}}-nonroot-s390x
|
||||
|
||||
- name_template: ghcr.io/anchore/syft:nonroot
|
||||
@@ -275,6 +322,7 @@ docker_manifests:
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-amd64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-arm64v8
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-ppc64le
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-riscv64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-s390x
|
||||
|
||||
- name_template: anchore/syft:{{.Tag}}-nonroot
|
||||
@@ -282,6 +330,7 @@ docker_manifests:
|
||||
- anchore/syft:{{.Tag}}-nonroot-amd64
|
||||
- anchore/syft:{{.Tag}}-nonroot-arm64v8
|
||||
- anchore/syft:{{.Tag}}-nonroot-ppc64le
|
||||
- anchore/syft:{{.Tag}}-nonroot-riscv64
|
||||
- anchore/syft:{{.Tag}}-nonroot-s390x
|
||||
|
||||
- name_template: ghcr.io/anchore/syft:{{.Tag}}-nonroot
|
||||
@@ -289,6 +338,7 @@ docker_manifests:
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-amd64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-arm64v8
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-ppc64le
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-riscv64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-nonroot-s390x
|
||||
|
||||
# debug images...
|
||||
@@ -297,6 +347,7 @@ docker_manifests:
|
||||
- anchore/syft:{{.Tag}}-debug-amd64
|
||||
- anchore/syft:{{.Tag}}-debug-arm64v8
|
||||
- anchore/syft:{{.Tag}}-debug-ppc64le
|
||||
- anchore/syft:{{.Tag}}-debug-riscv64
|
||||
- anchore/syft:{{.Tag}}-debug-s390x
|
||||
|
||||
- name_template: ghcr.io/anchore/syft:debug
|
||||
@@ -304,6 +355,7 @@ docker_manifests:
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-amd64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-riscv64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
|
||||
|
||||
- name_template: anchore/syft:{{.Tag}}-debug
|
||||
@@ -311,6 +363,7 @@ docker_manifests:
|
||||
- anchore/syft:{{.Tag}}-debug-amd64
|
||||
- anchore/syft:{{.Tag}}-debug-arm64v8
|
||||
- anchore/syft:{{.Tag}}-debug-ppc64le
|
||||
- anchore/syft:{{.Tag}}-debug-riscv64
|
||||
- anchore/syft:{{.Tag}}-debug-s390x
|
||||
|
||||
- name_template: ghcr.io/anchore/syft:{{.Tag}}-debug
|
||||
@@ -318,6 +371,7 @@ docker_manifests:
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-amd64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-arm64v8
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-ppc64le
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-riscv64
|
||||
- ghcr.io/anchore/syft:{{.Tag}}-debug-s390x
|
||||
|
||||
sboms:
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
module github.com/anchore/syft/.make
|
||||
|
||||
go 1.25.8
|
||||
|
||||
require github.com/anchore/go-make v0.8.0
|
||||
|
||||
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
|
||||
)
|
||||
@@ -0,0 +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/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=
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
||||
. "github.com/anchore/go-make"
|
||||
"github.com/anchore/go-make/file"
|
||||
"github.com/anchore/go-make/lang"
|
||||
"github.com/anchore/go-make/run"
|
||||
"github.com/anchore/go-make/tasks/golint"
|
||||
"github.com/anchore/go-make/tasks/goreleaser"
|
||||
"github.com/anchore/go-make/tasks/gotask"
|
||||
"github.com/anchore/go-make/tasks/gotest"
|
||||
)
|
||||
|
||||
func main() {
|
||||
Makefile(
|
||||
// shared anchore tasks
|
||||
golint.Tasks(),
|
||||
goreleaser.Tasks(),
|
||||
|
||||
// unit tests: exclude packages under any test/ directory (matches the syft
|
||||
// Taskfile's prior `grep -v` against test paths). Coverage threshold of 62%
|
||||
// preserves the prior coverage gate that used to live in scripts/coverage.py.
|
||||
gotest.Tasks(
|
||||
gotest.Name("unit"),
|
||||
gotest.ExcludeGlob("**/test/**"),
|
||||
gotest.CoverageThreshold(62),
|
||||
),
|
||||
|
||||
// integration tests: native go-make Task. The race-detector smoke against a
|
||||
// real image stays bundled here (RunsOn integration) so `make integration`
|
||||
// behaves like the Taskfile version did.
|
||||
gotest.Tasks(
|
||||
gotest.Name("integration"),
|
||||
gotest.IncludeGlob("./cmd/syft/internal/test/integration/..."),
|
||||
gotest.Verbose(),
|
||||
gotest.NoCoverage(),
|
||||
),
|
||||
Task{
|
||||
Name: "integration:race-smoke",
|
||||
Description: "exercise the CLI with the race detector",
|
||||
RunsOn: lang.List("integration"),
|
||||
Run: func() {
|
||||
Run("go run -race cmd/syft/main.go anchore/test_images:grype-quality-dotnet-69f15d2")
|
||||
},
|
||||
},
|
||||
|
||||
// cli tests: native go-make Task. Runs SYFT_BINARY_LOCATION at an *absolute*
|
||||
// path to the snapshot binary. Builds the snapshot only when the binary is
|
||||
// missing rather than depending on the snapshot task unconditionally: in
|
||||
// validations.yaml CI we download a pre-built snapshot artifact, so the binary
|
||||
// already exists and rebuilding would both burn ~10m and clobber it. When
|
||||
// `make test` runs cold (e.g. the release pipeline) or locally with no
|
||||
// snapshot, we build a single-target snapshot (current OS/arch only) since
|
||||
// that's all the CLI tests need.
|
||||
Task{
|
||||
Name: "cli",
|
||||
Description: "Run CLI tests",
|
||||
RunsOn: lang.List("test"),
|
||||
Run: func() {
|
||||
bin := snapshotBinPath()
|
||||
if !file.Exists(bin) {
|
||||
Log("snapshot binary not found at %s; building single-target snapshot", bin)
|
||||
Run("make snapshot:single-target")
|
||||
}
|
||||
Log("testing binary: %s", bin)
|
||||
Run(
|
||||
"go test -count=1 -timeout=15m -v ./test/cli",
|
||||
run.Env("SYFT_BINARY_LOCATION", bin),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
// default validation pipeline (replaces Taskfile `default`/`pr-validations`/`validations`).
|
||||
Task{
|
||||
Name: "default",
|
||||
Description: "Run all validation tasks",
|
||||
Dependencies: Deps("static-analysis", "test", "install-test"),
|
||||
},
|
||||
|
||||
// --- everything else is implemented in Taskfile.yaml. gotask.Tasks()
|
||||
// discovers every (non-internal) Taskfile task — including the namespaced
|
||||
// `generate:cpe-index:*` tasks from the included task.d file — and surfaces
|
||||
// them as first-class go-make tasks (with their canonical names and `desc:`)
|
||||
// that forward to `task <name>`. Descriptions live in Taskfile.yaml as the
|
||||
// single source of truth; no per-task wrapping needed here.
|
||||
gotask.Tasks(),
|
||||
|
||||
// gotask.Tasks() discovers canonical task names only, not Taskfile aliases,
|
||||
// so re-expose `refresh-fixtures`'s `fixtures` alias for manual use.
|
||||
Task{
|
||||
Name: "fixtures",
|
||||
Description: "Clear and fetch all test fixture cache (alias of refresh-fixtures)",
|
||||
Dependencies: Deps("refresh-fixtures"),
|
||||
},
|
||||
|
||||
// gotask.Tasks() can't attach RunsOn labels, so wire the syft-specific
|
||||
// Taskfile tasks into go-make's native phases here. These thin hooks have
|
||||
// no body and no description (hidden from `make help`); they only pull the
|
||||
// discovered tasks in when the labeled phase runs.
|
||||
Task{
|
||||
Name: "static-analysis:syft",
|
||||
RunsOn: lang.List("static-analysis"),
|
||||
Dependencies: Deps("check-json-schema-drift", "check-binary-fixture-size"),
|
||||
},
|
||||
Task{
|
||||
Name: "test:syft",
|
||||
RunsOn: lang.List("test"),
|
||||
Dependencies: Deps("validate-cyclonedx-schema", "test-utils", "check-docker-cache"),
|
||||
},
|
||||
// refresh-fixtures hooks into "unit" so `make unit` triggers the stale-cache
|
||||
// detection + download just like `task unit` did on main (its
|
||||
// `deps: [tmpdir, fixtures]` is what kept the fixture cache fresh).
|
||||
Task{
|
||||
Name: "unit:syft",
|
||||
RunsOn: lang.List("unit"),
|
||||
Dependencies: Deps("refresh-fixtures"),
|
||||
},
|
||||
Task{
|
||||
Name: "clean:syft",
|
||||
RunsOn: lang.List("clean"),
|
||||
Dependencies: Deps(
|
||||
"clean-snapshot",
|
||||
"clean-docker-cache",
|
||||
"clean-oras-cache",
|
||||
"clean-cache",
|
||||
"clean-test-observations",
|
||||
),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// snapshotBinPath replicates the SNAPSHOT_BIN computation from the prior Taskfile:
|
||||
// <repoRoot>/snapshot/<os>-build_<os>_<arch>/syft, where arch maps amd64->amd64_v1
|
||||
// and arm64->arm64_v8.0 to match goreleaser's per-target output directory naming.
|
||||
// Returns an absolute path: the cli tests' getSyftBinaryLocation contract requires
|
||||
// SYFT_BINARY_LOCATION to be absolute because subtests run with cmd.Dir = t.TempDir().
|
||||
func snapshotBinPath() string {
|
||||
osName := runtime.GOOS
|
||||
var arch string
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
arch = "amd64_v1"
|
||||
case "arm64":
|
||||
arch = "arm64_v8.0"
|
||||
default:
|
||||
arch = runtime.GOARCH
|
||||
}
|
||||
return filepath.Join(RootDir(), "snapshot", osName+"-build_"+osName+"_"+arch, "syft")
|
||||
}
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
FROM gcr.io/distroless/static-debian12:latest AS build
|
||||
ARG DEBIAN_VERSION=12
|
||||
FROM gcr.io/distroless/static-debian${DEBIAN_VERSION}:latest AS build
|
||||
|
||||
FROM scratch
|
||||
# needed for version check HTTPS request
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
FROM gcr.io/distroless/static-debian12:debug-nonroot
|
||||
ARG DEBIAN_VERSION=12
|
||||
FROM gcr.io/distroless/static-debian${DEBIAN_VERSION}:debug-nonroot
|
||||
|
||||
# create the /tmp dir, which is needed for image content cache
|
||||
WORKDIR /tmp
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
FROM gcr.io/distroless/static-debian12:nonroot
|
||||
ARG DEBIAN_VERSION=12
|
||||
FROM gcr.io/distroless/static-debian${DEBIAN_VERSION}:nonroot
|
||||
|
||||
# create the /tmp dir, which is needed for image content cache
|
||||
WORKDIR /tmp
|
||||
|
||||
@@ -1,46 +1,18 @@
|
||||
OWNER = anchore
|
||||
PROJECT = syft
|
||||
# `test` and `snapshot` have matching directory names in this repo, so make would
|
||||
# refuse to run them without an explicit .PHONY (Nothing to be done for ...).
|
||||
.PHONY: test snapshot
|
||||
test:
|
||||
@go run -C .make . test
|
||||
|
||||
TOOL_DIR = .tool
|
||||
BINNY = $(TOOL_DIR)/binny
|
||||
TASK = $(TOOL_DIR)/task
|
||||
snapshot:
|
||||
@go run -C .make . snapshot
|
||||
|
||||
.DEFAULT_GOAL := make-default
|
||||
.PHONY: *
|
||||
.DEFAULT_GOAL: make-default
|
||||
|
||||
## Bootstrapping targets #################################
|
||||
make-default:
|
||||
@go run -C .make .
|
||||
|
||||
# note: we need to assume that binny and task have not already been installed
|
||||
$(BINNY):
|
||||
@mkdir -p $(TOOL_DIR)
|
||||
@curl -sSfL https://get.anchore.io/binny | sh -s -- -b $(TOOL_DIR)
|
||||
|
||||
# note: we need to assume that binny and task have not already been installed
|
||||
.PHONY: task
|
||||
$(TASK) task: $(BINNY)
|
||||
@$(BINNY) install task -q
|
||||
|
||||
.PHONY: ci-bootstrap-go
|
||||
ci-bootstrap-go:
|
||||
go mod download
|
||||
|
||||
# this is a bootstrapping catch-all, where if the target doesn't exist, we'll ensure the tools are installed and then try again
|
||||
.DEFAULT:
|
||||
%:
|
||||
@make --silent $(TASK)
|
||||
@$(TASK) $@
|
||||
|
||||
## Shim targets #################################
|
||||
|
||||
.PHONY: make-default
|
||||
make-default: $(TASK)
|
||||
@# run the default task in the taskfile
|
||||
@$(TASK)
|
||||
|
||||
# for those of us that can't seem to kick the habit of typing `make ...` lets wrap the superior `task` tool
|
||||
TASKS := $(shell bash -c "test -f $(TASK) && NO_COLOR=1 $(TASK) -l | grep '^\* ' | cut -d' ' -f2 | tr -d ':' | tr '\n' ' '" ) $(shell bash -c "test -f $(TASK) && NO_COLOR=1 $(TASK) -l | grep 'aliases:' | cut -d ':' -f 3 | tr '\n' ' ' | tr -d ','")
|
||||
|
||||
.PHONY: $(TASKS)
|
||||
$(TASKS): $(TASK)
|
||||
@$(TASK) $@
|
||||
|
||||
help: $(TASK)
|
||||
@$(TASK) -l
|
||||
@go run -C .make . $@
|
||||
|
||||
+48
-262
@@ -1,6 +1,11 @@
|
||||
|
||||
version: "3"
|
||||
|
||||
# NOTE: most generic tasks (static-analysis, format, lint, unit, snapshot, release,
|
||||
# changelog, ci-release, etc.) are now provided natively by anchore/go-make and
|
||||
# defined in .make/main.go. This file holds the syft-specific tasks, which go-make
|
||||
# auto-discovers via gotask.Tasks() and surfaces as `make <name>` — keep
|
||||
# descriptions (`desc:`) populated so they show up in `make help`.
|
||||
|
||||
includes:
|
||||
generate:cpe-index: ./task.d/generate/cpe-index.yaml
|
||||
|
||||
@@ -25,11 +30,7 @@ vars:
|
||||
YQ: "{{ .TOOL_DIR }}/yq"
|
||||
TASK: "{{ .TOOL_DIR }}/task"
|
||||
|
||||
# used for changelog generation
|
||||
CHANGELOG: CHANGELOG.md
|
||||
NEXT_VERSION: VERSION
|
||||
|
||||
# used for snapshot builds
|
||||
# used for snapshot bin discovery in compare/install tasks
|
||||
OS:
|
||||
sh: uname -s | tr '[:upper:]' '[:lower:]'
|
||||
ARCH:
|
||||
@@ -42,11 +43,6 @@ vars:
|
||||
# e.g. when installing snapshot debs from a local path, ./ forces the deb to be installed in the current working directory instead of referencing a package name
|
||||
SNAPSHOT_DIR: ./snapshot
|
||||
SNAPSHOT_BIN: "{{ .PROJECT_ROOT }}/{{ .SNAPSHOT_DIR }}/{{ .OS }}-build_{{ .OS }}_{{ .ARCH }}/{{ .PROJECT }}"
|
||||
SNAPSHOT_CMD: "{{ .TOOL_DIR }}/goreleaser release --config {{ .TMP_DIR }}/goreleaser.yaml --clean --snapshot --skip=publish --skip=sign"
|
||||
BUILD_CMD: "{{ .TOOL_DIR }}/goreleaser build --config {{ .TMP_DIR }}/goreleaser.yaml --clean --snapshot --single-target"
|
||||
RELEASE_CMD: "{{ .TOOL_DIR }}/goreleaser release --clean --release-notes {{ .CHANGELOG }}"
|
||||
VERSION:
|
||||
sh: git describe --dirty --always --tags
|
||||
|
||||
# used for install and acceptance testing
|
||||
COMPARE_DIR: ./test/compare
|
||||
@@ -57,43 +53,10 @@ env:
|
||||
|
||||
tasks:
|
||||
|
||||
## High-level tasks #################################
|
||||
|
||||
default:
|
||||
desc: Run all validation tasks
|
||||
aliases:
|
||||
- pr-validations
|
||||
- validations
|
||||
cmds:
|
||||
- task: static-analysis
|
||||
- task: test
|
||||
- task: install-test
|
||||
|
||||
static-analysis:
|
||||
desc: Run all static analysis tasks
|
||||
cmds:
|
||||
- task: check-go-mod-tidy
|
||||
- task: check-licenses
|
||||
- task: lint
|
||||
- task: check-json-schema-drift
|
||||
- task: check-binary-fixture-size
|
||||
|
||||
test:
|
||||
desc: Run all levels of test
|
||||
cmds:
|
||||
- task: unit
|
||||
- task: integration
|
||||
- task: validate-cyclonedx-schema
|
||||
- task: test-utils
|
||||
- task: snapshot
|
||||
- task: cli
|
||||
- task: check-docker-cache
|
||||
|
||||
## Bootstrap tasks #################################
|
||||
## Bootstrap (internal helpers used by other Taskfile tasks) ###############
|
||||
|
||||
binny:
|
||||
internal: true
|
||||
# desc: Get the binny tool
|
||||
generates:
|
||||
- "{{ .TOOL_DIR }}/binny"
|
||||
status:
|
||||
@@ -102,10 +65,8 @@ tasks:
|
||||
silent: true
|
||||
|
||||
tools:
|
||||
desc: Install all tools needed for CI and local development
|
||||
internal: true
|
||||
deps: [binny]
|
||||
aliases:
|
||||
- bootstrap
|
||||
generates:
|
||||
- ".binny.yaml"
|
||||
- "{{ .TOOL_DIR }}/*"
|
||||
@@ -114,79 +75,14 @@ tasks:
|
||||
cmd: "{{ .TOOL_DIR }}/binny install -v"
|
||||
silent: true
|
||||
|
||||
update-tools:
|
||||
desc: Update pinned versions of all tools to their latest available versions
|
||||
deps: [binny]
|
||||
generates:
|
||||
- ".binny.yaml"
|
||||
- "{{ .TOOL_DIR }}/*"
|
||||
cmd: "{{ .TOOL_DIR }}/binny update -v"
|
||||
silent: true
|
||||
|
||||
list-tools:
|
||||
desc: List all tools needed for CI and local development
|
||||
deps: [binny]
|
||||
cmd: "{{ .TOOL_DIR }}/binny list"
|
||||
silent: true
|
||||
|
||||
list-tool-updates:
|
||||
desc: List all tools that are not up to date relative to the binny config
|
||||
deps: [binny]
|
||||
cmd: "{{ .TOOL_DIR }}/binny list --updates"
|
||||
silent: true
|
||||
|
||||
tmpdir:
|
||||
internal: true
|
||||
silent: true
|
||||
generates:
|
||||
- "{{ .TMP_DIR }}"
|
||||
cmd: "mkdir -p {{ .TMP_DIR }}"
|
||||
|
||||
## Static analysis tasks #################################
|
||||
|
||||
format:
|
||||
desc: Auto-format all source code
|
||||
deps: [tools]
|
||||
cmds:
|
||||
- gofmt -w -s .
|
||||
- "{{ .TOOL_DIR }}/gosimports -local github.com/anchore -w ."
|
||||
- go mod tidy
|
||||
|
||||
lint-fix:
|
||||
desc: Auto-format all source code + run golangci lint fixers
|
||||
deps: [tools]
|
||||
cmds:
|
||||
- task: format
|
||||
- "{{ .TOOL_DIR }}/golangci-lint run --tests=false --fix"
|
||||
|
||||
lint:
|
||||
desc: Run gofmt + golangci lint checks
|
||||
vars:
|
||||
BAD_FMT_FILES:
|
||||
sh: gofmt -l -s .
|
||||
BAD_FILE_NAMES:
|
||||
sh: "find . | grep -e ':' || true"
|
||||
deps: [tools]
|
||||
cmds:
|
||||
# ensure there are no go fmt differences
|
||||
- cmd: 'test -z "{{ .BAD_FMT_FILES }}" || (echo "files with gofmt issues: [{{ .BAD_FMT_FILES }}]"; exit 1)'
|
||||
silent: true
|
||||
# ensure there are no files with ":" in it (a known back case in the go ecosystem)
|
||||
- cmd: 'test -z "{{ .BAD_FILE_NAMES }}" || (echo "files with bad names: [{{ .BAD_FILE_NAMES }}]"; exit 1)'
|
||||
silent: true
|
||||
# run linting
|
||||
- "{{ .TOOL_DIR }}/golangci-lint run --tests=false"
|
||||
|
||||
|
||||
check-licenses:
|
||||
# desc: Ensure transitive dependencies are compliant with the current license policy
|
||||
deps: [tools]
|
||||
cmd: "{{ .TOOL_DIR }}/bouncer check ./..."
|
||||
|
||||
check-go-mod-tidy:
|
||||
# desc: Ensure go.mod and go.sum are up to date
|
||||
cmds:
|
||||
- cmd: .github/scripts/go-mod-tidy-check.sh && echo "go.mod and go.sum are tidy!"
|
||||
silent: true
|
||||
## Static analysis extras #################################################
|
||||
|
||||
check-json-schema-drift:
|
||||
desc: Ensure there is no drift between the JSON schema and the code
|
||||
@@ -203,8 +99,8 @@ tasks:
|
||||
cmds:
|
||||
- .github/scripts/check_binary_fixture_size.sh syft/pkg/cataloger/binary/testdata/classifiers/snippets
|
||||
|
||||
## Test extras ############################################################
|
||||
|
||||
## Testing tasks #################################
|
||||
update-format-golden-files:
|
||||
desc: "Update golden (i.e. snapshot) files used by unit tests"
|
||||
cmds:
|
||||
@@ -214,60 +110,34 @@ tasks:
|
||||
- go test ./syft/format/cyclonedxjson -update-cyclonedx-json
|
||||
- go test ./syft/format/syftjson -update-json
|
||||
|
||||
unit:
|
||||
desc: Run unit tests
|
||||
deps:
|
||||
- tmpdir
|
||||
- fixtures
|
||||
vars:
|
||||
TEST_PKGS:
|
||||
sh: "go list ./... | grep -v {{ .OWNER }}/{{ .PROJECT }}/test | grep -v {{ .OWNER }}/{{ .PROJECT }}/cmd/syft/internal/test | tr '\n' ' '"
|
||||
|
||||
# unit test coverage threshold (in % coverage)
|
||||
COVERAGE_THRESHOLD: 62
|
||||
cmds:
|
||||
- task: clean-test-observations
|
||||
- "go test -coverprofile {{ .TMP_DIR }}/unit-coverage-details.txt {{ .TEST_PKGS }}"
|
||||
- cmd: ".github/scripts/coverage.py {{ .COVERAGE_THRESHOLD }} {{ .TMP_DIR }}/unit-coverage-details.txt"
|
||||
silent: true
|
||||
|
||||
integration:
|
||||
desc: Run integration tests
|
||||
cmds:
|
||||
- "go test -v ./cmd/syft/internal/test/integration"
|
||||
# exercise most of the CLI with the data race detector
|
||||
# we use a larger image to ensure we're using multiple catalogers at a time
|
||||
- "go run -race cmd/syft/main.go anchore/test_images:grype-quality-dotnet-69f15d2"
|
||||
|
||||
validate-cyclonedx-schema:
|
||||
desc: Validate that Syft produces valid CycloneDX documents
|
||||
cmds:
|
||||
- "cd schema/cyclonedx && make"
|
||||
|
||||
cli:
|
||||
desc: Run CLI tests
|
||||
deps: [tools]
|
||||
cmds:
|
||||
- cmd: "echo 'testing binary: {{ .SNAPSHOT_BIN }}'"
|
||||
silent: true
|
||||
- cmd: "test -f {{ .SNAPSHOT_BIN }} || (find {{ .SNAPSHOT_DIR }} && echo '\nno snapshot found for {{ .SNAPSHOT_BIN }}' && false)"
|
||||
silent: true
|
||||
|
||||
- "go test -count=1 -timeout=15m -v ./test/cli"
|
||||
env:
|
||||
SYFT_BINARY_LOCATION: "{{ .SNAPSHOT_BIN }}"
|
||||
|
||||
test-utils:
|
||||
desc: Run tests for pipeline utils
|
||||
cmds:
|
||||
- cmd: .github/scripts/labeler_test.py
|
||||
|
||||
snapshot-smoke-test:
|
||||
desc: Run a smoke test on the snapshot builds + docker images
|
||||
cmds:
|
||||
- cmd: "echo 'testing snapshot binary: {{ .SNAPSHOT_BIN }}'"
|
||||
silent: true
|
||||
- cmd: "test -f {{ .SNAPSHOT_BIN }} || (find {{ .SNAPSHOT_DIR }} && echo '\nno snapshot found for {{ .SNAPSHOT_BIN }}' && false)"
|
||||
silent: true
|
||||
- "{{ .SNAPSHOT_BIN }} version"
|
||||
- "{{ .SNAPSHOT_BIN }} scan alpine:latest"
|
||||
- docker run --rm anchore/syft:latest version
|
||||
- docker run --rm anchore/syft:latest scan alpine:latest
|
||||
|
||||
## Test-fixture-related targets #################################
|
||||
## Test-fixture-related targets ###########################################
|
||||
|
||||
fingerprints:
|
||||
desc: Generate fingerprints for all non-docker test fixture
|
||||
desc: Generate fingerprints for all non-docker test fixtures
|
||||
silent: true
|
||||
deps: [tmpdir]
|
||||
# this will look for `testdata/Makefile` and invoke the `fingerprint` target to calculate all cache input fingerprint files
|
||||
generates:
|
||||
- '**/testdata/**/*.fingerprint'
|
||||
@@ -281,16 +151,10 @@ tasks:
|
||||
echo -e "${YELLOW}creating fingerprint files for non-docker fixtures...${RESET}"
|
||||
for dir in $(find . -type d -name 'testdata'); do
|
||||
if [ -f "$dir/Makefile" ]; then
|
||||
# for debugging...
|
||||
#echo -e "${YELLOW}• calculating fingerprints in $dir... ${RESET}"
|
||||
|
||||
(make -C "$dir" fingerprint)
|
||||
fi
|
||||
done
|
||||
|
||||
# for debugging...
|
||||
# echo -e "generated all fixture fingerprints"
|
||||
|
||||
- .github/scripts/fingerprint_docker_fixtures.py
|
||||
- |
|
||||
# if DOWNLOAD_TEST_FIXTURE_CACHE is set to 'false', then we don't need to calculate the fingerprint for the cache
|
||||
@@ -432,6 +296,7 @@ tasks:
|
||||
eval $oras_command
|
||||
|
||||
show-test-image-cache:
|
||||
desc: Print the on-disk + docker daemon state of the stereoscope fixture image cache
|
||||
silent: true
|
||||
cmds:
|
||||
- "echo 'Docker daemon cache:'"
|
||||
@@ -452,28 +317,36 @@ tasks:
|
||||
exit 1
|
||||
fi
|
||||
|
||||
## install.sh testing targets #################################
|
||||
## install.sh testing targets #############################################
|
||||
|
||||
install-test:
|
||||
desc: Run install.sh test suite (delegates to test/install/Makefile)
|
||||
cmds:
|
||||
- "cd test/install && make"
|
||||
|
||||
install-test-cache-save:
|
||||
desc: Save the install.sh test image cache (delegates to test/install/Makefile)
|
||||
cmds:
|
||||
- "cd test/install && make save"
|
||||
|
||||
install-test-cache-load:
|
||||
desc: Load the install.sh test image cache (delegates to test/install/Makefile)
|
||||
cmds:
|
||||
- "cd test/install && make load"
|
||||
|
||||
install-test-ci-mac:
|
||||
desc: Run install.sh CI test suite on macOS (delegates to test/install/Makefile)
|
||||
cmds:
|
||||
- "cd test/install && make ci-test-mac"
|
||||
|
||||
## Compare-test targets ###################################################
|
||||
|
||||
generate-compare-file:
|
||||
desc: Generate the acceptance comparison reference JSON for the current compare image
|
||||
cmd: "go run ./cmd/syft {{ .COMPARE_TEST_IMAGE }} -o json > {{ .COMPARE_DIR }}/testdata/acceptance-{{ .COMPARE_TEST_IMAGE }}.json"
|
||||
|
||||
compare-mac:
|
||||
desc: Run macOS install + acceptance comparison against the snapshot build
|
||||
deps: [tmpdir]
|
||||
cmd: |
|
||||
{{ .COMPARE_DIR }}/mac.sh \
|
||||
@@ -483,11 +356,13 @@ tasks:
|
||||
{{ .TMP_DIR }}
|
||||
|
||||
compare-linux:
|
||||
desc: Run Linux install + acceptance comparison (deb + rpm) against the snapshot build
|
||||
cmds:
|
||||
- task: compare-test-deb-package-install
|
||||
- task: compare-test-rpm-package-install
|
||||
|
||||
compare-test-deb-package-install:
|
||||
desc: Run Linux .deb install + acceptance comparison against the snapshot build
|
||||
deps: [tmpdir]
|
||||
cmd: |
|
||||
{{ .COMPARE_DIR }}/deb.sh \
|
||||
@@ -497,6 +372,7 @@ tasks:
|
||||
{{ .TMP_DIR }}
|
||||
|
||||
compare-test-rpm-package-install:
|
||||
desc: Run Linux .rpm install + acceptance comparison against the snapshot build
|
||||
deps: [tmpdir]
|
||||
cmd: |
|
||||
{{ .COMPARE_DIR }}/rpm.sh \
|
||||
@@ -506,7 +382,7 @@ tasks:
|
||||
{{ .TMP_DIR }}
|
||||
|
||||
|
||||
## Code and data generation targets #################################
|
||||
## Code and data generation targets ######################################
|
||||
|
||||
generate:
|
||||
desc: Add data generation tasks
|
||||
@@ -556,104 +432,7 @@ tasks:
|
||||
- "SYFT_ENABLE_COMPLETENESS_TESTS=true go test -p 1 ./internal/capabilities/... -count=1"
|
||||
|
||||
|
||||
## Build-related targets #################################
|
||||
|
||||
build:
|
||||
desc: Build the project
|
||||
deps: [tools, tmpdir]
|
||||
generates:
|
||||
- "{{ .PROJECT }}"
|
||||
cmds:
|
||||
- silent: true
|
||||
cmd: |
|
||||
echo "dist: {{ .SNAPSHOT_DIR }}" > {{ .TMP_DIR }}/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> {{ .TMP_DIR }}/goreleaser.yaml
|
||||
|
||||
- "{{ .BUILD_CMD }}"
|
||||
|
||||
snapshot:
|
||||
desc: Create a snapshot release
|
||||
aliases:
|
||||
- build
|
||||
deps: [tools, tmpdir]
|
||||
sources:
|
||||
- cmd/**/*.go
|
||||
- syft/**/*.go
|
||||
- internal/**/*.go
|
||||
method: checksum
|
||||
generates:
|
||||
- "{{ .SNAPSHOT_BIN }}"
|
||||
cmds:
|
||||
- silent: true
|
||||
cmd: |
|
||||
echo "dist: {{ .SNAPSHOT_DIR }}" > {{ .TMP_DIR }}/goreleaser.yaml
|
||||
cat .goreleaser.yaml >> {{ .TMP_DIR }}/goreleaser.yaml
|
||||
|
||||
- "{{ .SNAPSHOT_CMD }}"
|
||||
|
||||
snapshot-smoke-test:
|
||||
desc: Run a smoke test on the snapshot builds + docker images
|
||||
cmds:
|
||||
- cmd: "echo 'testing snapshot binary: {{ .SNAPSHOT_BIN }}'"
|
||||
silent: true
|
||||
- cmd: "test -f {{ .SNAPSHOT_BIN }} || (find {{ .SNAPSHOT_DIR }} && echo '\nno snapshot found for {{ .SNAPSHOT_BIN }}' && false)"
|
||||
silent: true
|
||||
- "{{ .SNAPSHOT_BIN }} version"
|
||||
- "{{ .SNAPSHOT_BIN }} scan alpine:latest"
|
||||
- docker run --rm anchore/syft:latest version
|
||||
- docker run --rm anchore/syft:latest scan alpine:latest
|
||||
|
||||
changelog:
|
||||
desc: Generate a changelog
|
||||
deps: [tools]
|
||||
generates:
|
||||
- "{{ .CHANGELOG }}"
|
||||
- "{{ .NEXT_VERSION }}"
|
||||
cmds:
|
||||
- "{{ .TOOL_DIR }}/chronicle -vv -n --version-file {{ .NEXT_VERSION }} > {{ .CHANGELOG }}"
|
||||
- "{{ .TOOL_DIR }}/glow -w 0 {{ .CHANGELOG }}"
|
||||
|
||||
|
||||
## Release targets #################################
|
||||
|
||||
release:
|
||||
desc: Create a release
|
||||
interactive: true
|
||||
deps: [tools]
|
||||
cmds:
|
||||
- cmd: .github/scripts/trigger-release.sh
|
||||
silent: true
|
||||
|
||||
|
||||
## CI-only targets #################################
|
||||
|
||||
ci-check:
|
||||
# desc: "[CI only] Are you in CI?"
|
||||
cmds:
|
||||
- cmd: .github/scripts/ci-check.sh
|
||||
silent: true
|
||||
|
||||
ci-release:
|
||||
# desc: "[CI only] Create a release"
|
||||
deps: [tools]
|
||||
cmds:
|
||||
- task: ci-check
|
||||
- "{{ .TOOL_DIR }}/chronicle -vvv > CHANGELOG.md"
|
||||
- cmd: "cat CHANGELOG.md"
|
||||
silent: true
|
||||
- "{{ .RELEASE_CMD }}"
|
||||
|
||||
|
||||
## Cleanup targets #################################
|
||||
|
||||
clean:
|
||||
desc: Remove all cache files and old builds
|
||||
cmds:
|
||||
- task: clean-snapshot
|
||||
- task: clean-cache
|
||||
- task: clean-test-observations
|
||||
- task: clean-docker-cache
|
||||
- task: clean-oras-cache
|
||||
## Cleanup targets ########################################################
|
||||
|
||||
clean-snapshot:
|
||||
desc: Remove any snapshot builds
|
||||
@@ -675,6 +454,7 @@ tasks:
|
||||
desc: Remove all image docker tar cache, images from the docker daemon, and ephemeral test fixtures
|
||||
cmds:
|
||||
- task: clean-docker-cache
|
||||
- task: prune-orphan-fingerprints
|
||||
- |
|
||||
BOLD='\033[1m'
|
||||
YELLOW='\033[0;33m'
|
||||
@@ -690,6 +470,12 @@ tasks:
|
||||
echo -e "${BOLD}Deleted all ephemeral test fixtures${RESET}"
|
||||
- rm -f {{ .LAST_CACHE_PULL_FILE }} {{ .CACHE_PATHS_FILE }}
|
||||
|
||||
prune-orphan-fingerprints:
|
||||
desc: Remove *.fingerprint files left behind by moved/deleted catalogers
|
||||
silent: true
|
||||
cmds:
|
||||
- .github/scripts/prune_orphan_fingerprints.py
|
||||
|
||||
clean-test-observations:
|
||||
desc: Remove all test observations (i.e. testdata/test-observations.json)
|
||||
cmds:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
[TestHandler_handleAttestationStarted/attesting_in_progress/task_line - 1]
|
||||
⠋ Creating a thing running a thing
|
||||
⠋ Creating a thing running a thing
|
||||
---
|
||||
|
||||
[TestHandler_handleAttestationStarted/attesting_in_progress/log - 1]
|
||||
@@ -11,7 +11,7 @@
|
||||
---
|
||||
|
||||
[TestHandler_handleAttestationStarted/attesting_complete/task_line - 1]
|
||||
✔ Created a thing running a thing
|
||||
✔ Created a thing running a thing
|
||||
---
|
||||
|
||||
[TestHandler_handleAttestationStarted/attesting_complete/log - 1]
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
[TestHandler_handleFetchImage/fetch_image_in_progress - 1]
|
||||
⠋ Loading image ━━━━━━━━━━━━━━━━━━━━ [current] the-image
|
||||
⠋ Loading image ━━━━━━━━━━━━━━━━━━━━ [current] the-image
|
||||
---
|
||||
|
||||
[TestHandler_handleFetchImage/fetch_image_complete - 1]
|
||||
✔ Loaded image the-image
|
||||
✔ Loaded image the-image
|
||||
---
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
[TestHandler_handleFileIndexingStarted/cataloging_in_progress - 1]
|
||||
⠋ Indexing file system ━━━━━━━━━━━━━━━━━━━━ [current] /some/path
|
||||
⠋ Indexing file system ━━━━━━━━━━━━━━━━━━━━ [current] /some/path
|
||||
---
|
||||
|
||||
[TestHandler_handleFileIndexingStarted/cataloging_complete - 1]
|
||||
✔ Indexed file system /some/path
|
||||
✔ Indexed file system /some/path
|
||||
---
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
[TestHandler_handlePullSourceStarted/snap_download_in_progress - 1]
|
||||
⠋ Downloading snap file... ━━━━━━━━━━━━━━━━━━━━ example-app_1.0_amd64.snap
|
||||
⠋ Downloading snap file... ━━━━━━━━━━━━━━━━━━━━ example-app_1.0_amd64.snap
|
||||
---
|
||||
|
||||
[TestHandler_handlePullSourceStarted/snap_download_complete - 1]
|
||||
✔ Snap downloaded successfully example-app_1.0_amd64.snap
|
||||
✔ Snap downloaded successfully example-app_1.0_amd64.snap
|
||||
---
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
|
||||
[TestHandler_handleReadImage/read_image_in_progress - 1]
|
||||
⠋ Parsing image ━━━━━━━━━━━━━━━━━━━━ id
|
||||
⠋ Parsing image ━━━━━━━━━━━━━━━━━━━━ id
|
||||
---
|
||||
|
||||
[TestHandler_handleReadImage/read_image_complete - 1]
|
||||
✔ Parsed image id
|
||||
✔ Parsed image id
|
||||
---
|
||||
|
||||
@@ -60,6 +60,7 @@ type (
|
||||
Packages []detectorPackageInfo `json:"packages,omitempty"`
|
||||
Comment string `json:"comment,omitempty"`
|
||||
PackageTypes []string `json:"package_types,omitempty"`
|
||||
PURLTypes []string `json:"purl_types,omitempty"`
|
||||
JSONSchemaTypes []string `json:"json_schema_types,omitempty"`
|
||||
Capabilities capabilities.CapabilitySet `json:"capabilities,omitempty"`
|
||||
}
|
||||
@@ -235,7 +236,7 @@ func renderCatalogerInfoJSON(doc *capabilities.Document, catalogers []capabiliti
|
||||
// if no parsers, use detectors instead
|
||||
if len(info.Patterns) == 0 {
|
||||
info.Capabilities = cat.Capabilities
|
||||
info.Patterns = convertDetectorsToPatterns(cat.Detectors, cat.PackageTypes, cat.JSONSchemaTypes)
|
||||
info.Patterns = convertDetectorsToPatterns(cat.Detectors, cat.PackageTypes, cat.PURLTypes, cat.JSONSchemaTypes)
|
||||
}
|
||||
|
||||
info.Config = getConfigInfoFromDocument(doc, cat.Config)
|
||||
@@ -278,6 +279,7 @@ func convertParsersToPatterns(parsers []capabilities.Parser) []patternInfo {
|
||||
Packages: convertDetectorPackages(parser.Detector.Packages),
|
||||
Comment: parser.Detector.Comment,
|
||||
PackageTypes: parser.PackageTypes,
|
||||
PURLTypes: parser.PURLTypes,
|
||||
JSONSchemaTypes: parser.JSONSchemaTypes,
|
||||
Capabilities: parser.Capabilities,
|
||||
})
|
||||
@@ -286,7 +288,7 @@ func convertParsersToPatterns(parsers []capabilities.Parser) []patternInfo {
|
||||
}
|
||||
|
||||
// convertDetectorsToPatterns converts detector entries to pattern info for JSON output (for non-parser catalogers)
|
||||
func convertDetectorsToPatterns(detectors []capabilities.Detector, packageTypes, jsonSchemaTypes []string) []patternInfo {
|
||||
func convertDetectorsToPatterns(detectors []capabilities.Detector, packageTypes, purlTypes, jsonSchemaTypes []string) []patternInfo {
|
||||
var patterns []patternInfo
|
||||
for _, det := range detectors {
|
||||
patterns = append(patterns, patternInfo{
|
||||
@@ -296,6 +298,7 @@ func convertDetectorsToPatterns(detectors []capabilities.Detector, packageTypes,
|
||||
Packages: convertDetectorPackages(det.Packages),
|
||||
Comment: det.Comment,
|
||||
PackageTypes: packageTypes,
|
||||
PURLTypes: purlTypes,
|
||||
JSONSchemaTypes: jsonSchemaTypes,
|
||||
})
|
||||
}
|
||||
@@ -353,7 +356,7 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
|
||||
)
|
||||
|
||||
// set headers
|
||||
table.Header("ECOSYSTEM", "CATALOGER", "CRITERIA", "LICENSE", "NODES", "EDGES", "KINDS", "LISTING", "DIGESTS", "HASH")
|
||||
table.Header("ECOSYSTEM", "CATALOGER", "CRITERIA", "PURL", "LICENSE", "NODES", "EDGES", "KINDS", "LISTING", "DIGESTS", "HASH")
|
||||
|
||||
// build rows for each cataloger
|
||||
var data [][]string
|
||||
@@ -367,13 +370,13 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
|
||||
// generic catalogers: one row per parser
|
||||
for _, parser := range cat.Parsers {
|
||||
criteria := formatCriteria([]capabilities.Detector{parser.Detector})
|
||||
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, parser.Capabilities)
|
||||
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, parser.PURLTypes, parser.Capabilities)
|
||||
data = append(data, row)
|
||||
}
|
||||
} else {
|
||||
// custom catalogers: one row with all detectors
|
||||
criteria := formatCriteria(cat.Detectors)
|
||||
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, cat.Capabilities)
|
||||
row := buildTableRowFromCapabilities(ecosystem, cat.Name, criteria, cat.PURLTypes, cat.Capabilities)
|
||||
data = append(data, row)
|
||||
}
|
||||
}
|
||||
@@ -385,7 +388,7 @@ func renderCatalogerInfoTable(_ *capabilities.Document, catalogers []capabilitie
|
||||
}
|
||||
|
||||
// buildTableRowFromCapabilities builds a table row from capability values
|
||||
func buildTableRowFromCapabilities(ecosystem, name, criteria string, caps capabilities.CapabilitySet) []string {
|
||||
func buildTableRowFromCapabilities(ecosystem, name, criteria string, purlTypes []string, caps capabilities.CapabilitySet) []string {
|
||||
// extract capability default values
|
||||
license := extractBoolCapability(caps, "license")
|
||||
nodes := extractNodesCapability(caps)
|
||||
@@ -399,6 +402,7 @@ func buildTableRowFromCapabilities(ecosystem, name, criteria string, caps capabi
|
||||
ecosystem,
|
||||
name,
|
||||
criteria,
|
||||
formatPURLTypes(purlTypes),
|
||||
license,
|
||||
nodes,
|
||||
edges,
|
||||
@@ -409,6 +413,14 @@ func buildTableRowFromCapabilities(ecosystem, name, criteria string, caps capabi
|
||||
}
|
||||
}
|
||||
|
||||
// formatPURLTypes renders the PURL types as a comma-separated list, or a placeholder when empty
|
||||
func formatPURLTypes(purlTypes []string) string {
|
||||
if len(purlTypes) == 0 {
|
||||
return noStyle.Render("·")
|
||||
}
|
||||
return strings.Join(purlTypes, ", ")
|
||||
}
|
||||
|
||||
// extractBoolCapability extracts a boolean capability value and formats it
|
||||
func extractBoolCapability(caps capabilities.CapabilitySet, name string) string {
|
||||
for _, cap := range caps {
|
||||
|
||||
@@ -280,12 +280,15 @@ func Test_catalogerInfoReport(t *testing.T) {
|
||||
Detectors: []capabilities.Detector{
|
||||
{Method: capabilities.GlobDetection, Criteria: []string{"**/*.test"}},
|
||||
},
|
||||
PURLTypes: []string{"npm"},
|
||||
},
|
||||
},
|
||||
assertions: func(t *testing.T, got string) {
|
||||
assert.Contains(t, got, "test-cataloger")
|
||||
assert.Contains(t, got, "ECOSYSTEM")
|
||||
assert.Contains(t, got, "CATALOGER")
|
||||
assert.Contains(t, got, "PURL")
|
||||
assert.Contains(t, got, "npm")
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -298,12 +301,17 @@ func Test_catalogerInfoReport(t *testing.T) {
|
||||
Ecosystem: "test",
|
||||
Type: "custom",
|
||||
Selectors: []string{"test", "custom"},
|
||||
Detectors: []capabilities.Detector{
|
||||
{Method: capabilities.GlobDetection, Criteria: []string{"**/*.test"}},
|
||||
},
|
||||
PURLTypes: []string{"npm"},
|
||||
},
|
||||
},
|
||||
assertions: func(t *testing.T, got string) {
|
||||
assert.Contains(t, got, `"name":"test-cataloger"`)
|
||||
assert.Contains(t, got, `"ecosystem":"test"`)
|
||||
assert.Contains(t, got, `"type":"custom"`)
|
||||
assert.Contains(t, got, `"purl_types":["npm"]`)
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -13,12 +13,10 @@ func DefaultFormatSPDXJSON() FormatSPDXJSON {
|
||||
}
|
||||
|
||||
func (o FormatSPDXJSON) config(v string) spdxjson.EncoderConfig {
|
||||
var pretty bool
|
||||
c := spdxjson.DefaultEncoderConfig()
|
||||
c.Version = v
|
||||
if o.Pretty != nil {
|
||||
pretty = *o.Pretty
|
||||
}
|
||||
return spdxjson.EncoderConfig{
|
||||
Version: v,
|
||||
Pretty: pretty,
|
||||
c.Pretty = *o.Pretty
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package options
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestFormatSPDXJSON_buildConfig(t *testing.T) {
|
||||
@@ -11,5 +13,7 @@ func TestFormatSPDXJSON_buildConfig(t *testing.T) {
|
||||
ft = setAllToNonZero(t, ft).(*FormatSPDXJSON)
|
||||
|
||||
subject := ft.config("Version")
|
||||
assertExpectedValue(t, subject)
|
||||
require.Equal(t, "Version", subject.Version)
|
||||
require.Equal(t, "2.3", subject.DefaultVersion)
|
||||
require.True(t, subject.Pretty)
|
||||
}
|
||||
|
||||
@@ -1,62 +1,68 @@
|
||||
module github.com/anchore/syft
|
||||
|
||||
go 1.25.8
|
||||
go 1.26.3
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.6.0
|
||||
github.com/CycloneDX/cyclonedx-go v0.10.0
|
||||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
github.com/CycloneDX/cyclonedx-go v0.11.0
|
||||
github.com/Masterminds/semver/v3 v3.5.0
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/OneOfOne/xxhash v1.2.8
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/acobaugh/osrelease v0.1.0
|
||||
github.com/adrg/xdg v0.5.3
|
||||
github.com/anchore/bubbly v0.2.0
|
||||
github.com/anchore/clio v0.1.0
|
||||
github.com/anchore/fangs v0.1.0
|
||||
github.com/anchore/go-collections v0.1.0
|
||||
github.com/anchore/go-homedir v0.1.0
|
||||
github.com/anchore/go-logger v0.1.0
|
||||
github.com/anchore/go-macholibre v0.1.0
|
||||
github.com/anchore/bubbly v0.2.1
|
||||
github.com/anchore/clio v0.1.1
|
||||
github.com/anchore/fangs v0.1.1
|
||||
github.com/anchore/go-collections v0.1.1
|
||||
github.com/anchore/go-homedir v0.1.1
|
||||
github.com/anchore/go-logger v0.1.1
|
||||
github.com/anchore/go-macholibre v0.1.1
|
||||
github.com/anchore/go-rpmdb v0.1.0
|
||||
github.com/anchore/go-sync v0.1.0
|
||||
github.com/anchore/go-sync v0.1.1
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/packageurl-go v0.2.0
|
||||
github.com/anchore/stereoscope v0.1.23
|
||||
github.com/anchore/stereoscope v0.2.2
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1
|
||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
|
||||
github.com/bmatcuk/doublestar/v4 v4.10.0
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/charmbracelet/bubbles v1.0.0
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/dave/jennifer v1.7.1
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
github.com/diskfs/go-diskfs v1.7.0
|
||||
github.com/diskfs/go-diskfs v1.9.3
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/elliotchance/phpserialize v1.4.0
|
||||
github.com/facebookincubator/nvdtools v0.1.5
|
||||
github.com/github/go-spdx/v2 v2.4.0
|
||||
github.com/gkampitakis/go-snaps v0.5.21
|
||||
github.com/go-git/go-billy/v5 v5.8.0
|
||||
github.com/go-git/go-git/v5 v5.18.0
|
||||
github.com/github/go-spdx/v2 v2.7.0
|
||||
github.com/gkampitakis/go-snaps v0.5.22
|
||||
github.com/go-git/go-billy/v5 v5.9.0
|
||||
github.com/go-git/go-git/v5 v5.19.1
|
||||
github.com/go-test/deep v1.1.1
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0
|
||||
github.com/goccy/go-yaml v1.19.2
|
||||
github.com/gohugoio/hashstructure v0.6.0
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/google/go-containerregistry v0.21.5
|
||||
github.com/google/go-containerregistry v0.21.7
|
||||
github.com/google/licensecheck v0.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gookit/color v1.6.0
|
||||
github.com/gookit/color v1.6.1
|
||||
github.com/gpustack/gguf-parser-go v0.24.1
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2
|
||||
github.com/hashicorp/go-getter v1.8.6
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/hashicorp/hcl/v2 v2.24.0
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/invopop/jsonschema v0.13.0
|
||||
github.com/jedib0t/go-pretty/v6 v6.7.8
|
||||
github.com/invopop/jsonschema v0.14.0
|
||||
github.com/jedib0t/go-pretty/v6 v6.8.1
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||
github.com/klauspost/compress v1.18.6
|
||||
github.com/magiconair/properties v1.8.10
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/moby/sys/mountinfo v0.7.2
|
||||
@@ -73,10 +79,11 @@ require (
|
||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
|
||||
github.com/sergi/go-diff v1.4.0
|
||||
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb
|
||||
github.com/spdx/tools-golang v0.5.7
|
||||
github.com/spdx/tools-golang v0.6.0-rc4
|
||||
github.com/spf13/afero v1.15.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/ulikunitz/xz v0.5.15
|
||||
github.com/vbatts/go-mtree v0.7.0
|
||||
github.com/vifraa/gopom v1.0.0
|
||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
|
||||
@@ -85,201 +92,46 @@ require (
|
||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
|
||||
go.uber.org/goleak v1.3.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546
|
||||
golang.org/x/mod v0.35.0
|
||||
golang.org/x/net v0.53.0
|
||||
modernc.org/sqlite v1.46.2
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f
|
||||
golang.org/x/mod v0.37.0
|
||||
golang.org/x/net v0.56.0
|
||||
golang.org/x/time v0.15.0
|
||||
golang.org/x/tools v0.46.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.51.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1
|
||||
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.25.1 // indirect
|
||||
cloud.google.com/go v0.123.0 // indirect
|
||||
cloud.google.com/go/auth v0.18.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
cloud.google.com/go/iam v1.5.3 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.3 // indirect
|
||||
cloud.google.com/go/storage v1.61.3 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.14.0-rc.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 // indirect
|
||||
github.com/Microsoft/hcsshim v0.15.0-rc.1 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.1 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/agext/levenshtein v1.2.1 // indirect; indirectt
|
||||
github.com/anchore/go-lzo v0.1.0 // indirect
|
||||
github.com/anchore/go-struct-converter v0.1.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/aquasecurity/go-version v0.0.1 // indirect
|
||||
github.com/atotto/clipboard v0.1.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/becheran/wildmatch-go v1.0.0 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.1 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.1 // indirect
|
||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/containerd/containerd/api v1.10.0 // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v1.0.0-rc.4 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect
|
||||
github.com/containerd/ttrpc v1.2.7 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.2.3 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/cli v29.4.0+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.5 // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/fatih/color v1.18.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/gabriel-vasile/mimetype v1.4.13 // indirect
|
||||
github.com/gkampitakis/ciinfo v0.3.2 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-restruct/restruct v1.2.0-alpha // indirect
|
||||
github.com/goccy/go-yaml v1.19.2
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-version v1.8.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.18.5 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/maruel/natural v1.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/minlz v1.0.1 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/sys/sequential v0.6.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/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/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.3.0 // indirect
|
||||
github.com/opencontainers/selinux v1.13.1 // indirect
|
||||
github.com/pborman/indent v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/profile v1.7.0 // indirect
|
||||
github.com/pkg/xattr v0.4.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // 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/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/skeema/knownhosts v1.3.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/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/sylabs/sif/v2 v2.24.0 // indirect
|
||||
github.com/sylabs/squashfs v1.0.6 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // indirect
|
||||
github.com/tidwall/gjson v1.18.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/ulikunitz/xz v0.5.15
|
||||
github.com/vbatts/tar-split v0.12.2 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
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
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
golang.org/x/term v0.42.0 // indirect
|
||||
golang.org/x/text v0.36.0 // indirect
|
||||
golang.org/x/time v0.15.0
|
||||
golang.org/x/tools v0.44.0
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/api v0.271.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/libc v1.70.0 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/acobaugh/osrelease v0.1.0
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
github.com/cespare/xxhash/v2 v2.3.0
|
||||
github.com/gpustack/gguf-parser-go v0.24.0
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8
|
||||
)
|
||||
|
||||
require (
|
||||
cel.dev/expr v0.25.1 // indirect
|
||||
cloud.google.com/go/monitoring v1.24.3 // indirect
|
||||
cyphar.com/go-pathrs v0.2.1 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.12 // indirect
|
||||
@@ -299,36 +151,181 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect
|
||||
github.com/aws/smithy-go v1.24.2 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/becheran/wildmatch-go v1.0.0 // indirect
|
||||
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.1 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.2 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.10.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.6.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 // indirect
|
||||
github.com/containerd/cgroups/v3 v3.1.2 // indirect
|
||||
github.com/containerd/containerd/v2 v2.2.2 // indirect
|
||||
github.com/containerd/plugin v1.0.0 // 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.2 // indirect
|
||||
github.com/containerd/continuity v0.5.0 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v1.0.0-rc.4 // indirect
|
||||
github.com/containerd/plugin v1.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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/cli v29.5.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.5 // indirect
|
||||
github.com/docker/go-connections v0.7.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/fatih/color v1.18.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/gabriel-vasile/mimetype v1.4.13 // 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-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/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.17.0 // indirect
|
||||
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-version v1.8.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/henvic/httpretty v0.1.4 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/moby/moby/api v1.54.1 // indirect
|
||||
github.com/moby/moby/client v0.4.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/maruel/natural v1.3.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/minlz v1.0.1 // indirect
|
||||
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
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/moby/api v1.54.2 // indirect
|
||||
github.com/moby/moby/client v0.4.1 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.1 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // 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/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/pborman/indent v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.26 // indirect
|
||||
github.com/piprate/json-gold v0.7.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pkg/profile v1.7.0 // indirect
|
||||
github.com/pkg/xattr v0.4.12 // indirect
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.11.0 // indirect
|
||||
github.com/sahilm/fuzzy v0.1.1 // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/smallnest/ringbuffer v0.0.0-20241116012123-461381446e3d // 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.6.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/sylabs/sif/v2 v2.24.1 // indirect
|
||||
github.com/sylabs/squashfs v1.0.6 // 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/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
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
|
||||
gonum.org/v1/gonum v0.16.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.2 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/term v0.44.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
gonum.org/v1/gonum v0.17.0 // indirect
|
||||
google.golang.org/api v0.271.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
modernc.org/libc v1.72.3 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
retract (
|
||||
|
||||
@@ -67,8 +67,6 @@ cloud.google.com/go/storage v1.61.3 h1:VS//ZfBuPGDvakfD9xyPW1RGF1Vy3BWUoVZXgW1KM
|
||||
cloud.google.com/go/storage v1.61.3/go.mod h1:JtqK8BBB7TWv0HVGHubtUdzYYrakOQIsMLffZ2Z/HWk=
|
||||
cloud.google.com/go/trace v1.11.7 h1:kDNDX8JkaAG3R2nq1lIdkb7FCSi1rCmsEtKVsty7p+U=
|
||||
cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s=
|
||||
cyphar.com/go-pathrs v0.2.1 h1:9nx1vOgwVvX1mNBWDu93+vaceedpbsDqo+XuBGL40b8=
|
||||
cyphar.com/go-pathrs v0.2.1/go.mod h1:y8f1EMG7r+hCuFf/rXsKqMJrJAUoADZGNh5/vZPKcGc=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
@@ -79,13 +77,13 @@ github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbi
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/CycloneDX/cyclonedx-go v0.10.0 h1:7xyklU7YD+CUyGzSFIARG18NYLsKVn4QFg04qSsu+7Y=
|
||||
github.com/CycloneDX/cyclonedx-go v0.10.0/go.mod h1:vUvbCXQsEm48OI6oOlanxstwNByXjCZ2wuleUlwGEO8=
|
||||
github.com/CycloneDX/cyclonedx-go v0.11.0 h1:GokP8FiRC+foiuwWhSSLpSD5H4hSWtGnR3wo7apkBFI=
|
||||
github.com/CycloneDX/cyclonedx-go v0.11.0/go.mod h1:vUvbCXQsEm48OI6oOlanxstwNByXjCZ2wuleUlwGEO8=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
|
||||
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0 h1:sBEjpZlNHzK1voKq9695PJSX2o5NEXl7/OL3coiIY0c=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ=
|
||||
@@ -94,20 +92,20 @@ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapp
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc=
|
||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
|
||||
github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ=
|
||||
github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c=
|
||||
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29 h1:0kQAzHq8vLs7Pptv+7TxjdETLf/nIqJpIB4oC6Ba4vY=
|
||||
github.com/Microsoft/go-winio v0.6.3-0.20251027160822-ad3df93bed29/go.mod h1:ZWa7ssZJT30CCDGJ7fk/2SBTq9BIQrrVjrcss0UW2s0=
|
||||
github.com/Microsoft/hcsshim v0.15.0-rc.1 h1:FbbwtQmiD+BVHynGkx5S65JkLyhkEiiTP8nrpmg2SZw=
|
||||
github.com/Microsoft/hcsshim v0.15.0-rc.1/go.mod h1:HWvvUPIy9HF6LotILj1G4VyS065rcLQ6tqj6tMUdOfI=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
||||
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ=
|
||||
github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
|
||||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
@@ -122,34 +120,34 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/anchore/bubbly v0.2.0 h1:VE6nLlWsYV+bTOqqCzS46NirrVmvHah3UQDtFcjzgHU=
|
||||
github.com/anchore/bubbly v0.2.0/go.mod h1:zRg1mfRCCe4vq1t9Ibu24YurErmnp9LAVCbpnpsb1pQ=
|
||||
github.com/anchore/clio v0.1.0 h1:lrlSgD38V0T6TLjOG5Kn3miEXCQyYr6ZlwTSnW1WIJg=
|
||||
github.com/anchore/clio v0.1.0/go.mod h1:z4l6oHaU8x/yQ3BnES4Ekh4eLqFSYlj1jQpk7waPkJs=
|
||||
github.com/anchore/fangs v0.1.0 h1:EidxQN2XDRWn8q2n9pCVZXrOEnvpV1CuPLiWMyWG/vE=
|
||||
github.com/anchore/fangs v0.1.0/go.mod h1:5YocMj6a0MC+KiBXBP0/ocOfc54DvlsOcpkAxmPWgx0=
|
||||
github.com/anchore/go-collections v0.1.0 h1:vh2LE/j6HLKC27pENkas+J+h4BK4nOte9b7pDjlhY9w=
|
||||
github.com/anchore/go-collections v0.1.0/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
|
||||
github.com/anchore/go-homedir v0.1.0 h1:WP+lNLOz2og8hzCkY7IccyFNInVKsrjogzV19G5G8p0=
|
||||
github.com/anchore/go-homedir v0.1.0/go.mod h1:9B0DGhbmMAJVGGEbrlb+PkORM5eDFCEOtZ3xQ22qaLA=
|
||||
github.com/anchore/go-logger v0.1.0 h1:3pE9Sli4SE1I/mtV+2pZXzim+aWj83BRaneVXeMgD3A=
|
||||
github.com/anchore/go-logger v0.1.0/go.mod h1:0l63X5VyHqfWQwsNaTmkgXErsfohze3NAFCXFdhSjdQ=
|
||||
github.com/anchore/go-lzo v0.1.0 h1:NgAacnzqPeGH49Ky19QKLBZEuFRqtTG9cdaucc3Vncs=
|
||||
github.com/anchore/go-lzo v0.1.0/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk=
|
||||
github.com/anchore/go-macholibre v0.1.0 h1:qHbdusBZNcZM/uuKf1Psa9xxAFSoyRTps8GW9gpJgsg=
|
||||
github.com/anchore/go-macholibre v0.1.0/go.mod h1:eu0gbwaZ+ocVFJLePdmPPDKU8MboV1MKsUCr36Ckd5s=
|
||||
github.com/anchore/bubbly v0.2.1 h1:b3ZNBefY8ks5DI4SQ7BXSbBIsN6eoaTD6s1su7NuCBo=
|
||||
github.com/anchore/bubbly v0.2.1/go.mod h1:o6a9aH3/fCn+mb5g1T1tBbQZ4hGR0yO5/uD1d2Vtago=
|
||||
github.com/anchore/clio v0.1.1 h1:9bkYnaFbpT0J3G0qSHyQqa/lpK5zV0C9Gtgh82H/jj0=
|
||||
github.com/anchore/clio v0.1.1/go.mod h1:CFPIopJlakJbBpKtAL/EPpj4W9hgBO7PfFc8N2FAtlE=
|
||||
github.com/anchore/fangs v0.1.1 h1:NBTHocmWM2/0gNhebpoE/wEB0Z0nxh2lESww2o/Uf5M=
|
||||
github.com/anchore/fangs v0.1.1/go.mod h1:82oZuk4+RynOvAoxnXKmI1zO5xk/kbOw+5t4fAAPSsk=
|
||||
github.com/anchore/go-collections v0.1.1 h1:kGlmSehs+ctTBZr+98F2+taHeolHy+XDx1Nczl8GZc8=
|
||||
github.com/anchore/go-collections v0.1.1/go.mod h1:zgDbPSNDpfU47tVo7aW+2suG7+vXB1Sq1qtDhRJknQ0=
|
||||
github.com/anchore/go-homedir v0.1.1 h1:KpaO5BPrVT7cVMcEZ9KfFfEi0iuo3DtagPGl0C744X0=
|
||||
github.com/anchore/go-homedir v0.1.1/go.mod h1:9B0DGhbmMAJVGGEbrlb+PkORM5eDFCEOtZ3xQ22qaLA=
|
||||
github.com/anchore/go-logger v0.1.1 h1:HPBNmQVBdYliwaFVUL7oJ3UvSlaoXAMj/ZagAIYaCpA=
|
||||
github.com/anchore/go-logger v0.1.1/go.mod h1:ekWuh5BkZVwyXnyEd4fwL29N4pNLssgRsqh2+kFN/b8=
|
||||
github.com/anchore/go-lzo v0.1.1 h1:IwL/fvkdtlIrYIXck6WxZ3nb8WjjHziYYmGxlooyOnM=
|
||||
github.com/anchore/go-lzo v0.1.1/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk=
|
||||
github.com/anchore/go-macholibre v0.1.1 h1:sTBHi3S1WVIOCamc6NyT8Sn9mBmD30DCTfjfVm4k6/M=
|
||||
github.com/anchore/go-macholibre v0.1.1/go.mod h1:YNq2610RlvGCK0Za+Klz/OCMtUaEnwwAGA/VfUAQaro=
|
||||
github.com/anchore/go-rpmdb v0.1.0 h1:Q8dc208/HYzCqhx0L1zurfm1UPil24hlo9NjkdFmLdE=
|
||||
github.com/anchore/go-rpmdb v0.1.0/go.mod h1:eQVa6QFGzKy0qMcnW2pez0XBczvgwSjw9vA23qifEyU=
|
||||
github.com/anchore/go-struct-converter v0.1.0 h1:2rDRssAl6mgKBSLNiVCMADgZRhoqtw9dedlWa0OhD30=
|
||||
github.com/anchore/go-struct-converter v0.1.0/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
|
||||
github.com/anchore/go-sync v0.1.0 h1:1TEZM7jISrvtoBMOF79xP0caQKASAtgW1yKqc0EjyZg=
|
||||
github.com/anchore/go-sync v0.1.0/go.mod h1:Iposeub0kHipoTei1icj4Tys0SJN+cCdxEnkS7bZUFs=
|
||||
github.com/anchore/go-struct-converter v0.2.0-rc2 h1:q+859fW2/jbHJHB2etbNfRlFwYpknyvbqqk1hUdamQ4=
|
||||
github.com/anchore/go-struct-converter v0.2.0-rc2/go.mod h1:cDBA5vhcR62nXWo8QH9/Kk2807o65ISaHPNPX66L+Uw=
|
||||
github.com/anchore/go-sync v0.1.1 h1:91SZ+YqUIIHmf2jPAYZPuHqM/ZqnK1pVJDWtET6+AJE=
|
||||
github.com/anchore/go-sync v0.1.1/go.mod h1:3haGsk2BnaoVhsfqae8Kd0FKIAILE1Hw69P4Xo5iVBw=
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T/EAErk=
|
||||
github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE=
|
||||
github.com/anchore/stereoscope v0.1.23 h1:q9i3CtbicTuSlcCnA+5pfoT9WDCEoSqvXDfHMH1hyWo=
|
||||
github.com/anchore/stereoscope v0.1.23/go.mod h1:JLnun49fkLkuv3ebU0ROvFl/0JiRmNmUtCzc6y4ollo=
|
||||
github.com/anchore/stereoscope v0.2.2 h1:SGTLGoF6GHmKEn9Bb6LYpzzgz9nhvMZgN/c4fTSQjYY=
|
||||
github.com/anchore/stereoscope v0.2.2/go.mod h1:ylJXJKebLctP7u9ewguB1d0zUETJxvAA7r2DiuljDTM=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
@@ -250,8 +248,8 @@ github.com/charmbracelet/bubbles v1.0.0 h1:12J8/ak/uCZEMQ6KU7pcfwceyjLlWsDLAxB5f
|
||||
github.com/charmbracelet/bubbles v1.0.0/go.mod h1:9d/Zd5GdnauMI5ivUIVisuEm3ave1XwXtD1ckyV6r3E=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.4.1 h1:a1lO03qTrSIRaK8c3JRxJDZOvhvIeSco3ej+ngLk1kk=
|
||||
github.com/charmbracelet/colorprofile v0.4.1/go.mod h1:U1d9Dljmdf9DLegaJ0nGZNJvoXAhayhmidOdcBwAvKk=
|
||||
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
||||
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
@@ -276,10 +274,10 @@ github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/clipperhouse/displaywidth v0.10.0 h1:GhBG8WuerxjFQQYeuZAeVTuyxuX+UraiZGD4HJQ3Y8g=
|
||||
github.com/clipperhouse/displaywidth v0.10.0/go.mod h1:XqJajYsaiEwkxOj4bowCTMcT1SgvHo9flfF3jQasdbs=
|
||||
github.com/clipperhouse/uax29/v2 v2.6.0 h1:z0cDbUV+aPASdFb2/ndFnS9ts/WNXgTNNGFoKXuhpos=
|
||||
github.com/clipperhouse/uax29/v2 v2.6.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
@@ -294,14 +292,14 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||
github.com/containerd/cgroups/v3 v3.1.2 h1:OSosXMtkhI6Qove637tg1XgK4q+DhR0mX8Wi8EhrHa4=
|
||||
github.com/containerd/cgroups/v3 v3.1.2/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
|
||||
github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o=
|
||||
github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM=
|
||||
github.com/containerd/containerd/v2 v2.2.2 h1:mjVQdtfryzT7lOqs5EYUFZm8ioPVjOpkSoG1GJPxEMY=
|
||||
github.com/containerd/containerd/v2 v2.2.2/go.mod h1:5Jhevmv6/2J+Iu/A2xXAdUIdI5Ah/hfyO7okJ4AFIdY=
|
||||
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
|
||||
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/cgroups/v3 v3.1.3 h1:eUNflyMddm18+yrDmZPn3jI7C5hJ9ahABE5q6dyLYXQ=
|
||||
github.com/containerd/cgroups/v3 v3.1.3/go.mod h1:PKZ2AcWmSBsY/tJUVhtS/rluX0b1uq1GmPO1ElCmbOw=
|
||||
github.com/containerd/containerd/api v1.11.1 h1:h8nfoDW9+fNsC/9TwiAHj8B1GzXKtR4eFtkhi/X5RLU=
|
||||
github.com/containerd/containerd/api v1.11.1/go.mod h1:CaQFRu+N1MtbgL6JDOJLUB1hCKESU1lD6MuTJhgtdlw=
|
||||
github.com/containerd/containerd/v2 v2.3.2 h1:eLven1YxRMkeiKu7IcMrPKE+gn8sGR1DqHbbshMEvWM=
|
||||
github.com/containerd/containerd/v2 v2.3.2/go.mod h1:rHKGm3VW6wNrINb3x8mNT+w7qYXFVElTt/8HTuxVhD4=
|
||||
github.com/containerd/continuity v0.5.0 h1:7a85HZpCSs+1Zps0Ee3DPSuAWY+0SJM1JNM51nlEVDg=
|
||||
github.com/containerd/continuity v0.5.0/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
|
||||
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
|
||||
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
|
||||
@@ -312,12 +310,10 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v1.0.0-rc.4 h1:M42JrUT4zfZTqtkUwkr0GzmUWbfyO5VO0Q5b3op97T4=
|
||||
github.com/containerd/platforms v1.0.0-rc.4/go.mod h1:lKlMXyLybmBedS/JJm11uDofzI8L2v0J2ZbYvNsbq1A=
|
||||
github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y=
|
||||
github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=
|
||||
github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ=
|
||||
github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/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/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@@ -325,8 +321,8 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cyphar/filepath-securejoin v0.6.0 h1:BtGB77njd6SVO6VztOHfPxKitJvd/VPT+OFBFMOi1Is=
|
||||
github.com/cyphar/filepath-securejoin v0.6.0/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
|
||||
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -337,18 +333,18 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
github.com/diskfs/go-diskfs v1.7.0 h1:vonWmt5CMowXwUc79jWyGrf2DIMeoOjkLlMnQYGVOs8=
|
||||
github.com/diskfs/go-diskfs v1.7.0/go.mod h1:LhQyXqOugWFRahYUSw47NyZJPezFzB9UELwhpszLP/k=
|
||||
github.com/diskfs/go-diskfs v1.9.3 h1:cLciNCeZ4QAXVxyPJDr1ZJ9N9CCG3rQlQ/z/Cs/cNDM=
|
||||
github.com/diskfs/go-diskfs v1.9.3/go.mod h1:TePJORO83Adh5pb2SqsxAwaP0fofFxKLkxctiS/9OQc=
|
||||
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
|
||||
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
|
||||
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
|
||||
github.com/docker/cli v29.4.0+incompatible h1:+IjXULMetlvWJiuSI0Nbor36lcJ5BTcVpUmB21KBoVM=
|
||||
github.com/docker/cli v29.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v29.5.3+incompatible h1:nbEFfz774vBwQ5KRYv7c/AghjReqnGISvrRhzjV0evs=
|
||||
github.com/docker/cli v29.5.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
|
||||
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
|
||||
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
|
||||
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
|
||||
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
|
||||
@@ -360,8 +356,8 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/elliotchance/phpserialize v1.4.0 h1:cAp/9+KSnEbUC8oYCE32n2n84BeW8HOY3HMDI8hG2OY=
|
||||
github.com/elliotchance/phpserialize v1.4.0/go.mod h1:gt7XX9+ETUcLXbtTKEuyrqW3lcLUAeS/AnGZ2e49TZs=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab h1:h1UgjJdAAhj+uPL68n7XASS6bU+07ZX1WJvVS2eyoeY=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20230602022725-51bbb787efab/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57 h1:x5yxNrq8XffV/OoNUeFPM6hxHVi5OTspSTBxr/9pemg=
|
||||
github.com/elliotwutingfeng/asciiset v0.0.0-20260129054604-cfde2086bc57/go.mod h1:GLo/8fDswSAniFG+BFIaiSPcK610jyzgEhWYPQwuQdw=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
@@ -385,6 +381,8 @@ github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
|
||||
github.com/erofs/go-erofs v0.3.0 h1:o/W5ABAA3sHYl97WL93dacKEfeDpJhdFf3c2snAti7I=
|
||||
github.com/erofs/go-erofs v0.3.0/go.mod h1:XkSeN9MHszGd4+3gcEjadJLYHCQpWzJ7/8yznzMuzJs=
|
||||
github.com/facebookincubator/flog v0.0.0-20190930132826-d2511d0ce33c/go.mod h1:QGzNH9ujQ2ZUr/CjDGZGWeDAVStrWNjHeEcjJL96Nuk=
|
||||
github.com/facebookincubator/nvdtools v0.1.5 h1:jbmDT1nd6+k+rlvKhnkgMokrCAzHoASWE5LtHbX2qFQ=
|
||||
github.com/facebookincubator/nvdtools v0.1.5/go.mod h1:Kh55SAWnjckS96TBSrXI99KrEKH4iB0OJby3N8GRJO4=
|
||||
@@ -408,24 +406,24 @@ github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/github/go-spdx/v2 v2.4.0 h1:+4IwVwJJbm3rzvrQ6P1nI9BDMcy3la4RchRy5uehV/M=
|
||||
github.com/github/go-spdx/v2 v2.4.0/go.mod h1:/5rwgS0txhGtRdUZwc02bTglzg6HK3FfuEbECKlK2Sg=
|
||||
github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
|
||||
github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-snaps v0.5.21 h1:SvhSFeZviQXwlT+dnGyAIATVehkhqRVW6qfQZhCZH+Y=
|
||||
github.com/gkampitakis/go-snaps v0.5.21/go.mod h1:gC3YqxQTPyIXvQrw/Vpt3a8VqR1MO8sVpZFWN4DGwNs=
|
||||
github.com/github/go-spdx/v2 v2.7.0 h1:GzfXx4wFdlilARxmFRXW/mgUy3A4vSqZocCMFV6XFdQ=
|
||||
github.com/github/go-spdx/v2 v2.7.0/go.mod h1:Ftc45YYG1WzpzwEPKRVm9Jv8vDqOrN4gWoCkK+bHer0=
|
||||
github.com/gkampitakis/ciinfo v0.3.4 h1:5eBSibVuSMbb/H6Elc0IIEFbkzCJi3lm94n0+U7Z0KY=
|
||||
github.com/gkampitakis/ciinfo v0.3.4/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-snaps v0.5.22 h1:xg9omphRnbDnimMCl1KqznC4krlxOGpkB0vDSfX2P7M=
|
||||
github.com/gkampitakis/go-snaps v0.5.22/go.mod h1:uy3lVzCCRRsAwYqSocyw5fY8xRLCYEfqoOJNxr8HonM=
|
||||
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
|
||||
github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
|
||||
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
|
||||
github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
|
||||
github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.18.0 h1:O831KI+0PR51hM2kep6T8k+w0/LIAD490gvqMCvL5hM=
|
||||
github.com/go-git/go-git/v5 v5.18.0/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo=
|
||||
github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00=
|
||||
github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -511,8 +509,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-containerregistry v0.21.5 h1:KTJG9Pn/jC0VdZR6ctV3/jcN+q6/Iqlx0sTVz3ywZlM=
|
||||
github.com/google/go-containerregistry v0.21.5/go.mod h1:ySvMuiWg+dOsRW0Hw8GYwfMwBlNRTmpYBFJPlkco5zU=
|
||||
github.com/google/go-containerregistry v0.21.7 h1:/vPFuVXDjtFREsVArW+0h1CIl5urnOhzei4X2DMW9IU=
|
||||
github.com/google/go-containerregistry v0.21.7/go.mod h1:kjSbt7/zMsKLWfnHrIvKvhXHUw91jbe9DNjPPJ32gXE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
|
||||
github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
|
||||
@@ -558,10 +556,10 @@ github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOID
|
||||
github.com/gookit/assert v0.1.1 h1:lh3GcawXe/p+cU7ESTZ5Ui3Sm/x8JWpIis4/1aF0mY0=
|
||||
github.com/gookit/assert v0.1.1/go.mod h1:jS5bmIVQZTIwk42uXl4lyj4iaaxx32tqH16CFj0VX2E=
|
||||
github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
|
||||
github.com/gookit/color v1.6.0 h1:JjJXBTk1ETNyqyilJhkTXJYYigHG24TM9Xa2M1xAhRA=
|
||||
github.com/gookit/color v1.6.0/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
|
||||
github.com/gpustack/gguf-parser-go v0.24.0 h1:tdJceXYp9e5RhE9RwVYIuUpir72Jz2D68NEtDXkKCKc=
|
||||
github.com/gpustack/gguf-parser-go v0.24.0/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
|
||||
github.com/gookit/color v1.6.1 h1:KoTnDxJPRgrL0SoX0f8rCFg2zI0t4E3GZZBMo2nN8LU=
|
||||
github.com/gookit/color v1.6.1/go.mod h1:9ACFc7/1IpHGBW8RwuDm/0YEnhg3dwwXpoMsmtyHfjs=
|
||||
github.com/gpustack/gguf-parser-go v0.24.1 h1:nTYtL8HFK6ZhB90RKBu4oX2b3ZHpJLrMmKRfL9w9Cyc=
|
||||
github.com/gpustack/gguf-parser-go v0.24.1/go.mod h1:y4TwTtDqFWTK+xvprOjRUh+dowgU2TKCX37vRKvGiZ0=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72 h1:vTCWu1wbdYo7PEZFem/rlr01+Un+wwVmI7wiegFdRLk=
|
||||
github.com/hashicorp/aws-sdk-go-base/v2 v2.0.0-beta.72/go.mod h1:Vn+BBgKQHVQYdVQ4NZDICE1Brb+JfaONyDHr3q07oQc=
|
||||
@@ -622,12 +620,12 @@ github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/jsonschema v0.13.0 h1:KvpoAJWEjR3uD9Kbm2HWJmqsEaHt8lBUpd0qHcIi21E=
|
||||
github.com/invopop/jsonschema v0.13.0/go.mod h1:ffZ5Km5SWWRAIN6wbDXItl95euhFz2uON45H2qjYt+0=
|
||||
github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8iyZjg=
|
||||
github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jedib0t/go-pretty/v6 v6.7.8 h1:BVYrDy5DPBA3Qn9ICT+PokP9cvCv1KaHv2i+Hc8sr5o=
|
||||
github.com/jedib0t/go-pretty/v6 v6.7.8/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
||||
github.com/jedib0t/go-pretty/v6 v6.8.1 h1:0fkCNhjrX0zPpwkWaDYU5VMrygg41Tu197mWILIJoqQ=
|
||||
github.com/jedib0t/go-pretty/v6 v6.8.1/go.mod h1:YwC5CE4fJ1HFUDeivSV1r//AmANFHyqczZk+U6BDALU=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
@@ -646,9 +644,11 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -671,10 +671,9 @@ github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc8
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
|
||||
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
|
||||
github.com/maruel/natural v1.3.0 h1:VsmCsBmEyrR46RomtgHs5hbKADGRVtliHTyCOLFBpsg=
|
||||
github.com/maruel/natural v1.3.0/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
@@ -692,8 +691,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZMYDR+NGImiFvErt6VWfIRPuGM+vyjiEdkmIw=
|
||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
|
||||
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
@@ -723,10 +722,10 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
|
||||
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
|
||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/moby/api v1.54.1 h1:TqVzuJkOLsgLDDwNLmYqACUuTehOHRGKiPhvH8V3Nn4=
|
||||
github.com/moby/moby/api v1.54.1/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
|
||||
github.com/moby/moby/client v0.4.0 h1:S+2XegzHQrrvTCvF6s5HFzcrywWQmuVnhOXe2kiWjIw=
|
||||
github.com/moby/moby/client v0.4.0/go.mod h1:QWPbvWchQbxBNdaLSpoKpCdf5E+WxFAgNHogCWDoa7g=
|
||||
github.com/moby/moby/api v1.54.2 h1:wiat9QAhnDQjA7wk1kh/TqHz2I1uUA7M7t9SAl/JNXg=
|
||||
github.com/moby/moby/api v1.54.2/go.mod h1:+RQ6wluLwtYaTd1WnPLykIDPekkuyD/ROWQClE83pzs=
|
||||
github.com/moby/moby/client v0.4.1 h1:DMQgisVoMkmMs7fp3ROSdiBnoAu8+vo3GggFl06M/wY=
|
||||
github.com/moby/moby/client v0.4.1/go.mod h1:z52C9O2POPOsnxZAy//WtKcQ32P+jT/NGeXu/7nfjGQ=
|
||||
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
|
||||
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
|
||||
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
|
||||
@@ -776,22 +775,24 @@ github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJw
|
||||
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
|
||||
github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg=
|
||||
github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.13.1 h1:A8nNeceYngH9Ow++M+VVEwJVpdFmrlxsN22F+ISDCJE=
|
||||
github.com/opencontainers/selinux v1.13.1/go.mod h1:S10WXZ/osk2kWOYKy1x2f/eXF5ZHJoUs8UU/2caNRbg=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY=
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ=
|
||||
github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
|
||||
github.com/pborman/indent v1.2.1/go.mod h1:FitS+t35kIYtB5xWTZAPhnmrxcciEEOdbyrrpz5K6Vw=
|
||||
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
|
||||
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
|
||||
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc=
|
||||
github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY=
|
||||
github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
|
||||
github.com/piprate/json-gold v0.7.0 h1:bEMirgA5y8Z2loTQfxyIFfY+EflxH1CTP6r/KIlcJNw=
|
||||
github.com/piprate/json-gold v0.7.0/go.mod h1:RVhE35veDX19r5gfUAR+IYHkAUuPwJO8Ie/qVeFaIzw=
|
||||
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
|
||||
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
@@ -800,8 +801,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
||||
github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pkg/xattr v0.4.9 h1:5883YPCtkSd8LFbs13nXplj9g9tlrwoJRjgpgMu1/fE=
|
||||
github.com/pkg/xattr v0.4.9/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM=
|
||||
github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo=
|
||||
github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
@@ -810,6 +811,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
@@ -822,8 +825,8 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.23 h1:lxjt5B6ZCiBeeNO8/oQsegE6fLeCzuMRoVWSkXC4uvY=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.23/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||
@@ -878,8 +881,8 @@ github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8/go.mod h1:3n1Cw
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb h1:bLo8hvc8XFm9J47r690TUKBzcjSWdJDxmjXJZ+/f92U=
|
||||
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
|
||||
github.com/spdx/tools-golang v0.5.7 h1:+sWcKGnhwp3vLdMqPcLdA6QK679vd86cK9hQWH3AwCg=
|
||||
github.com/spdx/tools-golang v0.5.7/go.mod h1:jg7w0LOpoNAw6OxKEzCoqPC2GCTj45LyTlVmXubDsYw=
|
||||
github.com/spdx/tools-golang v0.6.0-rc4 h1:2GkvNr0DcnJHY9BDm3OYHo229jZS/h4qYDK+tHYXPOo=
|
||||
github.com/spdx/tools-golang v0.6.0-rc4/go.mod h1:ruCHu3shgy7bVbZ7gtEU4Gq4fI08n2SdXtgV5PoN/OM=
|
||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
|
||||
@@ -921,17 +924,19 @@ github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/sylabs/sif/v2 v2.24.0 h1:1wB5uMDUQYjk8AckTySaDcP9YnpMb1LyDRr1Jt9A10w=
|
||||
github.com/sylabs/sif/v2 v2.24.0/go.mod h1:DbXWqWZ1hdLSU+K9ipdds5AmZeHWsyxCOj/oQakBa88=
|
||||
github.com/sylabs/sif/v2 v2.24.1 h1:OhTOfTwBaGXfbWYWXK9C6Pojma3A1bTaNJ6CEhyuKok=
|
||||
github.com/sylabs/sif/v2 v2.24.1/go.mod h1:PRq9MoP0g+p0qE5ZRGrOhyVAUdHrPsAVmXHgoiW6VU0=
|
||||
github.com/sylabs/squashfs v1.0.6 h1:PvJcDzxr+vIm2kH56mEMbaOzvGu79gK7P7IX+R7BDZI=
|
||||
github.com/sylabs/squashfs v1.0.6/go.mod h1:DlDeUawVXLWAsSRa085Eo0ZenGzAB32JdAUFaB0LZfE=
|
||||
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd h1:Rf9uhF1+VJ7ZHqxrG8pJ6YacmHvVCmByDmGbAWCc/gA=
|
||||
github.com/tailscale/hujson v0.0.0-20260302212456-ecc657c15afd/go.mod h1:EbW0wDK/qEUYI0A5bqq0C2kF8JTQwWONmGDBbzsxxHo=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.6/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
|
||||
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
||||
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
@@ -945,16 +950,12 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
|
||||
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/vbatts/go-mtree v0.7.0 h1:ytmOc3MTRidZiBi9VBCyZ2BHe4fZS47L5v7BVXDWW4E=
|
||||
github.com/vbatts/go-mtree v0.7.0/go.mod h1:EjdpFC+LZy1TXbRGNa1MKKgjQ+7ew3foMFJK8o4/TdY=
|
||||
github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=
|
||||
github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=
|
||||
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
|
||||
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
|
||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
|
||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20=
|
||||
github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0 h1:EHsPe0Q0ANoLOZff1dBLAyeWLTA4sbPTpGI+2zb0FnM=
|
||||
github.com/wagoodman/go-progress v0.0.0-20260303201901-10176f79b2c0/go.mod h1:g/D9uEUFp5YLyciwCpVsSOZOm56hfv4rzGJod6MlqIM=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -998,10 +999,10 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.39.0/go.mod h1:t/OGqzHBa5v6RHZwrDBJ2OirWc+4q/w2fTbLZwAKjTk=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
|
||||
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.40.0 h1:ZrPRak/kS4xI3AVXy8F7pipuDXmDsrO8Lg+yQjBLjw0=
|
||||
@@ -1022,6 +1023,8 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.2 h1:/FrI8D64VSr4HtGIlUtlFMGsm7H7pWTbj6vOLVZcA6s=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.2/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
|
||||
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -1036,8 +1039,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -1048,8 +1051,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY=
|
||||
golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -1077,8 +1080,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1123,8 +1126,8 @@ golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1156,8 +1159,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1232,13 +1235,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
|
||||
golang.org/x/term v0.42.0/go.mod h1:Dq/D+snpsbazcBG5+F9Q1n2rXV8Ma+71xEjTRufARgY=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1249,8 +1252,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
@@ -1311,16 +1314,16 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
|
||||
golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
|
||||
golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk=
|
||||
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -1427,10 +1430,10 @@ google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ6
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20 h1:7ei4lp52gK1uSejlA8AZl5AJjeLUOHBQscRQZUgAcu0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -1458,8 +1461,8 @@ google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnD
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
@@ -1474,8 +1477,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af h1:+5/Sw3GsDNlEmu7TfklWKPdQ0Ykja5VEmq2i817+jbI=
|
||||
google.golang.org/protobuf v1.36.12-0.20260120151049-f2248ac996af/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@@ -1506,10 +1509,10 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
modernc.org/cc/v4 v4.27.1 h1:9W30zRlYrefrDV2JE2O8VDtJ1yPGownxciz5rrbQZis=
|
||||
modernc.org/cc/v4 v4.27.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.32.0 h1:hjG66bI/kqIPX1b2yT6fr/jt+QedtP2fqojG2VrFuVw=
|
||||
modernc.org/ccgo/v4 v4.32.0/go.mod h1:6F08EBCx5uQc38kMGl+0Nm0oWczoo1c7cgpzEry7Uc0=
|
||||
modernc.org/cc/v4 v4.28.2 h1:3tQ0lf2ADtoby2EtSP+J7IE2SHwEJdP8ioR59wx7XpY=
|
||||
modernc.org/cc/v4 v4.28.2/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.34.0 h1:yRLPFZieg532OT4rp4JFNIVcquwalMX26G95WQDqwCQ=
|
||||
modernc.org/ccgo/v4 v4.34.0/go.mod h1:AS5WYMyBakQ+fhsHhtP8mWB82KTGPkNNJDGfGQCe0/A=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
@@ -1518,18 +1521,18 @@ modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
|
||||
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.70.0 h1:U58NawXqXbgpZ/dcdS9kMshu08aiA6b7gusEusqzNkw=
|
||||
modernc.org/libc v1.70.0/go.mod h1:OVmxFGP1CI/Z4L3E0Q3Mf1PDE0BucwMkcXjjLntvHJo=
|
||||
modernc.org/libc v1.72.3 h1:ZnDF4tXn4NBXFutMMQC4vtbTFSXhhKzR73fv0beZEAU=
|
||||
modernc.org/libc v1.72.3/go.mod h1:dn0dZNnnn1clLyvRxLxYExxiKRZIRENOfqQ8XEeg4Qs=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.46.2 h1:gkXQ6R0+AjxFC/fTDaeIVLbNLNrRoOK7YYVz5BKhTcE=
|
||||
modernc.org/sqlite v1.46.2/go.mod h1:hWjRO6Tj/5Ik8ieqxQybiEOUXy0NJFNp2tpvVpKlvig=
|
||||
modernc.org/sqlite v1.51.0 h1:aH/MMSoayAIhozZ7uJbVTT9QO/VhzBf0J9tymmmuC/U=
|
||||
modernc.org/sqlite v1.51.0/go.mod h1:tcNzv5p84E0skkmJn038y+hWJbLQXQqEnQfeh5r2JLM=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
||||
@@ -154,6 +154,7 @@ uname_arch_check() (
|
||||
mipsle) return 0 ;;
|
||||
mips64) return 0 ;;
|
||||
mips64le) return 0 ;;
|
||||
riscv64) return 0 ;;
|
||||
s390x) return 0 ;;
|
||||
amd64p32) return 0 ;;
|
||||
esac
|
||||
|
||||
@@ -297,6 +297,11 @@ func addCatalogerFieldComment(keyNode, valueNode *yaml.Node, catalogerName strin
|
||||
if keyNode.LineComment == "" {
|
||||
keyNode.LineComment = autoGeneratedComment
|
||||
}
|
||||
case "purl_types":
|
||||
// cataloger-level purl_types (for custom catalogers) are AUTO-GENERATED
|
||||
if keyNode.LineComment == "" {
|
||||
keyNode.LineComment = autoGeneratedComment
|
||||
}
|
||||
case "json_schema_types":
|
||||
// json_schema_types are AUTO-GENERATED
|
||||
if keyNode.LineComment == "" {
|
||||
@@ -367,7 +372,7 @@ func addParserComments(parsersNode *yaml.Node) {
|
||||
valueNode := parserNode.Content[i+1]
|
||||
|
||||
switch keyNode.Value {
|
||||
case "parser_function", "metadata_types", "package_types", "json_schema_types":
|
||||
case "parser_function", "metadata_types", "package_types", "purl_types", "json_schema_types":
|
||||
// add AUTO-GENERATED comment to these fields
|
||||
if keyNode.LineComment == "" {
|
||||
keyNode.LineComment = autoGeneratedComment
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"github.com/anchore/syft/internal/capabilities"
|
||||
"github.com/anchore/syft/internal/capabilities/internal"
|
||||
"github.com/anchore/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary"
|
||||
)
|
||||
|
||||
@@ -192,6 +194,9 @@ func RegenerateCapabilities(catalogerDir string, repoRoot string) (*Statistics,
|
||||
formatOrphans(orphans))
|
||||
}
|
||||
|
||||
// 5a. Derive PURL types from the discovered package types (purely a function of pkg.Type)
|
||||
derivePURLTypes(updated)
|
||||
|
||||
// 6. Write back to YAML files with comments, preserving existing node trees
|
||||
fmt.Print(" → Writing updated capabilities files...")
|
||||
if err := saveCapabilities(catalogerDir, repoRoot, updated, existingNodes); err != nil {
|
||||
@@ -427,6 +432,39 @@ func (e *EnrichmentData) GetMetadataTypes(catalogerName string) ([]string, bool)
|
||||
return types, ok
|
||||
}
|
||||
|
||||
// derivePURLTypes populates the PURLTypes field on every cataloger and parser by mapping each
|
||||
// observed package type to its canonical PURL type via pkg.Type.PackageURLType. this is a pure
|
||||
// function of the already-discovered package_types, so it needs no additional test observations.
|
||||
func derivePURLTypes(doc *capabilities.Document) {
|
||||
for i := range doc.Catalogers {
|
||||
cat := &doc.Catalogers[i]
|
||||
cat.PURLTypes = purlTypesFromPackageTypes(cat.PackageTypes)
|
||||
for j := range cat.Parsers {
|
||||
cat.Parsers[j].PURLTypes = purlTypesFromPackageTypes(cat.Parsers[j].PackageTypes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// purlTypesFromPackageTypes maps syft package type strings (e.g. "go-module") to their PURL types
|
||||
// (e.g. "golang"), returning a deduplicated, sorted slice. package types with no PURL mapping are skipped.
|
||||
func purlTypesFromPackageTypes(packageTypes []string) []string {
|
||||
if len(packageTypes) == 0 {
|
||||
return nil
|
||||
}
|
||||
set := strset.New()
|
||||
for _, pt := range packageTypes {
|
||||
if purlType := pkg.Type(pt).PackageURLType(); purlType != "" {
|
||||
set.Add(purlType)
|
||||
}
|
||||
}
|
||||
if set.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
result := set.List()
|
||||
sort.Strings(result)
|
||||
return result
|
||||
}
|
||||
|
||||
// GetPackageTypes returns the package types for the given cataloger name
|
||||
func (e *EnrichmentData) GetPackageTypes(catalogerName string) ([]string, bool) {
|
||||
types, ok := e.packageTypes[catalogerName]
|
||||
@@ -490,11 +528,15 @@ func (e *EnrichmentData) EnrichWithBinaryClassifier(catalogerName string, entry
|
||||
}
|
||||
|
||||
for _, o := range binaryClassifierOverrides[classifier.Class] {
|
||||
cpeStrings := make([]string, len(o.CPEs))
|
||||
for i, c := range o.CPEs {
|
||||
cpeStrings[i] = c.Attributes.BindToFmtString()
|
||||
}
|
||||
packages = append(packages, capabilities.DetectorPackageInfo{
|
||||
Class: o.Class,
|
||||
Name: o.Package,
|
||||
PURL: stripPURLVersion(o.PURL),
|
||||
CPEs: o.CPEs,
|
||||
CPEs: cpeStrings,
|
||||
Type: "BinaryPkg",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -552,3 +552,48 @@ func TestConvertToJSONSchemaTypesFromMetadata(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPURLTypesFromPackageTypes(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
packageTypes []string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
name: "nil slice returns nil",
|
||||
packageTypes: nil,
|
||||
want: nil,
|
||||
},
|
||||
{
|
||||
name: "single package type maps to purl type",
|
||||
packageTypes: []string{"go-module"},
|
||||
want: []string{"golang"},
|
||||
},
|
||||
{
|
||||
name: "multiple package types are sorted",
|
||||
packageTypes: []string{"npm", "go-module"},
|
||||
want: []string{"golang", "npm"},
|
||||
},
|
||||
{
|
||||
// java and jenkins-plugin both map to maven, so the result is deduplicated
|
||||
name: "distinct package types collapsing to one purl type are deduplicated",
|
||||
packageTypes: []string{"java-archive", "jenkins-plugin"},
|
||||
want: []string{"maven"},
|
||||
},
|
||||
{
|
||||
// unknown package types have no purl mapping and are skipped
|
||||
name: "package type without a purl mapping is skipped",
|
||||
packageTypes: []string{"UnknownPackage"},
|
||||
want: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := purlTypesFromPackageTypes(tt.packageTypes)
|
||||
if diff := cmp.Diff(tt.want, got); diff != "" {
|
||||
t.Errorf("purlTypesFromPackageTypes() mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ type binaryClassifierOverride struct {
|
||||
Class string
|
||||
Package string
|
||||
PURL string
|
||||
CPEs []string
|
||||
CPEs []cpe.CPE
|
||||
}
|
||||
|
||||
var binaryClassifierOverrides = map[string][]binaryClassifierOverride{
|
||||
@@ -96,12 +96,61 @@ var binaryClassifierOverrides = map[string][]binaryClassifierOverride{
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:jdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
"openssl-binary": {
|
||||
{
|
||||
Class: "openssl-binary-aws-lc",
|
||||
Package: "aws-lc",
|
||||
PURL: mustPURL("pkg:generic/aws-lc@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:amazon:aws_libcrypto:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "openssl-binary",
|
||||
Package: "openssl",
|
||||
PURL: mustPURL("pkg:generic/openssl@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
"mysqld-binary": {
|
||||
{
|
||||
Class: "mysqld-mysql-cluster-legacy-binary",
|
||||
Package: "mysql-cluster",
|
||||
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
Class: "mysqld-mysql-cluster-binary",
|
||||
Package: "mysql-cluster",
|
||||
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
Class: "mysqld-mysql-server-binary",
|
||||
Package: "mysql-server",
|
||||
PURL: mustPURL("pkg:generic/mysql-server@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func mustPURL(purl string) string {
|
||||
return purl
|
||||
}
|
||||
|
||||
func singleCPE(cpeString string, _ ...any) []string {
|
||||
return []string{cpeString}
|
||||
func singleCPE(cpeString string, source ...cpe.Source) []cpe.CPE {
|
||||
src := cpe.GeneratedSource
|
||||
if len(source) > 0 {
|
||||
src = source[0]
|
||||
}
|
||||
return []cpe.CPE{
|
||||
cpe.Must(cpeString, src),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ type CatalogerEntry struct {
|
||||
Detectors []Detector `yaml:"detectors,omitempty" json:"detectors,omitempty"` // AUTO-GENERATED - detection methods (only for type=custom)
|
||||
MetadataTypes []string `yaml:"metadata_types,omitempty" json:"metadata_types,omitempty"` // AUTO-GENERATED - pkg metadata types emitted (only for type=custom)
|
||||
PackageTypes []string `yaml:"package_types,omitempty" json:"package_types,omitempty"` // AUTO-GENERATED - package types emitted (only for type=custom)
|
||||
PURLTypes []string `yaml:"purl_types,omitempty" json:"purl_types,omitempty"` // AUTO-GENERATED - PURL types derived from package_types (only for type=custom)
|
||||
JSONSchemaTypes []string `yaml:"json_schema_types,omitempty" json:"json_schema_types,omitempty"` // AUTO-GENERATED - JSON schema type names (UpperCamelCase)
|
||||
Capabilities CapabilitySet `yaml:"capabilities,omitempty" json:"capabilities,omitempty"` // MANUAL - config-driven capability definitions (only for type=custom)
|
||||
}
|
||||
@@ -92,6 +93,7 @@ type Parser struct {
|
||||
Detector Detector `yaml:"detector" json:"detector"` // AUTO-GENERATED - how artifacts are detected
|
||||
MetadataTypes []string `yaml:"metadata_types,omitempty" json:"metadata_types,omitempty"` // AUTO-GENERATED - pkg metadata types emitted by this parser
|
||||
PackageTypes []string `yaml:"package_types,omitempty" json:"package_types,omitempty"` // AUTO-GENERATED - package types emitted by this parser
|
||||
PURLTypes []string `yaml:"purl_types,omitempty" json:"purl_types,omitempty"` // AUTO-GENERATED - PURL types derived from package_types
|
||||
JSONSchemaTypes []string `yaml:"json_schema_types,omitempty" json:"json_schema_types,omitempty"` // AUTO-GENERATED - JSON schema type names (UpperCamelCase)
|
||||
Capabilities CapabilitySet `yaml:"capabilities,omitempty" json:"capabilities,omitempty"` // MANUAL - config-driven capability definitions
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ package internal
|
||||
const (
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "16.1.3"
|
||||
JSONSchemaVersion = "16.1.5"
|
||||
|
||||
// Changelog
|
||||
// 16.1.0 - reformulated the python pdm fields (added "URL" and removed the unused "path" field).
|
||||
// 16.1.1 - correct elf package osCpe field according to the document of systemd (also add appCpe field)
|
||||
// 16.1.2 - placeholder for 16.1.2 changelog
|
||||
// 16.1.3 - add GGUFFileParts to GGUFFileHeader metadata
|
||||
// 16.1.4 - add BunLockEntry metadata type for bun.lock support
|
||||
// 16.1.5 - add DenoLockEntry and DenoRemoteLockEntry metadata types for deno.lock support
|
||||
|
||||
)
|
||||
|
||||
+13
-1
@@ -12,8 +12,20 @@ const perFileReadLimit = 2 * GB
|
||||
// protect against decompression bomb attacks.
|
||||
func safeCopy(writer io.Writer, reader io.Reader) error {
|
||||
numBytes, err := io.Copy(writer, io.LimitReader(reader, perFileReadLimit))
|
||||
if numBytes >= perFileReadLimit || errors.Is(err, io.EOF) {
|
||||
if numBytes >= perFileReadLimit {
|
||||
return fmt.Errorf("zip read limit hit (potential decompression bomb attack)")
|
||||
}
|
||||
// Propagate decompression / read errors up to the caller. io.Copy
|
||||
// on the happy path returns (n, nil); the only way err is non-nil
|
||||
// here is that the underlying reader surfaced a real failure
|
||||
// ("flate: corrupt input before offset X" on a mangled ZIP entry,
|
||||
// a network-backed reader erroring mid-stream, etc.). The previous
|
||||
// implementation dropped that error and the caller stored a
|
||||
// partial / empty buffer as a "successful" extract, which silently
|
||||
// downgraded Java cataloger output and caused SBOM scanners to
|
||||
// miss known CVEs (#4806).
|
||||
if err != nil && !errors.Is(err, io.EOF) {
|
||||
return fmt.Errorf("failed to read archive entry: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// errReader returns a deterministic non-EOF error after emitting some
|
||||
// bytes, mimicking what compress/flate does when it hits a corrupt
|
||||
// stream mid-entry.
|
||||
type errReader struct {
|
||||
data []byte
|
||||
err error
|
||||
off int
|
||||
}
|
||||
|
||||
func (r *errReader) Read(p []byte) (int, error) {
|
||||
if r.off >= len(r.data) {
|
||||
return 0, r.err
|
||||
}
|
||||
n := copy(p, r.data[r.off:])
|
||||
r.off += n
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func TestSafeCopy(t *testing.T) {
|
||||
t.Run("clean copy returns nil", func(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
if err := safeCopy(&buf, strings.NewReader("hello")); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if got := buf.String(); got != "hello" {
|
||||
t.Fatalf("unexpected buffer contents: %q", got)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("propagates decompression error", func(t *testing.T) {
|
||||
// #4806: safeCopy used to drop non-EOF errors, so the caller
|
||||
// would persist a partial buffer as a successful extract and
|
||||
// downstream catalogers silently read empty manifests.
|
||||
sentinel := errors.New("flate: corrupt input before offset 42")
|
||||
var buf bytes.Buffer
|
||||
err := safeCopy(&buf, &errReader{data: []byte("partial"), err: sentinel})
|
||||
if err == nil {
|
||||
t.Fatalf("expected error to be returned, got nil")
|
||||
}
|
||||
if !errors.Is(err, sentinel) {
|
||||
t.Fatalf("error does not wrap sentinel: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("EOF is not treated as an error", func(t *testing.T) {
|
||||
// The old code had a dead io.EOF branch that labelled clean
|
||||
// reads as decompression bombs; keep the happy path clean.
|
||||
var buf bytes.Buffer
|
||||
err := safeCopy(&buf, io.LimitReader(strings.NewReader("abc"), 3))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error on clean copy: %v", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/diskfs/go-diskfs/filesystem"
|
||||
)
|
||||
@@ -14,8 +15,7 @@ type WalkDiskDirFunc func(fsys filesystem.FileSystem, path string, d os.FileInfo
|
||||
// WalkDiskDir walks the file tree within the go-diskfs filesystem at root, calling fn for each file or directory in the tree, including root.
|
||||
// This is meant to mimic the behavior of fs.WalkDir in the standard library.
|
||||
func WalkDiskDir(fsys filesystem.FileSystem, root string, fn WalkDiskDirFunc) error {
|
||||
infos, err := fsys.ReadDir(root)
|
||||
|
||||
infos, err := readDiskDir(fsys, root)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -51,7 +51,7 @@ func walkDiskDir(fsys filesystem.FileSystem, name string, d os.FileInfo, walkDir
|
||||
|
||||
isDir := d != nil && d.IsDir()
|
||||
if d == nil {
|
||||
_, err := fsys.ReadDir(name)
|
||||
_, err := readDiskDir(fsys, name)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -62,7 +62,7 @@ func walkDiskDir(fsys filesystem.FileSystem, name string, d os.FileInfo, walkDir
|
||||
return nil
|
||||
}
|
||||
|
||||
dirs, err := fsys.ReadDir(name)
|
||||
dirs, err := readDiskDir(fsys, name)
|
||||
if err != nil {
|
||||
err = walkDirFn(fsys, name, d, err)
|
||||
if err != nil {
|
||||
@@ -87,3 +87,33 @@ func walkDiskDir(fsys filesystem.FileSystem, name string, d os.FileInfo, walkDir
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readDiskDir reads the directory entries at the given path from a go-diskfs filesystem.
|
||||
// go-diskfs returns fs.DirEntry values; these are resolved to os.FileInfo so callers have
|
||||
// access to the full file metadata (mode, size, modification time).
|
||||
func readDiskDir(fsys filesystem.FileSystem, p string) ([]os.FileInfo, error) {
|
||||
entries, err := fsys.ReadDir(ToFSPath(p))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
infos := make([]os.FileInfo, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
info, err := entry.Info()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
infos = append(infos, info)
|
||||
}
|
||||
return infos, nil
|
||||
}
|
||||
|
||||
// ToFSPath converts an absolute ("/"-rooted) path into an io/fs-valid path as required by
|
||||
// go-diskfs, where the root is "." and other paths carry no leading slash (see io/fs.ValidPath).
|
||||
func ToFSPath(p string) string {
|
||||
p = strings.TrimPrefix(p, "/")
|
||||
if p == "" {
|
||||
return "."
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -43,10 +43,10 @@ func createTestFS(t *testing.T) filesystem.FileSystem {
|
||||
|
||||
for _, tf := range testFiles {
|
||||
if tf.isDir {
|
||||
err := fsys.Mkdir(tf.path)
|
||||
err := fsys.Mkdir(ToFSPath(tf.path))
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
f, err := fsys.OpenFile(tf.path, os.O_CREATE|os.O_RDWR)
|
||||
f, err := fsys.OpenFile(ToFSPath(tf.path), os.O_CREATE|os.O_RDWR)
|
||||
require.NoError(t, err)
|
||||
_, err = f.Write([]byte(tf.content))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -6,9 +6,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/invopop/jsonschema"
|
||||
orderedmap "github.com/pb33f/ordered-map/v2"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
orderedmap "github.com/wk8/go-ordered-map/v2"
|
||||
)
|
||||
|
||||
// TestCopyAliasFieldComments verifies that field comments from source types are correctly copied to alias types.
|
||||
|
||||
@@ -11,6 +11,7 @@ func AllTypes() []any {
|
||||
pkg.ApkDBEntry{},
|
||||
pkg.BinarySignature{},
|
||||
pkg.BitnamiSBOMEntry{},
|
||||
pkg.BunLockEntry{},
|
||||
pkg.CocoaPodfileLockEntry{},
|
||||
pkg.ConanV1LockEntry{},
|
||||
pkg.ConanV2LockEntry{},
|
||||
@@ -19,6 +20,8 @@ func AllTypes() []any {
|
||||
pkg.CondaMetaPackage{},
|
||||
pkg.DartPubspec{},
|
||||
pkg.DartPubspecLockEntry{},
|
||||
pkg.DenoLockEntry{},
|
||||
pkg.DenoRemoteLockEntry{},
|
||||
pkg.DotnetDepsEntry{},
|
||||
pkg.DotnetPackagesLockEntry{},
|
||||
pkg.DotnetPortableExecutableEntry{},
|
||||
|
||||
@@ -73,6 +73,8 @@ var jsonTypes = makeJSONTypes(
|
||||
jsonNames(pkg.ConaninfoEntry{}, "c-conan-info-entry"),
|
||||
jsonNames(pkg.DartPubspecLockEntry{}, "dart-pubspec-lock-entry", "DartPubMetadata"),
|
||||
jsonNames(pkg.DartPubspec{}, "dart-pubspec"),
|
||||
jsonNames(pkg.DenoLockEntry{}, "deno-lock-entry"),
|
||||
jsonNames(pkg.DenoRemoteLockEntry{}, "deno-remote-lock-entry"),
|
||||
jsonNames(pkg.DotnetDepsEntry{}, "dotnet-deps-entry", "DotnetDepsMetadata"),
|
||||
jsonNames(pkg.DotnetPortableExecutableEntry{}, "dotnet-portable-executable-entry"),
|
||||
jsonNames(pkg.DpkgArchiveEntry{}, "dpkg-archive-entry"),
|
||||
@@ -96,6 +98,7 @@ var jsonTypes = makeJSONTypes(
|
||||
jsonNames(pkg.NpmPackageLockEntry{}, "javascript-npm-package-lock-entry", "NpmPackageLockJsonMetadata"),
|
||||
jsonNames(pkg.YarnLockEntry{}, "javascript-yarn-lock-entry", "YarnLockJsonMetadata"),
|
||||
jsonNames(pkg.PnpmLockEntry{}, "javascript-pnpm-lock-entry"),
|
||||
jsonNames(pkg.BunLockEntry{}, "javascript-bun-lock-entry"),
|
||||
jsonNames(pkg.PEBinary{}, "pe-binary"),
|
||||
jsonNames(pkg.PhpComposerLockEntry{}, "php-composer-lock-entry", "PhpComposerJsonMetadata"),
|
||||
jsonNamesWithoutLookup(pkg.PhpComposerInstalledEntry{}, "php-composer-installed-entry", "PhpComposerJsonMetadata"), // the legacy value is split into two types, where the other is preferred
|
||||
|
||||
@@ -65,6 +65,30 @@ func (i *Index) Add(relationships ...artifact.Relationship) {
|
||||
}
|
||||
|
||||
func (i *Index) Remove(id artifact.ID) {
|
||||
// scrub this node's edges from the adjacent nodes' maps before dropping the node itself. without this, the
|
||||
// edges live on as dangling pointers inside the other endpoints' mappedRelationships, so query methods
|
||||
// (From/To/References/Coordinates/Contains) would keep returning relationships that have been removed.
|
||||
if mapped := i.fromID[id]; mapped != nil {
|
||||
for _, sr := range mapped.allRelated {
|
||||
if sr.to == id {
|
||||
continue // self-edge: this node's own maps are deleted below
|
||||
}
|
||||
if other := i.toID[sr.to]; other != nil {
|
||||
other.remove(id, sr)
|
||||
}
|
||||
}
|
||||
}
|
||||
if mapped := i.toID[id]; mapped != nil {
|
||||
for _, sr := range mapped.allRelated {
|
||||
if sr.from == id {
|
||||
continue // self-edge: this node's own maps are deleted below
|
||||
}
|
||||
if other := i.fromID[sr.from]; other != nil {
|
||||
other.remove(id, sr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete(i.fromID, id)
|
||||
delete(i.toID, id)
|
||||
|
||||
@@ -78,6 +102,12 @@ func (i *Index) Remove(id artifact.ID) {
|
||||
}
|
||||
|
||||
func (i *Index) Replace(ogID artifact.ID, replacement artifact.Identifiable) {
|
||||
if replacement == nil || replacement.ID() == ogID {
|
||||
// replacing a node with one that has the same ID would re-add the (deduped) edges and then the trailing
|
||||
// Remove(ogID) would delete them, silently wiping all of the node's relationships. treat it as a no-op.
|
||||
return
|
||||
}
|
||||
|
||||
for _, mapped := range fromMappedByID(i.fromID, ogID) {
|
||||
// the stale relationship(i.e. if there's an elder ID in either side) should be discarded
|
||||
if len(fromMappedByID(i.toID, mapped.relationship.To.ID())) == 0 {
|
||||
@@ -91,8 +121,10 @@ func (i *Index) Replace(ogID artifact.ID, replacement artifact.Identifiable) {
|
||||
}
|
||||
|
||||
for _, mapped := range fromMappedByID(i.toID, ogID) {
|
||||
// same as the above
|
||||
if len(fromMappedByID(i.fromID, mapped.relationship.To.ID())) == 0 {
|
||||
// same as the above, but check the surviving other endpoint (the From side, since these are edges TO ogID).
|
||||
// note: this must reference From (not To, which is ogID itself) so that nodes appearing only on the To side
|
||||
// of relationships (e.g. a go main module that nothing depends on) keep their edges after an ID change.
|
||||
if len(fromMappedByID(i.fromID, mapped.relationship.From.ID())) == 0 {
|
||||
continue
|
||||
}
|
||||
i.Add(artifact.Relationship{
|
||||
@@ -206,6 +238,22 @@ func (m *mappedRelationships) add(id artifact.ID, newRelationship *sortableRelat
|
||||
typeMap[id] = newRelationship
|
||||
}
|
||||
|
||||
// remove deletes a single relationship (the other endpoint keyed by id) from this node's maps. it is the inverse
|
||||
// of add and is used to keep adjacent nodes consistent when a node is removed from the index.
|
||||
func (m *mappedRelationships) remove(id artifact.ID, target *sortableRelationship) {
|
||||
filtered := m.allRelated[:0]
|
||||
for _, r := range m.allRelated {
|
||||
if r != target {
|
||||
filtered = append(filtered, r)
|
||||
}
|
||||
}
|
||||
m.allRelated = filtered
|
||||
|
||||
if typeMap := m.typeMap[target.relationship.Type]; typeMap != nil && typeMap[id] == target {
|
||||
delete(typeMap, id)
|
||||
}
|
||||
}
|
||||
|
||||
type sortableRelationship struct {
|
||||
from artifact.ID
|
||||
to artifact.ID
|
||||
|
||||
@@ -380,6 +380,41 @@ func TestReplace(t *testing.T) {
|
||||
compareRelationships(t, expectedRels, index.All())
|
||||
}
|
||||
|
||||
func TestReplace_NodeOnlyOnToSide(t *testing.T) {
|
||||
// regression: a node that only appears on the To side of relationships (nothing depends on it, e.g. a go main
|
||||
// module that other modules are a dependency-of) must keep its incoming edges when its ID changes (such as when a
|
||||
// compliance rule stubs a missing version and recomputes the package ID). previously the To-side remap guard
|
||||
// checked ogID itself instead of the surviving From endpoint, so all such edges were dropped.
|
||||
main := pkg.Package{Name: "main-module"} // empty version, like a go main module
|
||||
dep1 := pkg.Package{Name: "dep-1", Version: "1.0.0"}
|
||||
dep2 := pkg.Package{Name: "dep-2", Version: "2.0.0"}
|
||||
|
||||
for _, p := range []*pkg.Package{&main, &dep1, &dep2} {
|
||||
p.SetID()
|
||||
}
|
||||
|
||||
// both deps are a dependency-of main; main has no outgoing edges
|
||||
r1 := artifact.Relationship{From: dep1, To: main, Type: artifact.DependencyOfRelationship}
|
||||
r2 := artifact.Relationship{From: dep2, To: main, Type: artifact.DependencyOfRelationship}
|
||||
|
||||
index := NewIndex(r1, r2)
|
||||
|
||||
// simulate the version stub: main gets a new ID
|
||||
stubbedMain := main
|
||||
stubbedMain.Version = "UNKNOWN"
|
||||
stubbedMain.SetID()
|
||||
require.NotEqual(t, main.ID(), stubbedMain.ID())
|
||||
|
||||
index.Replace(main.ID(), &stubbedMain)
|
||||
|
||||
expectedRels := []artifact.Relationship{
|
||||
{From: dep1, To: stubbedMain, Type: artifact.DependencyOfRelationship},
|
||||
{From: dep2, To: stubbedMain, Type: artifact.DependencyOfRelationship},
|
||||
}
|
||||
|
||||
compareRelationships(t, expectedRels, index.All())
|
||||
}
|
||||
|
||||
func compareRelationships(t testing.TB, expected, actual []artifact.Relationship) {
|
||||
assert.Equal(t, len(expected), len(actual), "number of relationships should match")
|
||||
for _, e := range expected {
|
||||
@@ -408,3 +443,94 @@ func TestReplace_NoExistingRelations(t *testing.T) {
|
||||
allRels := index.All()
|
||||
assert.Len(t, allRels, 0)
|
||||
}
|
||||
|
||||
// Remove must scrub the removed node's edges from adjacent nodes too, otherwise query methods keep returning
|
||||
// relationships that no longer exist. the original Remove only deleted the removed node's own maps, leaving
|
||||
// dangling pointers in the other endpoints.
|
||||
func TestRemove_NoStaleEdgesInAdjacentNodes(t *testing.T) {
|
||||
p1 := pkg.Package{Name: "pkg-1", Version: "1"}
|
||||
p2 := pkg.Package{Name: "pkg-2", Version: "2"}
|
||||
p3 := pkg.Package{Name: "pkg-3", Version: "3"}
|
||||
for _, p := range []*pkg.Package{&p1, &p2, &p3} {
|
||||
p.SetID()
|
||||
}
|
||||
|
||||
r12 := artifact.Relationship{From: p1, To: p2, Type: artifact.DependencyOfRelationship}
|
||||
r32 := artifact.Relationship{From: p3, To: p2, Type: artifact.DependencyOfRelationship}
|
||||
index := NewIndex(r12, r32)
|
||||
|
||||
index.Remove(p1.ID())
|
||||
|
||||
// p2 was on the To side of the removed edge; its incoming view must no longer include the stale p1 edge,
|
||||
// but must still include the live p3 edge.
|
||||
compareRelationships(t, []artifact.Relationship{r32}, index.To(p2, artifact.DependencyOfRelationship))
|
||||
assert.False(t, index.Contains(r12), "Contains must not report a removed edge")
|
||||
assert.Len(t, index.References(p2), 1, "p2 should only reference the surviving p3 edge")
|
||||
assert.True(t, index.Contains(r32), "surviving edge should remain")
|
||||
compareRelationships(t, []artifact.Relationship{r32}, index.All())
|
||||
}
|
||||
|
||||
// Replace must leave no dangling references to the old ID in adjacent nodes' query views.
|
||||
func TestReplace_NoStaleEdgesAfterReplace(t *testing.T) {
|
||||
main := pkg.Package{Name: "main-module"} // empty version
|
||||
dep := pkg.Package{Name: "dep", Version: "1.0.0"}
|
||||
main.SetID()
|
||||
dep.SetID()
|
||||
|
||||
oldEdge := artifact.Relationship{From: dep, To: main, Type: artifact.DependencyOfRelationship}
|
||||
index := NewIndex(oldEdge)
|
||||
|
||||
stubbed := main
|
||||
stubbed.Version = "UNKNOWN"
|
||||
stubbed.SetID()
|
||||
require.NotEqual(t, main.ID(), stubbed.ID())
|
||||
|
||||
index.Replace(main.ID(), &stubbed)
|
||||
|
||||
newEdge := artifact.Relationship{From: dep, To: stubbed, Type: artifact.DependencyOfRelationship}
|
||||
// the dep's outgoing view must point at the stubbed node, not the old one
|
||||
from := index.From(dep, artifact.DependencyOfRelationship)
|
||||
require.Len(t, from, 1)
|
||||
assert.Equal(t, stubbed.ID(), from[0].To.ID())
|
||||
assert.True(t, index.Contains(newEdge))
|
||||
assert.False(t, index.Contains(oldEdge), "stale edge to old ID must be gone")
|
||||
assert.Empty(t, index.To(main), "old node should have no incoming edges")
|
||||
compareRelationships(t, []artifact.Relationship{newEdge}, index.All())
|
||||
}
|
||||
|
||||
// replacing a node with one that has the same ID must be a no-op, not silently wipe its edges.
|
||||
func TestReplace_SameIDIsNoOp(t *testing.T) {
|
||||
a := pkg.Package{Name: "a", Version: "1"}
|
||||
b := pkg.Package{Name: "b", Version: "2"}
|
||||
a.SetID()
|
||||
b.SetID()
|
||||
edge := artifact.Relationship{From: a, To: b, Type: artifact.DependencyOfRelationship}
|
||||
index := NewIndex(edge)
|
||||
|
||||
// same identity object -> same ID
|
||||
index.Replace(b.ID(), b)
|
||||
|
||||
compareRelationships(t, []artifact.Relationship{edge}, index.All())
|
||||
assert.True(t, index.Contains(edge))
|
||||
}
|
||||
|
||||
// a self-edge on the replaced node should be remapped to replacement->replacement, not dropped.
|
||||
func TestReplace_SelfEdge(t *testing.T) {
|
||||
n := pkg.Package{Name: "n"} // empty version
|
||||
n.SetID()
|
||||
index := NewIndex(artifact.Relationship{From: n, To: n, Type: artifact.DependencyOfRelationship})
|
||||
|
||||
sn := n
|
||||
sn.Version = "UNKNOWN"
|
||||
sn.SetID()
|
||||
require.NotEqual(t, n.ID(), sn.ID())
|
||||
|
||||
index.Replace(n.ID(), &sn)
|
||||
|
||||
all := index.All()
|
||||
require.Len(t, all, 1)
|
||||
assert.Equal(t, sn.ID(), all[0].From.ID())
|
||||
assert.Equal(t, sn.ID(), all[0].To.ID())
|
||||
assert.Empty(t, index.From(n), "no edges should reference the old ID")
|
||||
assert.Empty(t, index.To(n), "no edges should reference the old ID")
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ func DefaultPackageTaskFactories() Factories {
|
||||
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
||||
return javascript.NewLockCataloger(cfg.PackagesConfig.JavaScript)
|
||||
},
|
||||
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, JavaScript, Node, NPM,
|
||||
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, JavaScript, Node, NPM, "deno",
|
||||
),
|
||||
newSimplePackageTaskFactory(php.NewComposerLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "php", "composer"),
|
||||
newSimplePackageTaskFactory(php.NewPearCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, pkgcataloging.ImageTag, "php", "pear"),
|
||||
@@ -161,7 +161,7 @@ func DefaultPackageTaskFactories() Factories {
|
||||
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary",
|
||||
),
|
||||
newSimplePackageTaskFactory(binary.NewELFPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "elf-package", "elf"),
|
||||
newSimplePackageTaskFactory(binary.NewPEPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "pe-package", "pe", "dll", "exe"),
|
||||
newSimplePackageTaskFactory(binary.NewPEPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "pe-package", "pe", "dll", "exe", "bpl"),
|
||||
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
||||
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
||||
newSimplePackageTaskFactory(java.NewJvmDistributionCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "java", "jvm", "jdk", "jre"),
|
||||
|
||||
+1385
-358
File diff suppressed because it is too large
Load Diff
+1848
-386
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+166
-1
@@ -2,7 +2,7 @@
|
||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
||||
elementFormDefault="qualified"
|
||||
targetNamespace="http://cyclonedx.org/schema/spdx"
|
||||
version="1.0-3.27.0">
|
||||
version="1.0-3.28.0">
|
||||
|
||||
<xs:simpleType name="licenseId">
|
||||
<xs:restriction base="xs:string">
|
||||
@@ -57,6 +57,11 @@
|
||||
<xs:documentation>Amazon Digital Services License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Advanced-Cryptics-Dictionary">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Advanced Cryptics Dictionary License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="AFL-1.1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Academic Free License v1.1</xs:documentation>
|
||||
@@ -122,6 +127,11 @@
|
||||
<xs:documentation>Aladdin Free Public License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ALGLIB-Documentation">
|
||||
<xs:annotation>
|
||||
<xs:documentation>ALGLIB Documentation License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="AMD-newlib">
|
||||
<xs:annotation>
|
||||
<xs:documentation>AMD newlib License</xs:documentation>
|
||||
@@ -327,6 +337,11 @@
|
||||
<xs:documentation>Boehm-Demers-Weiser GC License (without fee)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BOLA-1.1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Buena Onda License Agreement v1.1</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Borceux">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Borceux license</xs:documentation>
|
||||
@@ -457,6 +472,11 @@
|
||||
<xs:documentation>BSD 3-Clause Sun Microsystems</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BSD-3-Clause-Tso">
|
||||
<xs:annotation>
|
||||
<xs:documentation>BSD 3-Clause Tso variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BSD-4-Clause">
|
||||
<xs:annotation>
|
||||
<xs:documentation>BSD 4-Clause "Original" or "Old" License</xs:documentation>
|
||||
@@ -497,6 +517,11 @@
|
||||
<xs:documentation>BSD-Inferno-Nettverk</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BSD-Mark-Modifications">
|
||||
<xs:annotation>
|
||||
<xs:documentation>BSD Mark Modifications License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BSD-Protection">
|
||||
<xs:annotation>
|
||||
<xs:documentation>BSD Protection License</xs:documentation>
|
||||
@@ -527,6 +552,11 @@
|
||||
<xs:documentation>Boost Software License 1.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Buddy">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Buddy License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="BUSL-1.1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Business Source License 1.1</xs:documentation>
|
||||
@@ -567,6 +597,11 @@
|
||||
<xs:documentation>Caldera License (without preamble)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CAPEC-tou">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Common Attack Pattern Enumeration and Classification License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Catharon">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Catharon License</xs:documentation>
|
||||
@@ -1212,6 +1247,21 @@
|
||||
<xs:documentation>Erlang Public License v1.1</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ESA-PL-permissive-2.4">
|
||||
<xs:annotation>
|
||||
<xs:documentation>European Space Agency Public License – v2.4 – Permissive (Type 3)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ESA-PL-strong-copyleft-2.4">
|
||||
<xs:annotation>
|
||||
<xs:documentation>European Space Agency Public License (ESA-PL) - V2.4 - Strong Copyleft (Type 1)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ESA-PL-weak-copyleft-2.4">
|
||||
<xs:annotation>
|
||||
<xs:documentation>European Space Agency Public License – v2.4 – Weak Copyleft (Type 2)</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="etalab-2.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Etalab Open License 2.0</xs:documentation>
|
||||
@@ -1737,6 +1787,11 @@
|
||||
<xs:documentation>Historical Permission Notice and Disclaimer - sell variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HPND-sell-variant-critical-systems">
|
||||
<xs:annotation>
|
||||
<xs:documentation>HPND - sell variant with safety critical systems clause</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HPND-sell-variant-MIT-disclaimer">
|
||||
<xs:annotation>
|
||||
<xs:documentation>HPND sell variant with MIT disclaimer</xs:documentation>
|
||||
@@ -1747,6 +1802,11 @@
|
||||
<xs:documentation>HPND sell variant with MIT disclaimer - reverse</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HPND-SMC">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Historical Permission Notice and Disclaimer - SMC variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="HPND-UC">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Historical Permission Notice and Disclaimer - University of California variant</xs:documentation>
|
||||
@@ -1762,6 +1822,11 @@
|
||||
<xs:documentation>HTML Tidy License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="hyphen-bulgarian">
|
||||
<xs:annotation>
|
||||
<xs:documentation>hyphen-bulgarian License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="IBM-pibs">
|
||||
<xs:annotation>
|
||||
<xs:documentation>IBM PowerPC Initialization and Boot Software</xs:documentation>
|
||||
@@ -1852,6 +1917,11 @@
|
||||
<xs:documentation>ISC Veillard variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ISO-permission">
|
||||
<xs:annotation>
|
||||
<xs:documentation>ISO permission notice</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Jam">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Jam License</xs:documentation>
|
||||
@@ -2237,6 +2307,11 @@
|
||||
<xs:documentation>MIT Open Group variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MIT-STK">
|
||||
<xs:annotation>
|
||||
<xs:documentation>MIT-STK License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MIT-testregex">
|
||||
<xs:annotation>
|
||||
<xs:documentation>MIT testregex Variant</xs:documentation>
|
||||
@@ -2257,6 +2332,11 @@
|
||||
<xs:documentation>MMIXware License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="MMPL-1.0.1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Minecraft Mod Public License v1.0.1</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Motosoto">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Motosoto License</xs:documentation>
|
||||
@@ -2422,6 +2502,11 @@
|
||||
<xs:documentation>NIST Public Domain Notice with license fallback</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="NIST-PD-TNT">
|
||||
<xs:annotation>
|
||||
<xs:documentation>NIST Public Domain Notice TNT variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="NIST-Software">
|
||||
<xs:annotation>
|
||||
<xs:documentation>NIST Software License</xs:documentation>
|
||||
@@ -2687,6 +2772,11 @@
|
||||
<xs:documentation>Open Market License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="OpenMDW-1.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>OpenMDW License Agreement v1.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="OpenPBS-2.3">
|
||||
<xs:annotation>
|
||||
<xs:documentation>OpenPBS v2.3 Software License</xs:documentation>
|
||||
@@ -2722,6 +2812,11 @@
|
||||
<xs:documentation>Open Publication License v1.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="OSC-1.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>OSC License 1.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="OSET-PL-2.1">
|
||||
<xs:annotation>
|
||||
<xs:documentation>OSET Public License version 2.1</xs:documentation>
|
||||
@@ -2752,11 +2847,21 @@
|
||||
<xs:documentation>Open Software License 3.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="OSSP">
|
||||
<xs:annotation>
|
||||
<xs:documentation>OSSP License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="PADL">
|
||||
<xs:annotation>
|
||||
<xs:documentation>PADL License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="ParaType-Free-Font-1.3">
|
||||
<xs:annotation>
|
||||
<xs:documentation>ParaType Free Font Licensing Agreement v1.3</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Parity-6.0.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>The Parity Public License 6.0.0</xs:documentation>
|
||||
@@ -2977,6 +3082,11 @@
|
||||
<xs:documentation>SGI OpenGL License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SGMLUG-PM">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SGMLUG Parser Materials License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SGP4">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SGP4 Permission Notice</xs:documentation>
|
||||
@@ -3162,6 +3272,11 @@
|
||||
<xs:documentation>TCP Wrappers License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="TekHVC">
|
||||
<xs:annotation>
|
||||
<xs:documentation>TekHVC License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="TermReadKey">
|
||||
<xs:annotation>
|
||||
<xs:documentation>TermReadKey License</xs:documentation>
|
||||
@@ -3297,6 +3412,11 @@
|
||||
<xs:documentation>Unlicense - libwhirlpool variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="UnRAR">
|
||||
<xs:annotation>
|
||||
<xs:documentation>UnRAR License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="UPL-1.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Universal Permissive License v1.0</xs:documentation>
|
||||
@@ -3312,6 +3432,11 @@
|
||||
<xs:documentation>Vim License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Vixie-Cron">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Vixie Cron License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="VOSTROM">
|
||||
<xs:annotation>
|
||||
<xs:documentation>VOSTROM Public License for Open Source</xs:documentation>
|
||||
@@ -3352,11 +3477,21 @@
|
||||
<xs:documentation>Widget Workshop License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="WordNet">
|
||||
<xs:annotation>
|
||||
<xs:documentation>WordNet License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Wsuipa">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Wsuipa License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="WTFNMFPL">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Do What The F*ck You Want To But It's Not My Fault Public License</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="WTFPL">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Do What The F*ck You Want To Public License</xs:documentation>
|
||||
@@ -3382,6 +3517,11 @@
|
||||
<xs:documentation>X11 License Distribution Modification Variant</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="X11-no-permit-persons">
|
||||
<xs:annotation>
|
||||
<xs:documentation>X11 no permit persons clause</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="X11-swapped">
|
||||
<xs:annotation>
|
||||
<xs:documentation>X11 swapped final paragraphs</xs:documentation>
|
||||
@@ -3568,6 +3708,11 @@
|
||||
<xs:documentation>Classpath exception 2.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Classpath-exception-2.0-short">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Classpath exception 2.0 - short</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="CLISP-exception-2.0">
|
||||
<xs:annotation>
|
||||
<xs:documentation>CLISP exception 2.0</xs:documentation>
|
||||
@@ -3718,6 +3863,11 @@
|
||||
<xs:documentation>KiCad Libraries Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="kvirc-openssl-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>kvirc OpenSSL Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="LGPL-3.0-linking-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>LGPL-3.0 Linking Exception</xs:documentation>
|
||||
@@ -3833,6 +3983,11 @@
|
||||
<xs:documentation>RRDtool FLOSS exception 2.0</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="rsync-linking-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>rsync Linking Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="SANE-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>SANE Exception</xs:documentation>
|
||||
@@ -3848,6 +4003,16 @@
|
||||
<xs:documentation>Solderpad Hardware License v2.1</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="Simple-Library-Usage-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>Simple Library Usage Exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="sqlitestudio-OpenSSL-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>sqlitestudio OpenSSL exception</xs:documentation>
|
||||
</xs:annotation>
|
||||
</xs:enumeration>
|
||||
<xs:enumeration value="stunnel-exception">
|
||||
<xs:annotation>
|
||||
<xs:documentation>stunnel Exception</xs:documentation>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "anchore.io/schema/syft/json/16.1.3/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.1.5/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@@ -748,6 +748,45 @@
|
||||
],
|
||||
"description": "DartPubspecLockEntry is a struct that represents a single entry found in the \"packages\" section in a Dart pubspec.lock file."
|
||||
},
|
||||
"DenoLockEntry": {
|
||||
"properties": {
|
||||
"integrity": {
|
||||
"type": "string",
|
||||
"description": "Integrity is the crpto hash of the package content for verification"
|
||||
},
|
||||
"dependencies": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array",
|
||||
"description": "Dependencies is the list of package specifiers that this package depends on"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"integrity",
|
||||
"dependencies"
|
||||
],
|
||||
"description": "DenoLockEntry is a struct that rep a single entry found in the \"packages\" section of a Deno deno.lock file"
|
||||
},
|
||||
"DenoRemoteLockEntry": {
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string",
|
||||
"description": "URL is the remote URL from which the module fetcef"
|
||||
},
|
||||
"integrity": {
|
||||
"type": "string",
|
||||
"description": "Integrity is the crpto hash of the package content for verification"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"url",
|
||||
"integrity"
|
||||
],
|
||||
"description": "DenoRemoteLockEntry is a struct that rep a single entry found in the \"remote\" section of a Deno deno.lock file"
|
||||
},
|
||||
"Descriptor": {
|
||||
"properties": {
|
||||
"name": {
|
||||
@@ -1918,6 +1957,61 @@
|
||||
"type": "object",
|
||||
"description": "JavaVMRelease represents JVM version and build information extracted from the release file in a Java installation."
|
||||
},
|
||||
"JavascriptBunLockEntry": {
|
||||
"properties": {
|
||||
"integrity": {
|
||||
"type": "string",
|
||||
"description": "Integrity is Subresource Integrity hash for verification (SRI format)"
|
||||
},
|
||||
"dependencies": {
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "object",
|
||||
"description": "Dependencies is a map of runtime dependencies and their version specifiers"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "object",
|
||||
"description": "OptionalDependencies is a map of optional dependencies and their version specifiers"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "object",
|
||||
"description": "PeerDependencies is a map of peer dependencies and their version specifiers"
|
||||
},
|
||||
"bin": {
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "object",
|
||||
"description": "Bin is a map of binary names to the paths they are installed to"
|
||||
},
|
||||
"os": {
|
||||
"type": "string",
|
||||
"description": "OS is the operating system constraint for the package (e.g. \"darwin\")"
|
||||
},
|
||||
"cpu": {
|
||||
"type": "string",
|
||||
"description": "CPU is the CPU architecture constraint for the package (e.g. \"arm64\")"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"integrity",
|
||||
"dependencies",
|
||||
"optionalDependencies",
|
||||
"peerDependencies",
|
||||
"bin",
|
||||
"os",
|
||||
"cpu"
|
||||
],
|
||||
"description": "BunLockEntry represents a single entry in the \"packages\" section of a bun.lock file"
|
||||
},
|
||||
"JavascriptNpmPackage": {
|
||||
"properties": {
|
||||
"name": {
|
||||
@@ -2603,6 +2697,12 @@
|
||||
{
|
||||
"$ref": "#/$defs/DartPubspecLockEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DenoLockEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DenoRemoteLockEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DotnetDepsEntry"
|
||||
},
|
||||
@@ -2657,6 +2757,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/JavaJvmInstallation"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/JavascriptBunLockEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/JavascriptNpmPackage"
|
||||
},
|
||||
|
||||
+9
-5
@@ -15,14 +15,18 @@ fixtures: build verify
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
# for selfrando...
|
||||
# docker buildx build --platform linux/amd64 -t $(TOOL_IMAGE) .
|
||||
|
||||
tools:
|
||||
@(docker image inspect $(TOOL_IMAGE) > /dev/null 2>&1 && make tools-check) || (docker build -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
@if docker image inspect $(TOOL_IMAGE) > /dev/null 2>&1 \
|
||||
&& test -f Dockerfile.sha256 \
|
||||
&& sha256sum --quiet -c Dockerfile.sha256 2>/dev/null; then \
|
||||
: ; \
|
||||
else \
|
||||
docker build -t $(TOOL_IMAGE) . \
|
||||
&& sha256sum Dockerfile > Dockerfile.sha256; \
|
||||
fi
|
||||
|
||||
build: tools
|
||||
mkdir -p $(BIN)
|
||||
@@ -46,4 +50,4 @@ $(FINGERPRINT_FILE):
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(VERIFY_FILE) $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build verify debug clean
|
||||
.PHONY: tools build verify debug clean
|
||||
@@ -15,11 +15,15 @@ fixtures: build
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
tools-check:
|
||||
@sha256sum -c Dockerfile.sha256 || (echo "Tools Dockerfile has changed" && exit 1)
|
||||
|
||||
tools:
|
||||
@(docker image inspect $(TOOL_IMAGE) > /dev/null 2>&1 && make tools-check) || (docker build --platform linux/amd64 -t $(TOOL_IMAGE) . && sha256sum Dockerfile > Dockerfile.sha256)
|
||||
@if docker image inspect $(TOOL_IMAGE) > /dev/null 2>&1 \
|
||||
&& test -f Dockerfile.sha256 \
|
||||
&& sha256sum --quiet -c Dockerfile.sha256 2>/dev/null; then \
|
||||
: ; \
|
||||
else \
|
||||
docker build --platform linux/amd64 -t $(TOOL_IMAGE) . \
|
||||
&& sha256sum Dockerfile > Dockerfile.sha256; \
|
||||
fi
|
||||
|
||||
build: tools
|
||||
@mkdir -p $(BIN)
|
||||
@@ -38,4 +42,4 @@ $(FINGERPRINT_FILE):
|
||||
clean:
|
||||
rm -rf $(BIN) Dockerfile.sha256 $(VERIFY_FILE) $(FINGERPRINT_FILE)
|
||||
|
||||
.PHONY: tools tools-check build debug clean
|
||||
.PHONY: tools build debug clean
|
||||
|
||||
@@ -399,7 +399,7 @@ func collectDocRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out
|
||||
to = toLocation
|
||||
case helpers.OtherRelationship:
|
||||
// Encoding uses a specifically formatted comment...
|
||||
if strings.Index(r.RelationshipComment, string(artifact.EvidentByRelationship)) == 0 {
|
||||
if strings.HasPrefix(r.RelationshipComment, string(artifact.EvidentByRelationship)) {
|
||||
typ = artifact.EvidentByRelationship
|
||||
to = toLocation
|
||||
}
|
||||
@@ -418,7 +418,7 @@ func collectDocRelationships(spdxIDMap map[string]any, doc *spdx.Document) (out
|
||||
to = toPackage
|
||||
case helpers.OtherRelationship:
|
||||
// Encoding uses a specifically formatted comment...
|
||||
if strings.Index(r.RelationshipComment, string(artifact.OwnershipByFileOverlapRelationship)) == 0 {
|
||||
if strings.HasPrefix(r.RelationshipComment, string(artifact.OwnershipByFileOverlapRelationship)) {
|
||||
typ = artifact.OwnershipByFileOverlapRelationship
|
||||
to = toPackage
|
||||
}
|
||||
@@ -462,10 +462,10 @@ func collectPackageFileRelationships(spdxIDMap map[string]any, doc *spdx.Documen
|
||||
func toSyftCoordinates(f *spdx.File) file.Coordinates {
|
||||
const layerIDPrefix = "layerID: "
|
||||
var fileSystemID string
|
||||
if strings.Index(f.FileComment, layerIDPrefix) == 0 {
|
||||
if strings.HasPrefix(f.FileComment, layerIDPrefix) {
|
||||
fileSystemID = strings.TrimPrefix(f.FileComment, layerIDPrefix)
|
||||
}
|
||||
if strings.Index(string(f.FileSPDXIdentifier), layerIDPrefix) == 0 {
|
||||
if strings.HasPrefix(string(f.FileSPDXIdentifier), layerIDPrefix) {
|
||||
fileSystemID = strings.TrimPrefix(string(f.FileSPDXIdentifier), layerIDPrefix)
|
||||
}
|
||||
return file.Coordinates{
|
||||
@@ -481,8 +481,8 @@ func toSyftLocation(f *spdx.File) file.Location {
|
||||
|
||||
func requireAndTrimPrefix(val any, prefix string) string {
|
||||
if v, ok := val.(string); ok {
|
||||
if i := strings.Index(v, prefix); i == 0 {
|
||||
return strings.Replace(v, prefix, "", 1)
|
||||
if strings.HasPrefix(v, prefix) {
|
||||
return strings.TrimPrefix(v, prefix)
|
||||
}
|
||||
}
|
||||
return ""
|
||||
|
||||
@@ -0,0 +1,774 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
spdx "github.com/spdx/tools-golang/spdx/v3/v3_0"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/format/internal"
|
||||
"github.com/anchore/syft/syft/format/internal/spdxutil/helpers"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func ToSyftModelV3(doc *spdx.Document) (*sbom.SBOM, error) {
|
||||
if doc == nil {
|
||||
return nil, errors.New("cannot convert SPDX document to Syft model because document is nil")
|
||||
}
|
||||
|
||||
spdxMap := ptrMap[any]{}
|
||||
|
||||
s := &sbom.SBOM{
|
||||
Source: v3extractSource(spdxMap, doc),
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(),
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{},
|
||||
FileDigests: map[file.Coordinates][]file.Digest{},
|
||||
LinuxDistribution: v3findLinuxReleaseByPURL(doc),
|
||||
},
|
||||
}
|
||||
|
||||
relationships := v3relationshipMap(doc)
|
||||
|
||||
v3collectSyftPackages(s, spdxMap, relationships, doc)
|
||||
|
||||
v3collectSyftFiles(s, spdxMap, doc)
|
||||
|
||||
s.Relationships = v3toSyftRelationships(spdxMap, doc)
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
func v3removePackage(packages spdx.ElementList, remove spdx.AnyPackage) (pkgs spdx.ElementList) {
|
||||
for _, p := range packages {
|
||||
if p == remove {
|
||||
continue
|
||||
}
|
||||
pkgs = append(pkgs, p)
|
||||
}
|
||||
return pkgs
|
||||
}
|
||||
|
||||
func v3removeRelationships(elements spdx.ElementList, element spdx.AnyElement) (relations spdx.ElementList) {
|
||||
for _, e := range elements {
|
||||
if r, ok := e.(spdx.AnyRelationship); ok {
|
||||
if r != nil && r.GetFrom() == element {
|
||||
continue
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
var tos spdx.ElementList
|
||||
for _, to := range r.GetTo() {
|
||||
if to == element {
|
||||
continue
|
||||
}
|
||||
tos = append(tos, to)
|
||||
}
|
||||
r.SetTo(tos)
|
||||
if len(r.GetTo()) == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
relations = append(relations, r)
|
||||
} else {
|
||||
relations = append(relations, e)
|
||||
}
|
||||
}
|
||||
return relations
|
||||
}
|
||||
|
||||
func v3findRootPackages(doc spdx.AnyElementCollection) (out spdx.PackageList) {
|
||||
for _, p := range doc.GetRootElements().Packages() {
|
||||
out = append(out, p)
|
||||
}
|
||||
for _, s := range doc.GetRootElements().SBOMs() {
|
||||
for _, p := range s.GetRootElements().Packages() {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func v3extractSource(spdxMap ptrMap[any], doc *spdx.Document) source.Description {
|
||||
namespace := doc.ID
|
||||
if namespace == "" && len(doc.NamespaceMaps) > 0 {
|
||||
namespace = string(doc.NamespaceMaps[0].GetNamespace())
|
||||
}
|
||||
src := extractSourceFromNamespace(namespace)
|
||||
|
||||
rootPackages := v3findRootPackages(doc)
|
||||
|
||||
if len(rootPackages) != 1 {
|
||||
return src
|
||||
}
|
||||
|
||||
p := rootPackages[0]
|
||||
|
||||
switch p.GetPrimaryPurpose() {
|
||||
case spdx.SoftwarePurpose_Container:
|
||||
src = v3containerSource(p)
|
||||
case spdx.SoftwarePurpose_File:
|
||||
src = v3fileSource(p)
|
||||
default:
|
||||
return src
|
||||
}
|
||||
|
||||
spdxMap.Set(p, src)
|
||||
|
||||
doc.Elements = v3removePackage(doc.Elements, p)
|
||||
doc.Elements = v3removeRelationships(doc.Elements, p)
|
||||
|
||||
return src
|
||||
}
|
||||
|
||||
func v3containerSource(p spdx.AnyPackage) source.Description {
|
||||
container := p.GetName()
|
||||
v := p.GetVersion()
|
||||
if v != "" {
|
||||
container += ":" + v
|
||||
}
|
||||
|
||||
digest := ""
|
||||
if len(p.GetVerifiedUsing().Hashes()) > 0 {
|
||||
h := p.GetVerifiedUsing().Hashes()[0]
|
||||
if h != nil {
|
||||
digest = fmt.Sprintf("%s:%s", v3fromChecksumAlgorithm(h.GetAlgorithm()), h.GetValue())
|
||||
}
|
||||
}
|
||||
|
||||
supplier := ""
|
||||
if p.GetSuppliedBy() != nil {
|
||||
supplier = v3agentString(p.GetSuppliedBy())
|
||||
}
|
||||
|
||||
return source.Description{
|
||||
ID: p.GetID(),
|
||||
Name: p.GetName(),
|
||||
Version: p.GetVersion(),
|
||||
Supplier: supplier,
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: container,
|
||||
ID: p.GetID(),
|
||||
Layers: nil, // TODO handle formats with nested layer packages like Tern and K8s BOM tool
|
||||
ManifestDigest: digest,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func v3fileSource(p spdx.AnyPackage) source.Description {
|
||||
typeRegex := regexp.MustCompile("DocumentRoot-([^-]+)-.*$")
|
||||
typeName := typeRegex.ReplaceAllString(p.GetID(), "$1")
|
||||
|
||||
var version string
|
||||
var metadata any
|
||||
switch {
|
||||
case typeName == prefixDirectory:
|
||||
// is a Syft SBOM, explicitly a directory source
|
||||
metadata, version = v3directorySourceMetadata(p)
|
||||
case typeName == prefixFile:
|
||||
// is a Syft SBOM, explicitly a file source
|
||||
metadata, version = v3fileSourceMetadata(p)
|
||||
case isDirectory(p.GetName()):
|
||||
// is a non-Syft SBOM, which looks like a directory
|
||||
metadata, version = v3directorySourceMetadata(p)
|
||||
default:
|
||||
// is a non-Syft SBOM, which is probably a file
|
||||
metadata, version = v3fileSourceMetadata(p)
|
||||
}
|
||||
|
||||
supplier := ""
|
||||
if p.GetSuppliedBy() != nil {
|
||||
supplier = p.GetSuppliedBy().GetName()
|
||||
}
|
||||
|
||||
return source.Description{
|
||||
ID: p.GetID(),
|
||||
Name: p.GetName(),
|
||||
Version: version,
|
||||
Supplier: supplier,
|
||||
Metadata: metadata,
|
||||
}
|
||||
}
|
||||
|
||||
func v3fileSourceMetadata(p spdx.AnyPackage) (any, string) {
|
||||
version := p.GetVersion()
|
||||
|
||||
m := source.FileMetadata{
|
||||
Path: p.GetName(),
|
||||
}
|
||||
// if this is a Syft SBOM, we might have output a digest as the version
|
||||
checksum := v3toChecksum(p.GetVersion())
|
||||
for _, d := range p.GetVerifiedUsing().Hashes() {
|
||||
if checksum != nil && checksum.GetValue() == d.GetValue() {
|
||||
version = ""
|
||||
}
|
||||
m.Digests = append(m.Digests, file.Digest{
|
||||
Algorithm: v3fromChecksumAlgorithm(d.GetAlgorithm()),
|
||||
Value: d.GetValue(),
|
||||
})
|
||||
}
|
||||
|
||||
return m, version
|
||||
}
|
||||
|
||||
func v3directorySourceMetadata(p spdx.AnyPackage) (any, string) {
|
||||
return source.DirectoryMetadata{
|
||||
Path: p.GetName(),
|
||||
Base: "",
|
||||
}, p.GetVersion()
|
||||
}
|
||||
|
||||
func v3findLinuxReleaseByPURL(doc *spdx.Document) *linux.Release {
|
||||
for _, p := range doc.Elements.Packages() {
|
||||
purlValue := v3findPURLValue(p)
|
||||
if purlValue == "" {
|
||||
continue
|
||||
}
|
||||
purl, err := packageurl.FromString(purlValue)
|
||||
if err != nil {
|
||||
log.Warnf("unable to parse purl: %s", purlValue)
|
||||
continue
|
||||
}
|
||||
distro := v3findQualifierValue(purl, pkg.PURLQualifierDistro)
|
||||
if distro != "" {
|
||||
parts := strings.Split(distro, "-")
|
||||
name := parts[0]
|
||||
version := ""
|
||||
if len(parts) > 1 {
|
||||
version = parts[1]
|
||||
}
|
||||
return &linux.Release{
|
||||
PrettyName: name,
|
||||
Name: name,
|
||||
ID: name,
|
||||
IDLike: []string{name},
|
||||
Version: version,
|
||||
VersionID: version,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func v3collectSyftPackages(s *sbom.SBOM, spdxMap ptrMap[any], relationships ptrMap[[]spdx.AnyRelationship], doc *spdx.Document) {
|
||||
skipIDs := v3packageIDsToSkip(doc)
|
||||
found := ptrMap[struct{}]{}
|
||||
// tools-golang collects all elements from the JSON LD @graph to the root SpdxDocument.Elements property
|
||||
for _, elementList := range []spdx.ElementList{doc.Elements, doc.RootElements} {
|
||||
for _, p := range elementList.Packages() {
|
||||
if p == nil || skipIDs.Has(p) || found.Has(p) {
|
||||
continue
|
||||
}
|
||||
found.Set(p, struct{}{})
|
||||
syftPkg := v3toSyftPackage(relationships, p)
|
||||
spdxMap.Set(p, syftPkg)
|
||||
s.Artifacts.Packages.Add(syftPkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func v3collectSyftFiles(s *sbom.SBOM, spdxMap ptrMap[any], doc *spdx.Document) {
|
||||
found := ptrMap[struct{}]{}
|
||||
for _, elementList := range []spdx.ElementList{doc.Elements, doc.RootElements} {
|
||||
for _, f := range elementList.Files() {
|
||||
if found.Has(f) {
|
||||
continue
|
||||
}
|
||||
found.Set(f, struct{}{})
|
||||
l := v3toSyftLocation(f)
|
||||
spdxMap.Set(f, l)
|
||||
|
||||
s.Artifacts.FileMetadata[l.Coordinates] = v3toFileMetadata(f)
|
||||
s.Artifacts.FileDigests[l.Coordinates] = v3toFileDigests(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func v3toFileDigests(f spdx.AnyFile) (digests []file.Digest) {
|
||||
for _, h := range f.GetVerifiedUsing().Hashes() {
|
||||
digests = append(digests, file.Digest{
|
||||
Algorithm: v3fromChecksumAlgorithm(h.GetAlgorithm()),
|
||||
Value: h.GetValue(),
|
||||
})
|
||||
}
|
||||
return digests
|
||||
}
|
||||
|
||||
func v3fromChecksumAlgorithm(algorithm spdx.HashAlgorithm) string {
|
||||
// it might be better to have a specific case statement with constants
|
||||
parts := strings.Split(algorithm.GetID(), "/")
|
||||
return strings.ToLower(parts[len(parts)-1])
|
||||
}
|
||||
|
||||
func v3toFileMetadata(f spdx.AnyFile) (meta file.Metadata) {
|
||||
// FIXME Syft is currently lossy due to the SPDX 2.2.1 spec not supporting arbitrary mimetypes
|
||||
if f.GetContentType() != "" {
|
||||
meta.MIMEType = f.GetContentType()
|
||||
}
|
||||
return meta
|
||||
}
|
||||
|
||||
func v3toSyftRelationships(spdxMap ptrMap[any], doc *spdx.Document) []artifact.Relationship {
|
||||
out := v3collectDocRelationships(spdxMap, doc)
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
//nolint:gocognit
|
||||
func v3collectDocRelationships(spdxMap ptrMap[any], doc *spdx.Document) (out []artifact.Relationship) {
|
||||
for _, r := range doc.Elements.Relationships() {
|
||||
from := r.GetFrom()
|
||||
if from == nil || from.GetID() == "" {
|
||||
log.Debugf("ignoring relationship to external document: %+v", r)
|
||||
continue
|
||||
}
|
||||
a := spdxMap.Get(from)
|
||||
|
||||
for _, to := range r.GetTo() {
|
||||
b := spdxMap.Get(to)
|
||||
from, fromOk := a.(pkg.Package)
|
||||
toPackage, toPackageOk := b.(pkg.Package)
|
||||
toLocation, toLocationOk := b.(file.Location)
|
||||
//nolint:staticcheck
|
||||
if !fromOk || !(toPackageOk || toLocationOk) {
|
||||
log.Debugf("unable to find valid relationship mapping from SPDX, ignoring: (from: %+v) (to: %+v)", a, b)
|
||||
continue
|
||||
}
|
||||
var to artifact.Identifiable
|
||||
var typ artifact.RelationshipType
|
||||
if toLocationOk {
|
||||
switch r.GetType() {
|
||||
case spdx.RelationshipType_Contains:
|
||||
typ = artifact.ContainsRelationship
|
||||
to = toLocation
|
||||
case spdx.RelationshipType_Other:
|
||||
// Encoding uses a specifically formatted comment...
|
||||
if strings.HasPrefix(r.GetComment(), string(artifact.EvidentByRelationship)) {
|
||||
typ = artifact.EvidentByRelationship
|
||||
to = toLocation
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch r.GetType() {
|
||||
case spdx.RelationshipType_DependsOn:
|
||||
typ = artifact.DependencyOfRelationship
|
||||
to = from
|
||||
from = toPackage
|
||||
case spdx.RelationshipType_Contains:
|
||||
typ = artifact.ContainsRelationship
|
||||
to = toPackage
|
||||
case spdx.RelationshipType_Other:
|
||||
// Encoding uses a specifically formatted comment...
|
||||
if strings.HasPrefix(r.GetComment(), string(artifact.OwnershipByFileOverlapRelationship)) {
|
||||
typ = artifact.OwnershipByFileOverlapRelationship
|
||||
to = toPackage
|
||||
}
|
||||
}
|
||||
}
|
||||
if typ != "" && to != nil {
|
||||
out = append(out, artifact.Relationship{
|
||||
From: from,
|
||||
To: to,
|
||||
Type: typ,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func v3toSyftCoordinates(f spdx.AnyFile) file.Coordinates {
|
||||
const layerIDPrefix = "layerID: "
|
||||
var fileSystemID string
|
||||
if strings.HasPrefix(f.GetComment(), layerIDPrefix) {
|
||||
fileSystemID = strings.TrimPrefix(f.GetComment(), layerIDPrefix)
|
||||
}
|
||||
if strings.HasPrefix(f.GetID(), layerIDPrefix) {
|
||||
fileSystemID = strings.TrimPrefix(f.GetID(), layerIDPrefix)
|
||||
}
|
||||
return file.Coordinates{
|
||||
RealPath: f.GetName(),
|
||||
FileSystemID: fileSystemID,
|
||||
}
|
||||
}
|
||||
|
||||
func v3toSyftLocation(f spdx.AnyFile) file.Location {
|
||||
l := file.NewVirtualLocationFromCoordinates(v3toSyftCoordinates(f), f.GetName())
|
||||
return l
|
||||
}
|
||||
|
||||
func v3findQualifierValue(purl packageurl.PackageURL, qualifier string) string {
|
||||
for _, q := range purl.Qualifiers {
|
||||
if q.Key == qualifier {
|
||||
return q.Value
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func v3extractPkgInfo(p spdx.AnyPackage) pkgInfo {
|
||||
pu := v3findPURLValue(p)
|
||||
purl, err := packageurl.FromString(pu)
|
||||
if err != nil {
|
||||
return pkgInfo{}
|
||||
}
|
||||
return pkgInfo{
|
||||
purl,
|
||||
pkg.TypeByName(purl.Type),
|
||||
pkg.LanguageByName(purl.Type),
|
||||
}
|
||||
}
|
||||
|
||||
func v3toSyftPackage(relationships ptrMap[[]spdx.AnyRelationship], p spdx.AnyPackage) pkg.Package {
|
||||
info := v3extractPkgInfo(p)
|
||||
sP := &pkg.Package{
|
||||
Type: info.typ,
|
||||
Name: p.GetName(),
|
||||
Version: p.GetVersion(),
|
||||
Licenses: pkg.NewLicenseSet(v3parseSPDXLicenses(relationships, p)...),
|
||||
CPEs: v3extractCPEs(p),
|
||||
PURL: v3purlValue(info.purl),
|
||||
Language: info.lang,
|
||||
Metadata: v3extractMetadata(p, info),
|
||||
}
|
||||
|
||||
internal.Backfill(sP)
|
||||
|
||||
if p.GetID() != "" {
|
||||
// always prefer the IDs from the SBOM over derived IDs
|
||||
sP.OverrideID(artifact.ID(p.GetID()))
|
||||
} else {
|
||||
sP.SetID()
|
||||
}
|
||||
|
||||
return *sP
|
||||
}
|
||||
|
||||
func v3purlValue(purl packageurl.PackageURL) string {
|
||||
val := purl.String()
|
||||
if _, err := packageurl.FromString(val); err != nil {
|
||||
return ""
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func v3parseSPDXLicenses(relationships ptrMap[[]spdx.AnyRelationship], p spdx.AnyPackage) []pkg.License {
|
||||
licenses := make([]pkg.License, 0)
|
||||
|
||||
// licenses are defined with relationships in SPDX 3, see:
|
||||
// https://github.com/spdx/tools-golang/blob/spdx3/spdx/v3/v3_0/convert.go#L536
|
||||
rels := relationships.Get(p)
|
||||
for _, r := range rels {
|
||||
if r.GetType() == spdx.RelationshipType_HasConcludedLicense {
|
||||
licenses = append(licenses, v3toSyftLicenses(license.Concluded, r.GetTo().LicenseInfos()...)...)
|
||||
}
|
||||
if r.GetType() == spdx.RelationshipType_HasDeclaredLicense {
|
||||
licenses = append(licenses, v3toSyftLicenses(license.Declared, r.GetTo().LicenseInfos()...)...)
|
||||
}
|
||||
}
|
||||
|
||||
return licenses
|
||||
}
|
||||
|
||||
func v3toSyftLicenses(licenseType license.Type, licenses ...spdx.AnyLicenseInfo) []pkg.License {
|
||||
var out []pkg.License
|
||||
for _, lic := range licenses {
|
||||
if lic == nil {
|
||||
continue
|
||||
}
|
||||
value := v3licenseInfoToExpression(lic)
|
||||
if value == "" {
|
||||
log.Debugf("skipping SPDX license during import: %#v", lic)
|
||||
continue
|
||||
}
|
||||
l := pkg.NewLicense(value)
|
||||
if l.Value != "" {
|
||||
l.Type = licenseType
|
||||
out = append(out, l)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// v3licenseInfoToExpression recursively converts an SPDX 3.0 license info object to an SPDX expression string.
|
||||
func v3licenseInfoToExpression(info spdx.AnyLicenseInfo) string {
|
||||
switch li := info.(type) {
|
||||
case spdx.AnyLicenseExpression:
|
||||
return li.GetLicenseExpression()
|
||||
case spdx.AnyOrLaterOperator:
|
||||
subject := v3licenseInfoToExpression(li.GetSubjectLicense())
|
||||
if subject != "" && !strings.HasSuffix(subject, "+") {
|
||||
return subject + "+"
|
||||
}
|
||||
case spdx.AnyWithAdditionOperator:
|
||||
subject := v3licenseInfoToExpression(li.GetSubjectExtendableLicense())
|
||||
addition := li.GetSubjectAddition()
|
||||
if subject != "" && addition != nil {
|
||||
return subject + " WITH " + addition.GetAdditionText()
|
||||
}
|
||||
case spdx.AnyConjunctiveLicenseSet:
|
||||
var parts []string
|
||||
for _, m := range li.GetMembers() {
|
||||
if e := v3licenseInfoToExpression(m); e != "" {
|
||||
parts = append(parts, e)
|
||||
}
|
||||
}
|
||||
if len(parts) > 0 {
|
||||
return strings.Join(parts, " AND ")
|
||||
}
|
||||
case spdx.AnyDisjunctiveLicenseSet:
|
||||
var parts []string
|
||||
for _, m := range li.GetMembers() {
|
||||
if e := v3licenseInfoToExpression(m); e != "" {
|
||||
parts = append(parts, e)
|
||||
}
|
||||
}
|
||||
if len(parts) > 0 {
|
||||
return "(" + strings.Join(parts, " OR ") + ")"
|
||||
}
|
||||
case spdx.AnyListedLicense:
|
||||
return li.GetName()
|
||||
case spdx.AnyCustomLicense:
|
||||
if li.GetID() != "" {
|
||||
return li.GetID()
|
||||
}
|
||||
return li.GetName()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
//nolint:funlen
|
||||
func v3extractMetadata(p spdx.AnyPackage, info pkgInfo) any {
|
||||
arch := info.qualifierValue(pkg.PURLQualifierArch)
|
||||
upstreamValue := info.qualifierValue(pkg.PURLQualifierUpstream)
|
||||
upstream := strings.SplitN(upstreamValue, "@", 2)
|
||||
upstreamName := upstream[0]
|
||||
upstreamVersion := ""
|
||||
if len(upstream) > 1 {
|
||||
upstreamVersion = upstream[1]
|
||||
}
|
||||
supplier := ""
|
||||
if p.GetSuppliedBy() != nil {
|
||||
supplier = v3agentString(p.GetSuppliedBy())
|
||||
}
|
||||
originator := ""
|
||||
if len(p.GetOriginatedBy()) > 0 {
|
||||
// FIXME there could be multiple
|
||||
originator = v3agentString(p.GetOriginatedBy()[0])
|
||||
}
|
||||
switch info.typ {
|
||||
case pkg.ApkPkg:
|
||||
return pkg.ApkDBEntry{
|
||||
Package: p.GetName(),
|
||||
OriginPackage: upstreamName,
|
||||
Maintainer: supplier,
|
||||
Version: p.GetVersion(),
|
||||
Architecture: arch,
|
||||
URL: string(p.GetHomePage()),
|
||||
Description: p.GetDescription(),
|
||||
}
|
||||
case pkg.RpmPkg:
|
||||
converted, err := strconv.Atoi(info.qualifierValue(pkg.PURLQualifierEpoch))
|
||||
var epoch *int
|
||||
if err != nil {
|
||||
epoch = nil
|
||||
} else {
|
||||
epoch = &converted
|
||||
}
|
||||
return pkg.RpmDBEntry{
|
||||
Name: p.GetName(),
|
||||
Version: p.GetVersion(),
|
||||
Epoch: epoch,
|
||||
Arch: arch,
|
||||
SourceRpm: upstreamValue,
|
||||
Vendor: originator,
|
||||
}
|
||||
case pkg.DebPkg:
|
||||
return pkg.DpkgDBEntry{
|
||||
Package: p.GetName(),
|
||||
Source: upstreamName,
|
||||
Version: p.GetVersion(),
|
||||
SourceVersion: upstreamVersion,
|
||||
Architecture: arch,
|
||||
Maintainer: originator,
|
||||
}
|
||||
case pkg.JavaPkg:
|
||||
var digests []file.Digest
|
||||
for _, value := range p.GetVerifiedUsing() {
|
||||
h, _ := value.(spdx.AnyHash)
|
||||
if h != nil {
|
||||
digests = append(digests, file.Digest{Algorithm: v3fromChecksumAlgorithm(h.GetAlgorithm()), Value: h.GetValue()})
|
||||
}
|
||||
}
|
||||
return pkg.JavaArchive{
|
||||
ArchiveDigests: digests,
|
||||
}
|
||||
case pkg.GoModulePkg:
|
||||
var h1Digest string
|
||||
for _, value := range p.GetVerifiedUsing() {
|
||||
h, _ := value.(spdx.AnyHash)
|
||||
if h == nil {
|
||||
continue
|
||||
}
|
||||
digest, err := helpers.HDigestFromSHA(v3fromChecksumAlgorithm(h.GetAlgorithm()), h.GetValue())
|
||||
if err != nil {
|
||||
log.Debugf("invalid h1digest: %v %v", value, err)
|
||||
continue
|
||||
}
|
||||
h1Digest = digest
|
||||
break
|
||||
}
|
||||
return pkg.GolangBinaryBuildinfoEntry{
|
||||
H1Digest: h1Digest,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func v3agentString(agent spdx.AnyAgent) string {
|
||||
out := ""
|
||||
switch o := agent.(type) {
|
||||
case spdx.AnyOrganization:
|
||||
out = o.GetName()
|
||||
case spdx.AnyPerson:
|
||||
out = o.GetName()
|
||||
}
|
||||
if out == helpers.NOASSERTION {
|
||||
return ""
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func v3findPURLValue(p spdx.AnyPackage) string {
|
||||
if p.GetPackageURL() != "" {
|
||||
return string(p.GetPackageURL())
|
||||
}
|
||||
for _, r := range p.GetExternalIdentifiers() {
|
||||
if r.GetType() == spdx.ExternalIdentifierType_PackageURL {
|
||||
for _, l := range r.GetIdentifierLocators() {
|
||||
// FIXME multiple values
|
||||
return string(l)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func v3extractCPEs(p spdx.AnyPackage) (cpes []cpe.CPE) {
|
||||
for _, r := range p.GetExternalIdentifiers() {
|
||||
if r.GetType() == spdx.ExternalIdentifierType_Cpe23 || r.GetType() == spdx.ExternalIdentifierType_Cpe22 {
|
||||
c, err := cpe.New(r.GetIdentifier(), cpe.DeclaredSource)
|
||||
if err == nil {
|
||||
cpes = append(cpes, c)
|
||||
continue
|
||||
}
|
||||
log.Tracef("unable to extract SPDX IDENTIFIER CPE=%q: %+v", r.GetIdentifier(), err)
|
||||
// try the locators
|
||||
for _, l := range r.GetIdentifierLocators() {
|
||||
c, err = cpe.New(string(l), cpe.DeclaredSource)
|
||||
if err != nil {
|
||||
log.Tracef("unable to extract SPDX CPE=%q: %+v", l, err)
|
||||
continue
|
||||
}
|
||||
cpes = append(cpes, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cpes
|
||||
}
|
||||
|
||||
// v3packageIDsToSkip returns a set of packageIDs that should not be imported
|
||||
func v3packageIDsToSkip(doc *spdx.Document) ptrMap[struct{}] {
|
||||
skipIDs := ptrMap[struct{}]{}
|
||||
for _, r := range doc.Elements.Relationships() {
|
||||
if r != nil && r.GetFrom() != nil && r.GetType() == spdx.RelationshipType_Generates {
|
||||
skipIDs.Set(r.GetFrom(), struct{}{}) // flipped from GENERATED_FROM
|
||||
}
|
||||
}
|
||||
return skipIDs
|
||||
}
|
||||
|
||||
// toChecksum takes a checksum in the format <algorithm>:<hash> and returns an spdx.Checksum or nil if the string is invalid
|
||||
func v3toChecksum(algorithmHash string) spdx.AnyHash {
|
||||
parts := strings.Split(algorithmHash, ":")
|
||||
if len(parts) < 2 {
|
||||
return nil
|
||||
}
|
||||
return &spdx.Hash{
|
||||
Algorithm: v3toChecksumAlgorithm(parts[0]),
|
||||
Value: parts[1],
|
||||
}
|
||||
}
|
||||
|
||||
func v3toChecksumAlgorithm(algorithm string) spdx.HashAlgorithm {
|
||||
// this needs to be an uppercase version of our algorithm
|
||||
switch strings.ToLower(algorithm) {
|
||||
case "sha1":
|
||||
return spdx.HashAlgorithm_Sha1
|
||||
case "sha256":
|
||||
return spdx.HashAlgorithm_Sha256
|
||||
case "sha384":
|
||||
return spdx.HashAlgorithm_Sha384
|
||||
case "sha512":
|
||||
return spdx.HashAlgorithm_Sha512
|
||||
case "md5":
|
||||
return spdx.HashAlgorithm_Md5
|
||||
}
|
||||
return spdx.HashAlgorithm{}
|
||||
}
|
||||
|
||||
func v3relationshipMap(doc *spdx.Document) ptrMap[[]spdx.AnyRelationship] {
|
||||
relationships := ptrMap[[]spdx.AnyRelationship]{}
|
||||
for _, r := range doc.Elements.Relationships() {
|
||||
existing := relationships.Get(r.GetFrom())
|
||||
relationships.Set(r.GetFrom(), append(existing, r))
|
||||
}
|
||||
return relationships
|
||||
}
|
||||
|
||||
// SPDX 3 values are stored as pointers, and there is a distinct possibility that an ID
|
||||
// will be blank if it was a JSON-LD blank node in the document, as these IDs are not persisted
|
||||
// nor should they be persisted from decoding
|
||||
|
||||
type ptrMap[T any] map[reflect.Value]T
|
||||
|
||||
func (s ptrMap[T]) Set(k any, v T) {
|
||||
s[ptrTo(k)] = v
|
||||
}
|
||||
|
||||
func (s ptrMap[T]) Get(k any) T {
|
||||
return s[ptrTo(k)]
|
||||
}
|
||||
|
||||
func (s ptrMap[T]) Remove(k any) {
|
||||
delete(s, ptrTo(k))
|
||||
}
|
||||
|
||||
func (s ptrMap[T]) Has(k any) bool {
|
||||
_, ok := s[ptrTo(k)]
|
||||
return ok
|
||||
}
|
||||
|
||||
func ptrTo(k any) reflect.Value {
|
||||
rv := reflect.ValueOf(k)
|
||||
if rv.Kind() != reflect.Pointer {
|
||||
// this case is a programming problem -- all objects in the SPDX 3 model are pointers and may not have IDs set
|
||||
panic(fmt.Errorf("value is not a pointer; comparable SPDX 3 elements are pointers, this is probably an implementation issue: %#v", k))
|
||||
}
|
||||
return rv
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
package spdxhelpers
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
spdx "github.com/spdx/tools-golang/spdx/v3/v3_0"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
func TestToSyftModelV3_documentConversion(t *testing.T) {
|
||||
// Build SPDX 3.0 elements
|
||||
pkg1 := &spdx.Package{
|
||||
ID: "pkg-1-id",
|
||||
Name: "pkg-1",
|
||||
Version: "1.0.0",
|
||||
ExternalIdentifiers: spdx.ExternalIdentifierList{
|
||||
&spdx.ExternalIdentifier{
|
||||
Type: spdx.ExternalIdentifierType_PackageURL,
|
||||
Identifier: "pkg:npm/pkg-1@1.0.0",
|
||||
IdentifierLocators: []spdx.URI{"pkg:npm/pkg-1@1.0.0"},
|
||||
},
|
||||
},
|
||||
}
|
||||
pkg2 := &spdx.Package{
|
||||
ID: "pkg-2-id",
|
||||
Name: "pkg-2",
|
||||
Version: "2.0.0",
|
||||
ExternalIdentifiers: spdx.ExternalIdentifierList{
|
||||
&spdx.ExternalIdentifier{
|
||||
Type: spdx.ExternalIdentifierType_PackageURL,
|
||||
Identifier: "pkg:npm/pkg-2@2.0.0",
|
||||
IdentifierLocators: []spdx.URI{"pkg:npm/pkg-2@2.0.0"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
file1 := &spdx.File{
|
||||
ID: "file-1-id",
|
||||
Name: "/src/main.go",
|
||||
VerifiedUsing: spdx.IntegrityMethodList{
|
||||
&spdx.Hash{
|
||||
Algorithm: spdx.HashAlgorithm_Sha256,
|
||||
Value: "abc123",
|
||||
},
|
||||
},
|
||||
}
|
||||
file2 := &spdx.File{
|
||||
ID: "file-2-id",
|
||||
Name: "/src/util.go",
|
||||
}
|
||||
|
||||
// licenses
|
||||
pkg1ConcludedLicense := &spdx.ListedLicense{Name: "MIT"}
|
||||
pkg1DeclaredLicense := &spdx.DisjunctiveLicenseSet{
|
||||
Members: spdx.LicenseInfoList{
|
||||
&spdx.ListedLicense{Name: "MIT"},
|
||||
&spdx.ListedLicense{Name: "Apache-2.0"},
|
||||
},
|
||||
}
|
||||
pkg2ConcludedLicense := &spdx.ConjunctiveLicenseSet{
|
||||
Members: spdx.LicenseInfoList{
|
||||
&spdx.ListedLicense{Name: "BSD-3-Clause"},
|
||||
&spdx.OrLaterOperator{
|
||||
SubjectLicense: &spdx.ListedLicense{Name: "GPL-2.0"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// relationships
|
||||
pkg1DependsOnPkg2 := &spdx.Relationship{
|
||||
From: pkg1,
|
||||
To: spdx.ElementList{pkg2},
|
||||
Type: spdx.RelationshipType_DependsOn,
|
||||
}
|
||||
pkg1ContainsFile1 := &spdx.Relationship{
|
||||
From: pkg1,
|
||||
To: spdx.ElementList{file1},
|
||||
Type: spdx.RelationshipType_Contains,
|
||||
}
|
||||
pkg2ContainsFile2 := &spdx.Relationship{
|
||||
From: pkg2,
|
||||
To: spdx.ElementList{file2},
|
||||
Type: spdx.RelationshipType_Contains,
|
||||
}
|
||||
pkg1HasConcludedLicense := &spdx.Relationship{
|
||||
From: pkg1,
|
||||
To: spdx.ElementList{pkg1ConcludedLicense},
|
||||
Type: spdx.RelationshipType_HasConcludedLicense,
|
||||
}
|
||||
pkg1HasDeclaredLicense := &spdx.Relationship{
|
||||
From: pkg1,
|
||||
To: spdx.ElementList{pkg1DeclaredLicense},
|
||||
Type: spdx.RelationshipType_HasDeclaredLicense,
|
||||
}
|
||||
pkg2HasConcludedLicense := &spdx.Relationship{
|
||||
From: pkg2,
|
||||
To: spdx.ElementList{pkg2ConcludedLicense},
|
||||
Type: spdx.RelationshipType_HasConcludedLicense,
|
||||
}
|
||||
|
||||
sbomElement := &spdx.SBOM{
|
||||
RootElements: spdx.ElementList{pkg1},
|
||||
Elements: spdx.ElementList{
|
||||
pkg1, pkg2,
|
||||
file1, file2,
|
||||
pkg1DependsOnPkg2,
|
||||
pkg1ContainsFile1,
|
||||
pkg2ContainsFile2,
|
||||
pkg1HasConcludedLicense,
|
||||
pkg1HasDeclaredLicense,
|
||||
pkg2HasConcludedLicense,
|
||||
},
|
||||
}
|
||||
|
||||
doc := &spdx.Document{
|
||||
SpdxDocument: spdx.SpdxDocument{
|
||||
ID: "https://example.org/test-doc",
|
||||
Name: "test-document",
|
||||
RootElements: spdx.ElementList{sbomElement},
|
||||
Elements: spdx.ElementList{
|
||||
sbomElement,
|
||||
pkg1, pkg2,
|
||||
file1, file2,
|
||||
pkg1DependsOnPkg2,
|
||||
pkg1ContainsFile1,
|
||||
pkg2ContainsFile2,
|
||||
pkg1HasConcludedLicense,
|
||||
pkg1HasDeclaredLicense,
|
||||
pkg2HasConcludedLicense,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Convert
|
||||
result, err := ToSyftModelV3(doc)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, result)
|
||||
|
||||
// Verify packages
|
||||
allPkgs := result.Artifacts.Packages.Sorted()
|
||||
require.Len(t, allPkgs, 2, "expected 2 packages")
|
||||
|
||||
pkgsByName := map[string]pkg.Package{}
|
||||
for _, p := range allPkgs {
|
||||
pkgsByName[p.Name] = p
|
||||
}
|
||||
|
||||
syftPkg1, ok := pkgsByName["pkg-1"]
|
||||
require.True(t, ok, "pkg-1 not found")
|
||||
assert.Equal(t, "1.0.0", syftPkg1.Version)
|
||||
assert.Equal(t, "pkg:npm/pkg-1@1.0.0", syftPkg1.PURL)
|
||||
|
||||
syftPkg2, ok := pkgsByName["pkg-2"]
|
||||
require.True(t, ok, "pkg-2 not found")
|
||||
assert.Equal(t, "2.0.0", syftPkg2.Version)
|
||||
assert.Equal(t, "pkg:npm/pkg-2@2.0.0", syftPkg2.PURL)
|
||||
|
||||
// Verify pkg-1 licenses: concluded MIT + declared (MIT OR Apache-2.0)
|
||||
pkg1Licenses := syftPkg1.Licenses.ToSlice()
|
||||
require.NotEmpty(t, pkg1Licenses, "pkg-1 should have licenses")
|
||||
|
||||
var pkg1Concluded, pkg1Declared []pkg.License
|
||||
for _, l := range pkg1Licenses {
|
||||
switch l.Type {
|
||||
case license.Concluded:
|
||||
pkg1Concluded = append(pkg1Concluded, l)
|
||||
case license.Declared:
|
||||
pkg1Declared = append(pkg1Declared, l)
|
||||
}
|
||||
}
|
||||
require.Len(t, pkg1Concluded, 1)
|
||||
assert.Equal(t, "MIT", pkg1Concluded[0].Value)
|
||||
require.Len(t, pkg1Declared, 1)
|
||||
assert.Equal(t, "(MIT OR Apache-2.0)", pkg1Declared[0].Value)
|
||||
|
||||
// Verify pkg-2 licenses: concluded BSD-3-Clause AND GPL-2.0-only+
|
||||
pkg2Licenses := syftPkg2.Licenses.ToSlice()
|
||||
require.NotEmpty(t, pkg2Licenses, "pkg-2 should have licenses")
|
||||
|
||||
var pkg2Concluded []pkg.License
|
||||
for _, l := range pkg2Licenses {
|
||||
if l.Type == license.Concluded {
|
||||
pkg2Concluded = append(pkg2Concluded, l)
|
||||
}
|
||||
}
|
||||
require.Len(t, pkg2Concluded, 1)
|
||||
assert.Equal(t, "BSD-3-Clause AND GPL-2.0+", pkg2Concluded[0].Value)
|
||||
|
||||
// Verify files
|
||||
coords1 := file.Coordinates{RealPath: "/src/main.go"}
|
||||
digests, ok := result.Artifacts.FileDigests[coords1]
|
||||
require.True(t, ok, "file1 digests not found")
|
||||
require.Len(t, digests, 1)
|
||||
assert.Equal(t, "sha256", digests[0].Algorithm)
|
||||
assert.Equal(t, "abc123", digests[0].Value)
|
||||
|
||||
coords2 := file.Coordinates{RealPath: "/src/util.go"}
|
||||
_, ok = result.Artifacts.FileMetadata[coords2]
|
||||
assert.True(t, ok, "file2 metadata not found")
|
||||
|
||||
// Verify relationships
|
||||
require.NotEmpty(t, result.Relationships)
|
||||
|
||||
foundDependsOn := false
|
||||
foundPkg1ContainsFile := false
|
||||
foundPkg2ContainsFile := false
|
||||
for _, rel := range result.Relationships {
|
||||
fromPkg, fromOk := rel.From.(pkg.Package)
|
||||
if !fromOk {
|
||||
continue
|
||||
}
|
||||
switch toPkg := rel.To.(type) {
|
||||
case pkg.Package:
|
||||
if fromPkg.Name == "pkg-2" && toPkg.Name == "pkg-1" && rel.Type == artifact.DependencyOfRelationship {
|
||||
foundDependsOn = true
|
||||
}
|
||||
case file.Location:
|
||||
if fromPkg.Name == "pkg-1" && toPkg.RealPath == "/src/main.go" && rel.Type == artifact.ContainsRelationship {
|
||||
foundPkg1ContainsFile = true
|
||||
}
|
||||
if fromPkg.Name == "pkg-2" && toPkg.RealPath == "/src/util.go" && rel.Type == artifact.ContainsRelationship {
|
||||
foundPkg2ContainsFile = true
|
||||
}
|
||||
}
|
||||
}
|
||||
assert.True(t, foundDependsOn, "expected pkg-2 DependencyOf pkg-1 relationship")
|
||||
assert.True(t, foundPkg1ContainsFile, "expected pkg-1 Contains /src/main.go relationship")
|
||||
assert.True(t, foundPkg2ContainsFile, "expected pkg-2 Contains /src/util.go relationship")
|
||||
}
|
||||
|
||||
func Test_v3licenseInfoToExpression(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
info spdx.AnyLicenseInfo
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "listed license",
|
||||
info: &spdx.ListedLicense{Name: "MIT"},
|
||||
expected: "MIT",
|
||||
},
|
||||
{
|
||||
name: "custom license with ID",
|
||||
info: &spdx.CustomLicense{ID: "LicenseRef-Custom-1", Name: "Custom License"},
|
||||
expected: "LicenseRef-Custom-1",
|
||||
},
|
||||
{
|
||||
name: "custom license without ID falls back to name",
|
||||
info: &spdx.CustomLicense{Name: "Custom License"},
|
||||
expected: "Custom License",
|
||||
},
|
||||
{
|
||||
name: "license expression",
|
||||
info: &spdx.LicenseExpression{LicenseExpression: "MIT AND Apache-2.0"},
|
||||
expected: "MIT AND Apache-2.0",
|
||||
},
|
||||
{
|
||||
name: "or-later operator",
|
||||
info: &spdx.OrLaterOperator{
|
||||
SubjectLicense: &spdx.ListedLicense{Name: "GPL-2.0"},
|
||||
},
|
||||
expected: "GPL-2.0+",
|
||||
},
|
||||
{
|
||||
name: "conjunctive license set (AND)",
|
||||
info: &spdx.ConjunctiveLicenseSet{
|
||||
Members: spdx.LicenseInfoList{
|
||||
&spdx.ListedLicense{Name: "MIT"},
|
||||
&spdx.ListedLicense{Name: "Apache-2.0"},
|
||||
},
|
||||
},
|
||||
expected: "MIT AND Apache-2.0",
|
||||
},
|
||||
{
|
||||
name: "disjunctive license set (OR)",
|
||||
info: &spdx.DisjunctiveLicenseSet{
|
||||
Members: spdx.LicenseInfoList{
|
||||
&spdx.ListedLicense{Name: "MIT"},
|
||||
&spdx.ListedLicense{Name: "Apache-2.0"},
|
||||
},
|
||||
},
|
||||
expected: "(MIT OR Apache-2.0)",
|
||||
},
|
||||
{
|
||||
name: "with-addition operator",
|
||||
info: &spdx.WithAdditionOperator{
|
||||
SubjectExtendableLicense: &spdx.ListedLicense{Name: "GPL-2.0-only"},
|
||||
SubjectAddition: &spdx.ListedLicenseException{AdditionText: "Classpath-exception-2.0"},
|
||||
},
|
||||
expected: "GPL-2.0-only WITH Classpath-exception-2.0",
|
||||
},
|
||||
{
|
||||
name: "nested: (MIT OR Apache-2.0) AND GPL-2.0",
|
||||
info: &spdx.ConjunctiveLicenseSet{
|
||||
Members: spdx.LicenseInfoList{
|
||||
&spdx.DisjunctiveLicenseSet{
|
||||
Members: spdx.LicenseInfoList{
|
||||
&spdx.ListedLicense{Name: "MIT"},
|
||||
&spdx.ListedLicense{Name: "Apache-2.0"},
|
||||
},
|
||||
},
|
||||
&spdx.ListedLicense{Name: "GPL-2.0-only"},
|
||||
},
|
||||
},
|
||||
expected: "(MIT OR Apache-2.0) AND GPL-2.0-only",
|
||||
},
|
||||
{
|
||||
name: "nested: GPL-2.0 WITH Classpath OR MIT",
|
||||
info: &spdx.DisjunctiveLicenseSet{
|
||||
Members: spdx.LicenseInfoList{
|
||||
&spdx.WithAdditionOperator{
|
||||
SubjectExtendableLicense: &spdx.ListedLicense{Name: "GPL-2.0-only"},
|
||||
SubjectAddition: &spdx.ListedLicenseException{AdditionText: "Classpath-exception-2.0"},
|
||||
},
|
||||
&spdx.ListedLicense{Name: "MIT"},
|
||||
},
|
||||
},
|
||||
expected: "(GPL-2.0-only WITH Classpath-exception-2.0 OR MIT)",
|
||||
},
|
||||
{
|
||||
name: "nil returns empty",
|
||||
info: nil,
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := v3licenseInfoToExpression(tt.info)
|
||||
assert.Equal(t, tt.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_v3toSyftLicenses(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
licenseType license.Type
|
||||
licenses []spdx.AnyLicenseInfo
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "simple listed license",
|
||||
licenseType: license.Declared,
|
||||
licenses: []spdx.AnyLicenseInfo{
|
||||
&spdx.ListedLicense{Name: "MIT"},
|
||||
},
|
||||
expected: []string{"MIT"},
|
||||
},
|
||||
{
|
||||
name: "conjunctive set produces single expression",
|
||||
licenseType: license.Concluded,
|
||||
licenses: []spdx.AnyLicenseInfo{
|
||||
&spdx.ConjunctiveLicenseSet{
|
||||
Members: spdx.LicenseInfoList{
|
||||
&spdx.ListedLicense{Name: "MIT"},
|
||||
&spdx.ListedLicense{Name: "Apache-2.0"},
|
||||
},
|
||||
},
|
||||
},
|
||||
expected: []string{"MIT AND Apache-2.0"},
|
||||
},
|
||||
{
|
||||
name: "multiple license infos",
|
||||
licenseType: license.Declared,
|
||||
licenses: []spdx.AnyLicenseInfo{
|
||||
&spdx.ListedLicense{Name: "MIT"},
|
||||
&spdx.ListedLicense{Name: "GPL-2.0-only"},
|
||||
},
|
||||
expected: []string{"MIT", "GPL-2.0-only"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := v3toSyftLicenses(tt.licenseType, tt.licenses...)
|
||||
require.Len(t, got, len(tt.expected))
|
||||
for i, l := range got {
|
||||
assert.Equal(t, tt.licenseType, l.Type)
|
||||
assert.Equal(t, tt.expected[i], l.Value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_v3parseSPDXLicenses(t *testing.T) {
|
||||
p := &spdx.Package{Name: "test-pkg"}
|
||||
|
||||
concludedLicense := &spdx.ListedLicense{Name: "MIT"}
|
||||
declaredLicense := &spdx.ConjunctiveLicenseSet{
|
||||
Members: spdx.LicenseInfoList{
|
||||
&spdx.ListedLicense{Name: "Apache-2.0"},
|
||||
&spdx.ListedLicense{Name: "BSD-3-Clause"},
|
||||
},
|
||||
}
|
||||
|
||||
relationships := ptrMap[[]spdx.AnyRelationship]{}
|
||||
relationships.Set(p, []spdx.AnyRelationship{
|
||||
&spdx.Relationship{
|
||||
Type: spdx.RelationshipType_HasConcludedLicense,
|
||||
From: p,
|
||||
To: spdx.ElementList{concludedLicense},
|
||||
},
|
||||
&spdx.Relationship{
|
||||
Type: spdx.RelationshipType_HasDeclaredLicense,
|
||||
From: p,
|
||||
To: spdx.ElementList{declaredLicense},
|
||||
},
|
||||
})
|
||||
|
||||
licenses := v3parseSPDXLicenses(relationships, p)
|
||||
|
||||
require.Len(t, licenses, 2)
|
||||
|
||||
var concluded, declared []pkg.License
|
||||
for _, l := range licenses {
|
||||
switch l.Type {
|
||||
case license.Concluded:
|
||||
concluded = append(concluded, l)
|
||||
case license.Declared:
|
||||
declared = append(declared, l)
|
||||
}
|
||||
}
|
||||
|
||||
require.Len(t, concluded, 1)
|
||||
assert.Equal(t, "MIT", concluded[0].Value)
|
||||
|
||||
require.Len(t, declared, 1)
|
||||
assert.Equal(t, "Apache-2.0 AND BSD-3-Clause", declared[0].Value)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.7",
|
||||
"serialNumber": "urn:uuid:5208fea9-73dd-4624-b596-69fddccdb9e7",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
"timestamp": "2023-09-29T12:02:02-04:00",
|
||||
"tools": [
|
||||
{
|
||||
"vendor": "anchore",
|
||||
"name": "syft",
|
||||
"version": "[not provided]"
|
||||
}
|
||||
],
|
||||
"component": {
|
||||
"bom-ref": "a0ff99a6af10f11f",
|
||||
"type": "file",
|
||||
"name": "go.mod",
|
||||
"version": "sha256:sha256:dc333f342905248a52e424d8dfd061251d01867d01a4f9d7397144a775ff9ebd"
|
||||
}
|
||||
},
|
||||
"components": [
|
||||
{
|
||||
"bom-ref": "pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651?package-id=2ff71a67fb024c86",
|
||||
"type": "library",
|
||||
"name": "github.com/wagoodman/go-partybus",
|
||||
"version": "v0.0.0-20230516145632-8ccac152c651",
|
||||
"cpe": "cpe:2.3:a:wagoodman:go-partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*",
|
||||
"purl": "pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651",
|
||||
"properties": [
|
||||
{
|
||||
"name": "syft:package:foundBy",
|
||||
"value": "go-module-file-cataloger"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:language",
|
||||
"value": "go"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:metadataType",
|
||||
"value": "GolangModMetadata"
|
||||
},
|
||||
{
|
||||
"name": "syft:package:type",
|
||||
"value": "go-module"
|
||||
},
|
||||
{
|
||||
"name": "syft:cpe23",
|
||||
"value": "cpe:2.3:a:wagoodman:go_partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*"
|
||||
},
|
||||
{
|
||||
"name": "syft:location:0:path",
|
||||
"value": "/go.mod"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
|
||||
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.6",
|
||||
"specVersion": "1.7",
|
||||
"serialNumber": "urn:uuid:redacted",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
|
||||
"$schema": "http://cyclonedx.org/schema/bom-1.7.schema.json",
|
||||
"bomFormat": "CycloneDX",
|
||||
"specVersion": "1.6",
|
||||
"specVersion": "1.7",
|
||||
"serialNumber": "urn:uuid:redacted",
|
||||
"version": 1,
|
||||
"metadata": {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="urn:uuid:098e8516-ecd5-4130-9d5f-c32ba1ddb0dd" version="1">
|
||||
<metadata>
|
||||
<timestamp>2023-09-29T11:48:10-04:00</timestamp>
|
||||
<tools>
|
||||
<tool>
|
||||
<vendor>anchore</vendor>
|
||||
<name>syft</name>
|
||||
<version>[not provided]</version>
|
||||
</tool>
|
||||
</tools>
|
||||
<component bom-ref="a0ff99a6af10f11f" type="file">
|
||||
<name>go.mod</name>
|
||||
<version>sha256:sha256:dc333f342905248a52e424d8dfd061251d01867d01a4f9d7397144a775ff9ebd</version>
|
||||
</component>
|
||||
</metadata>
|
||||
<components>
|
||||
<component bom-ref="pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651?package-id=2ff71a67fb024c86" type="library">
|
||||
<name>github.com/wagoodman/go-partybus</name>
|
||||
<version>v0.0.0-20230516145632-8ccac152c651</version>
|
||||
<cpe>cpe:2.3:a:wagoodman:go-partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*</cpe>
|
||||
<purl>pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651</purl>
|
||||
<properties>
|
||||
<property name="syft:package:foundBy">go-module-file-cataloger</property>
|
||||
<property name="syft:package:language">go</property>
|
||||
<property name="syft:package:metadataType">GolangModMetadata</property>
|
||||
<property name="syft:package:type">go-module</property>
|
||||
<property name="syft:cpe23">cpe:2.3:a:wagoodman:go_partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*</property>
|
||||
<property name="syft:location:0:path">/go.mod</property>
|
||||
</properties>
|
||||
</component>
|
||||
</components>
|
||||
</bom>
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="redacted" version="1">
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="redacted" version="1">
|
||||
<metadata>
|
||||
<timestamp>redacted</timestamp>
|
||||
<tools>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="redacted" version="1">
|
||||
<bom xmlns="http://cyclonedx.org/schema/bom/1.7" serialNumber="redacted" version="1">
|
||||
<metadata>
|
||||
<timestamp>redacted</timestamp>
|
||||
<tools>
|
||||
|
||||
@@ -14,12 +14,22 @@ import (
|
||||
)
|
||||
|
||||
type EncoderCollection struct {
|
||||
encoders []sbom.FormatEncoder
|
||||
defaultVersions map[sbom.FormatID]string
|
||||
encoders []sbom.FormatEncoder
|
||||
}
|
||||
|
||||
func NewEncoderCollection(encoders ...sbom.FormatEncoder) *EncoderCollection {
|
||||
defaultVersions := map[sbom.FormatID]string{}
|
||||
for _, encoder := range encoders {
|
||||
if defaultVersion, ok := encoder.(defaultVersionIndicator); ok {
|
||||
if defaultVersion.DefaultVersion() {
|
||||
defaultVersions[encoder.ID()] = encoder.Version()
|
||||
}
|
||||
}
|
||||
}
|
||||
return &EncoderCollection{
|
||||
encoders: encoders,
|
||||
defaultVersions: defaultVersions,
|
||||
encoders: encoders,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,13 +87,20 @@ func (e EncoderCollection) Get(name string, version string) sbom.FormatEncoder {
|
||||
var mostRecentFormat sbom.FormatEncoder
|
||||
|
||||
for _, f := range e.encoders {
|
||||
defaultVersion := e.defaultVersions[f.ID()]
|
||||
log.WithFields("name", f.ID(), "version", f.Version(), "aliases", f.Aliases()).Trace("considering format")
|
||||
names := []string{string(f.ID())}
|
||||
names = append(names, f.Aliases()...)
|
||||
for _, n := range names {
|
||||
if cleanFormatName(n) == name && versionMatches(f.Version(), version) {
|
||||
if mostRecentFormat == nil || f.Version() > mostRecentFormat.Version() {
|
||||
mostRecentFormat = f
|
||||
if cleanFormatName(n) == name {
|
||||
// if the name or alias matches, and the requested version is any-version, use the default
|
||||
if version == sbom.AnyVersion && defaultVersion != "" {
|
||||
version = defaultVersion
|
||||
}
|
||||
if versionMatches(f.Version(), version) {
|
||||
if mostRecentFormat == nil || f.Version() > mostRecentFormat.Version() {
|
||||
mostRecentFormat = f
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,3 +158,9 @@ func Encode(s sbom.SBOM, f sbom.FormatEncoder) ([]byte, error) {
|
||||
|
||||
return buff.Bytes(), nil
|
||||
}
|
||||
|
||||
type defaultVersionIndicator interface {
|
||||
// DefaultVersion a sbom.FormatEncoder may indicate it is the default version to be used
|
||||
// when no version is specified for the type
|
||||
DefaultVersion() bool
|
||||
}
|
||||
|
||||
@@ -4,7 +4,9 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/format/internal/spdxutil"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
@@ -109,3 +111,76 @@ func Test_versionMatches(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_EncoderCollection_Get(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
searchName string
|
||||
searchVersion string
|
||||
expectedID sbom.FormatID
|
||||
expectedVersion string
|
||||
}{
|
||||
{
|
||||
name: "explicit name and version",
|
||||
searchName: "spdx-json",
|
||||
searchVersion: "2.3",
|
||||
expectedID: spdxutil.JSONFormatID,
|
||||
expectedVersion: "2.3",
|
||||
},
|
||||
{
|
||||
name: "explicit name without version gets default",
|
||||
searchName: "spdx-json",
|
||||
searchVersion: "",
|
||||
expectedID: spdxutil.JSONFormatID,
|
||||
expectedVersion: "2.3",
|
||||
},
|
||||
{
|
||||
name: "alias name with version",
|
||||
searchName: "spdx",
|
||||
searchVersion: "2.2",
|
||||
expectedID: spdxutil.TagValueFormatID,
|
||||
expectedVersion: "2.2",
|
||||
},
|
||||
{
|
||||
name: "alias name without version gets default",
|
||||
searchName: "spdx",
|
||||
searchVersion: "",
|
||||
expectedID: spdxutil.TagValueFormatID,
|
||||
expectedVersion: "2.3",
|
||||
},
|
||||
{
|
||||
name: "invalid name gets nothing",
|
||||
searchName: "json-spdx",
|
||||
searchVersion: "2.3",
|
||||
expectedID: "",
|
||||
},
|
||||
{
|
||||
name: "invalid version gets nothing",
|
||||
searchName: "spdx-json",
|
||||
searchVersion: "2.0",
|
||||
expectedID: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
config := DefaultEncodersConfig()
|
||||
// ensure SPDX default is 2.3 for test
|
||||
config.SPDXJSON.DefaultVersion = "2.3"
|
||||
encoders, err := config.Encoders()
|
||||
require.NoError(t, err)
|
||||
collection := NewEncoderCollection(encoders...)
|
||||
result := collection.Get(test.searchName, test.searchVersion)
|
||||
|
||||
if test.expectedID != "" {
|
||||
require.NotNil(t, result, "expected to find encoder but got nil")
|
||||
if result != nil {
|
||||
require.Equal(t, test.expectedID, result.ID())
|
||||
require.Equal(t, test.expectedVersion, result.Version())
|
||||
}
|
||||
} else {
|
||||
require.Nil(t, result, "expected nil but found encoder")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ func backfillFromPurl(p *pkg.Package) {
|
||||
var cpes []cpe.CPE
|
||||
epoch := ""
|
||||
rpmmod := ""
|
||||
arch := ""
|
||||
|
||||
for _, qualifier := range purl.Qualifiers {
|
||||
switch qualifier.Key {
|
||||
@@ -65,6 +66,8 @@ func backfillFromPurl(p *pkg.Package) {
|
||||
epoch = qualifier.Value
|
||||
case pkg.PURLQualifierRpmModularity:
|
||||
rpmmod = qualifier.Value
|
||||
case pkg.PURLQualifierArch:
|
||||
arch = qualifier.Value
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,9 +87,7 @@ func backfillFromPurl(p *pkg.Package) {
|
||||
setJavaMetadataFromPurl(p, purl)
|
||||
}
|
||||
|
||||
if p.Type == pkg.RpmPkg {
|
||||
setRpmMetadataFromPurl(p, rpmmod)
|
||||
}
|
||||
setDistroMetadata(p, rpmmod, arch)
|
||||
|
||||
for _, c := range cpes {
|
||||
if slices.Contains(p.CPEs, c) {
|
||||
@@ -96,6 +97,19 @@ func backfillFromPurl(p *pkg.Package) {
|
||||
}
|
||||
}
|
||||
|
||||
func setDistroMetadata(p *pkg.Package, rpmmod, arch string) {
|
||||
switch p.Type {
|
||||
case pkg.RpmPkg:
|
||||
setRpmMetadataFromPurl(p, rpmmod, arch)
|
||||
case pkg.DebPkg:
|
||||
setDpkgMetadataFromPurl(p, arch)
|
||||
case pkg.AlpmPkg:
|
||||
setAlpmMetadataFromPurl(p, arch)
|
||||
case pkg.ApkPkg:
|
||||
setApkMetadataFromPurl(p, arch)
|
||||
}
|
||||
}
|
||||
|
||||
func setJavaMetadataFromPurl(p *pkg.Package, _ packageurl.PackageURL) {
|
||||
if p.Type != pkg.JavaPkg {
|
||||
return
|
||||
@@ -107,32 +121,103 @@ func setJavaMetadataFromPurl(p *pkg.Package, _ packageurl.PackageURL) {
|
||||
}
|
||||
}
|
||||
|
||||
func setRpmMetadataFromPurl(p *pkg.Package, rpmmod string) {
|
||||
func setRpmMetadataFromPurl(p *pkg.Package, rpmmod, arch string) {
|
||||
if p.Type != pkg.RpmPkg {
|
||||
return
|
||||
}
|
||||
if rpmmod == "" {
|
||||
if rpmmod == "" && arch == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if p.Metadata == nil {
|
||||
p.Metadata = pkg.RpmDBEntry{
|
||||
ModularityLabel: &rpmmod,
|
||||
}
|
||||
return
|
||||
p.Metadata = pkg.RpmDBEntry{}
|
||||
}
|
||||
|
||||
switch m := p.Metadata.(type) {
|
||||
case pkg.RpmDBEntry:
|
||||
if m.ModularityLabel == nil {
|
||||
if m.ModularityLabel == nil && rpmmod != "" {
|
||||
m.ModularityLabel = &rpmmod
|
||||
p.Metadata = m
|
||||
}
|
||||
if m.Arch == "" {
|
||||
m.Arch = arch
|
||||
}
|
||||
p.Metadata = m
|
||||
case pkg.RpmArchive:
|
||||
if m.ModularityLabel == nil {
|
||||
if m.ModularityLabel == nil && rpmmod != "" {
|
||||
m.ModularityLabel = &rpmmod
|
||||
p.Metadata = m
|
||||
}
|
||||
if m.Arch == "" {
|
||||
m.Arch = arch
|
||||
}
|
||||
p.Metadata = m
|
||||
}
|
||||
}
|
||||
|
||||
func setDpkgMetadataFromPurl(p *pkg.Package, arch string) {
|
||||
if p.Type != pkg.DebPkg {
|
||||
return
|
||||
}
|
||||
if arch == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if p.Metadata == nil {
|
||||
p.Metadata = pkg.DpkgDBEntry{}
|
||||
}
|
||||
|
||||
switch m := p.Metadata.(type) {
|
||||
case pkg.DpkgDBEntry:
|
||||
if m.Architecture == "" {
|
||||
m.Architecture = arch
|
||||
}
|
||||
p.Metadata = m
|
||||
case pkg.DpkgArchiveEntry:
|
||||
if m.Architecture == "" {
|
||||
m.Architecture = arch
|
||||
}
|
||||
p.Metadata = m
|
||||
}
|
||||
}
|
||||
|
||||
func setAlpmMetadataFromPurl(p *pkg.Package, arch string) {
|
||||
if p.Type != pkg.AlpmPkg {
|
||||
return
|
||||
}
|
||||
if arch == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if p.Metadata == nil {
|
||||
p.Metadata = pkg.AlpmDBEntry{Architecture: arch}
|
||||
return
|
||||
}
|
||||
|
||||
if m, ok := p.Metadata.(pkg.AlpmDBEntry); ok {
|
||||
if m.Architecture == "" {
|
||||
m.Architecture = arch
|
||||
}
|
||||
p.Metadata = m
|
||||
}
|
||||
}
|
||||
|
||||
func setApkMetadataFromPurl(p *pkg.Package, arch string) {
|
||||
if p.Type != pkg.ApkPkg {
|
||||
return
|
||||
}
|
||||
if arch == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if p.Metadata == nil {
|
||||
p.Metadata = pkg.ApkDBEntry{Architecture: arch}
|
||||
return
|
||||
}
|
||||
|
||||
if m, ok := p.Metadata.(pkg.ApkDBEntry); ok {
|
||||
if m.Architecture == "" {
|
||||
m.Architecture = arch
|
||||
}
|
||||
p.Metadata = m
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,9 @@ func Test_Backfill(t *testing.T) {
|
||||
Type: pkg.RpmPkg,
|
||||
Name: "dbus-common",
|
||||
Version: "1.12.8-26.el8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Arch: "noarch",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -51,6 +54,9 @@ func Test_Backfill(t *testing.T) {
|
||||
Type: pkg.RpmPkg,
|
||||
Name: "dbus-common",
|
||||
Version: "1:1.12.8-26.el8",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Arch: "noarch",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -65,6 +71,22 @@ func Test_Backfill(t *testing.T) {
|
||||
Version: "2.4.37-51",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
ModularityLabel: strRef("httpd:2.4"),
|
||||
Arch: "x86_64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rpm with arch and no rpmmod",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:rpm/redhat/httpd@2.4.37-51?arch=x86_64&distro=rhel-8.7",
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:rpm/redhat/httpd@2.4.37-51?arch=x86_64&distro=rhel-8.7",
|
||||
Type: pkg.RpmPkg,
|
||||
Name: "httpd",
|
||||
Version: "2.4.37-51",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Arch: "x86_64",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -121,6 +143,225 @@ func Test_Backfill(t *testing.T) {
|
||||
Metadata: pkg.JavaArchive{},
|
||||
},
|
||||
},
|
||||
// deb cases
|
||||
{
|
||||
name: "deb nil metadata gets arch set",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
Type: pkg.DebPkg,
|
||||
Name: "curl",
|
||||
Version: "7.74.0-1.3+deb11u7",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Architecture: "amd64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deb arch:all preserved (nil metadata)",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:deb/debian/tzdata@2021a-1+deb11u10?arch=all&distro=debian-11",
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:deb/debian/tzdata@2021a-1+deb11u10?arch=all&distro=debian-11",
|
||||
Type: pkg.DebPkg,
|
||||
Name: "tzdata",
|
||||
Version: "2021a-1+deb11u10",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Architecture: "all",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deb populated metadata empty arch gets filled",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "curl",
|
||||
},
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
Type: pkg.DebPkg,
|
||||
Name: "curl",
|
||||
Version: "7.74.0-1.3+deb11u7",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "curl",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deb populated metadata with arch preserved",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "curl",
|
||||
Architecture: "arm64",
|
||||
},
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
Type: pkg.DebPkg,
|
||||
Name: "curl",
|
||||
Version: "7.74.0-1.3+deb11u7",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "curl",
|
||||
Architecture: "arm64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deb archive entry empty arch gets filled",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
Metadata: pkg.DpkgArchiveEntry{
|
||||
Package: "curl",
|
||||
},
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
Type: pkg.DebPkg,
|
||||
Name: "curl",
|
||||
Version: "7.74.0-1.3+deb11u7",
|
||||
Metadata: pkg.DpkgArchiveEntry{
|
||||
Package: "curl",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deb archive entry with arch preserved",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
Metadata: pkg.DpkgArchiveEntry{
|
||||
Package: "curl",
|
||||
Architecture: "arm64",
|
||||
},
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11",
|
||||
Type: pkg.DebPkg,
|
||||
Name: "curl",
|
||||
Version: "7.74.0-1.3+deb11u7",
|
||||
Metadata: pkg.DpkgArchiveEntry{
|
||||
Package: "curl",
|
||||
Architecture: "arm64",
|
||||
},
|
||||
},
|
||||
},
|
||||
// alpm cases
|
||||
{
|
||||
name: "alpm nil metadata gets arch set",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
|
||||
Type: pkg.AlpmPkg,
|
||||
Name: "curl",
|
||||
Version: "7.88.1-1",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "alpm populated metadata empty arch gets filled",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "curl",
|
||||
},
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
|
||||
Type: pkg.AlpmPkg,
|
||||
Name: "curl",
|
||||
Version: "7.88.1-1",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "curl",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "alpm populated metadata with arch preserved",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "curl",
|
||||
Architecture: "aarch64",
|
||||
},
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:alpm/arch/curl@7.88.1-1?arch=x86_64&distro=arch-rolling",
|
||||
Type: pkg.AlpmPkg,
|
||||
Name: "curl",
|
||||
Version: "7.88.1-1",
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "curl",
|
||||
Architecture: "aarch64",
|
||||
},
|
||||
},
|
||||
},
|
||||
// apk cases
|
||||
{
|
||||
name: "apk nil metadata gets arch set",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
|
||||
Type: pkg.ApkPkg,
|
||||
Name: "curl",
|
||||
Version: "7.83.1-r2",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Architecture: "aarch64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "apk populated metadata empty arch gets filled",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "curl",
|
||||
},
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
|
||||
Type: pkg.ApkPkg,
|
||||
Name: "curl",
|
||||
Version: "7.83.1-r2",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "curl",
|
||||
Architecture: "aarch64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "apk populated metadata with arch preserved",
|
||||
in: pkg.Package{
|
||||
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "curl",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
},
|
||||
expected: pkg.Package{
|
||||
PURL: "pkg:apk/alpine/curl@7.83.1-r2?arch=aarch64&distro=alpine-3.16.2",
|
||||
Type: pkg.ApkPkg,
|
||||
Name: "curl",
|
||||
Version: "7.83.1-r2",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "curl",
|
||||
Architecture: "x86_64",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "target-sw from CPE",
|
||||
in: pkg.Package{
|
||||
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
const DefaultVersion = "1.6"
|
||||
|
||||
type Encoder struct {
|
||||
version cyclonedx.SpecVersion
|
||||
format cyclonedx.BOMFileFormat
|
||||
|
||||
@@ -317,7 +317,7 @@ func Test_decodeComponent(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantMetadata: pkg.RpmDBEntry{},
|
||||
wantMetadata: pkg.RpmDBEntry{Arch: "x86_64"},
|
||||
wantPURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8",
|
||||
},
|
||||
{
|
||||
@@ -341,6 +341,7 @@ func Test_decodeComponent(t *testing.T) {
|
||||
},
|
||||
wantMetadata: pkg.RpmDBEntry{
|
||||
Release: "some-release",
|
||||
Arch: "x86_64",
|
||||
},
|
||||
wantPURL: "pkg:rpm/centos/acl@2.2.53-1.el8?arch=x86_64&upstream=acl-2.2.53-1.el8.src.rpm&distro=centos-8",
|
||||
},
|
||||
|
||||
@@ -2,6 +2,10 @@ package cyclonedxutil
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
@@ -13,59 +17,81 @@ const (
|
||||
JSONFormatID sbom.FormatID = "cyclonedx-json"
|
||||
)
|
||||
|
||||
const DefaultVersion = "1.7"
|
||||
|
||||
var commonVersions = map[string]cyclonedx.SpecVersion{
|
||||
"1.2": cyclonedx.SpecVersion1_2,
|
||||
"1.3": cyclonedx.SpecVersion1_3,
|
||||
"1.4": cyclonedx.SpecVersion1_4,
|
||||
"1.5": cyclonedx.SpecVersion1_5,
|
||||
"1.6": cyclonedx.SpecVersion1_6,
|
||||
DefaultVersion: cyclonedx.SpecVersion1_7,
|
||||
}
|
||||
|
||||
var allVersions = func() map[string]cyclonedx.SpecVersion {
|
||||
out := map[string]cyclonedx.SpecVersion{
|
||||
"1.0": cyclonedx.SpecVersion1_0,
|
||||
"1.1": cyclonedx.SpecVersion1_1,
|
||||
}
|
||||
maps.Copy(out, commonVersions)
|
||||
return out
|
||||
}()
|
||||
|
||||
func SupportedVersions(id sbom.FormatID) []string {
|
||||
versions := []string{
|
||||
"1.2",
|
||||
"1.3",
|
||||
"1.4",
|
||||
"1.5",
|
||||
"1.6",
|
||||
versionSet := commonVersions
|
||||
if id == XMLFormatID {
|
||||
versionSet = allVersions
|
||||
}
|
||||
|
||||
if id != JSONFormatID {
|
||||
// JSON format not supported for version < 1.2
|
||||
versions = append([]string{"1.0", "1.1"}, versions...)
|
||||
versions := make([]string, 0, len(versionSet))
|
||||
for _, v := range versionSet {
|
||||
versions = append(versions, v.String())
|
||||
}
|
||||
|
||||
slices.SortFunc(versions, versionSort)
|
||||
return versions
|
||||
}
|
||||
|
||||
func SpecVersionFromString(v string) (cyclonedx.SpecVersion, error) {
|
||||
switch v {
|
||||
case "1.0":
|
||||
return cyclonedx.SpecVersion1_0, nil
|
||||
case "1.1":
|
||||
return cyclonedx.SpecVersion1_1, nil
|
||||
case "1.2":
|
||||
return cyclonedx.SpecVersion1_2, nil
|
||||
case "1.3":
|
||||
return cyclonedx.SpecVersion1_3, nil
|
||||
case "1.4":
|
||||
return cyclonedx.SpecVersion1_4, nil
|
||||
case "1.5":
|
||||
return cyclonedx.SpecVersion1_5, nil
|
||||
case "1.6":
|
||||
return cyclonedx.SpecVersion1_6, nil
|
||||
if specVersion, ok := allVersions[v]; ok {
|
||||
return specVersion, nil
|
||||
}
|
||||
return -1, fmt.Errorf("unsupported CycloneDX version %q", v)
|
||||
}
|
||||
|
||||
func VersionFromSpecVersion(spec cyclonedx.SpecVersion) string {
|
||||
switch spec {
|
||||
case cyclonedx.SpecVersion1_0:
|
||||
return "1.0"
|
||||
case cyclonedx.SpecVersion1_1:
|
||||
return "1.1"
|
||||
case cyclonedx.SpecVersion1_2:
|
||||
return "1.2"
|
||||
case cyclonedx.SpecVersion1_3:
|
||||
return "1.3"
|
||||
case cyclonedx.SpecVersion1_4:
|
||||
return "1.4"
|
||||
case cyclonedx.SpecVersion1_5:
|
||||
return "1.5"
|
||||
case cyclonedx.SpecVersion1_6:
|
||||
return "1.6"
|
||||
for version, specVersion := range allVersions {
|
||||
if specVersion == spec {
|
||||
return version
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func versionSort(a string, b string) int {
|
||||
partsA := strings.Split(a, ".")
|
||||
partsB := strings.Split(b, ".")
|
||||
lenA := len(partsA)
|
||||
lenB := len(partsB)
|
||||
for i := range max(lenA, lenB) {
|
||||
if i >= lenA {
|
||||
return -1 // 1 < 1.x
|
||||
}
|
||||
if i >= lenB {
|
||||
return 1 // 1.x > 1
|
||||
}
|
||||
partA, errA := strconv.ParseInt(partsA[i], 10, 64)
|
||||
partB, errB := strconv.ParseInt(partsB[i], 10, 64)
|
||||
if errA != nil || errB != nil {
|
||||
// string compare if we can't parse one of the sides
|
||||
strcmp := strings.Compare(partsA[i], partsB[i])
|
||||
if strcmp == 0 {
|
||||
continue
|
||||
}
|
||||
return strcmp // not equal
|
||||
}
|
||||
if partA == partB {
|
||||
continue
|
||||
}
|
||||
return int(partA - partB)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package cyclonedxutil
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"go/types"
|
||||
"regexp"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
// Test_allSpecVersionsMapped loads the cyclonedx-go package's type information and enumerates every
|
||||
// SpecVersion* constant it declares, asserting each one is present in our version maps. Upgrading
|
||||
// cyclonedx-go to a release that adds a new spec version will fail this test until versions.go is updated.
|
||||
func Test_allSpecVersionsMapped(t *testing.T) {
|
||||
const pkgPath = "github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
pkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName | packages.NeedTypes}, pkgPath)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pkgs, 1)
|
||||
|
||||
pkg := pkgs[0]
|
||||
require.Empty(t, pkg.Errors, "errors loading %s", pkgPath)
|
||||
require.NotNil(t, pkg.Types, "no type info loaded for %s", pkgPath)
|
||||
|
||||
// allVersions is the superset (json/common versions merged in), so it covers every spec version we map.
|
||||
mapped := make(map[cyclonedx.SpecVersion]struct{})
|
||||
for syftVersion, sv := range allVersions {
|
||||
// make sure the key matches the version it's mapped to
|
||||
require.Contains(t, sv.String(), syftVersion, "version %q is not correctly mapped to cyclonedx SpecVersion %q", syftVersion, sv.String())
|
||||
mapped[sv] = struct{}{}
|
||||
}
|
||||
|
||||
specVersionConst := regexp.MustCompile(`^SpecVersion\d`)
|
||||
scope := pkg.Types.Scope()
|
||||
found := 0
|
||||
for _, name := range scope.Names() {
|
||||
if !specVersionConst.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
c, ok := scope.Lookup(name).(*types.Const)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
found++
|
||||
val, ok := constant.Int64Val(c.Val())
|
||||
require.Truef(t, ok, "could not read int value of cyclonedx.%s", name)
|
||||
_, ok = mapped[cyclonedx.SpecVersion(val)]
|
||||
|
||||
assert.Truef(t, ok, "cyclonedx.%s is not mapped in versions.go; add it when upgrading cyclonedx-go", name)
|
||||
}
|
||||
require.NotZero(t, found, "no SpecVersion* constants found in %s", pkgPath)
|
||||
}
|
||||
|
||||
func Test_versionSort(t *testing.T) {
|
||||
// versionSort returns <0 when a<b, 0 when a==b, >0 when a>b; assert on sign rather than exact value.
|
||||
sign := func(n int) int {
|
||||
switch {
|
||||
case n < 0:
|
||||
return -1
|
||||
case n > 0:
|
||||
return 1
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
a string
|
||||
b string
|
||||
want int
|
||||
}{
|
||||
{name: "equal single part", a: "1", b: "1", want: 0},
|
||||
{name: "equal two parts", a: "1.4", b: "1.4", want: 0},
|
||||
{name: "minor less than", a: "1.3", b: "1.4", want: -1},
|
||||
{name: "minor greater than", a: "1.6", b: "1.5", want: 1},
|
||||
{name: "major less than", a: "1.7", b: "2.0", want: -1},
|
||||
{name: "major greater than", a: "2.0", b: "1.7", want: 1},
|
||||
{name: "fewer parts sorts first", a: "1", b: "1.0", want: -1},
|
||||
{name: "more parts sorts last", a: "1.0", b: "1", want: 1},
|
||||
{name: "numeric not lexical (10 > 9)", a: "1.10", b: "1.9", want: 1},
|
||||
{name: "multi-digit major", a: "10.0", b: "9.0", want: 1},
|
||||
{name: "three parts equal", a: "1.4.0", b: "1.4.0", want: 0},
|
||||
{name: "three parts patch differs", a: "1.4.1", b: "1.4.0", want: 1},
|
||||
{name: "non-numeric equal falls through", a: "1.x", b: "1.x", want: 0},
|
||||
{name: "non-numeric string compare", a: "1.a", b: "1.b", want: -1},
|
||||
{name: "non-numeric side string compare", a: "1.2", b: "1.x", want: -1},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, sign(versionSort(tt.a, tt.b)))
|
||||
// comparator must be antisymmetric: swapping operands negates the sign.
|
||||
assert.Equal(t, -tt.want, sign(versionSort(tt.b, tt.a)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_versionSort_sortsSlice(t *testing.T) {
|
||||
versions := []string{"1.10", "1.2", "1", "2.0", "1.9", "1.0"}
|
||||
slices.SortFunc(versions, versionSort)
|
||||
assert.Equal(t, []string{"1", "1.0", "1.2", "1.9", "1.10", "2.0"}, versions)
|
||||
}
|
||||
@@ -13,6 +13,7 @@ func Test_OriginatorSupplier(t *testing.T) {
|
||||
completionTester := packagemetadata.NewCompletionTester(t,
|
||||
pkg.BinarySignature{},
|
||||
pkg.BitnamiSBOMEntry{},
|
||||
pkg.BunLockEntry{},
|
||||
pkg.CocoaPodfileLockEntry{},
|
||||
pkg.ConanV1LockEntry{},
|
||||
pkg.ConanV2LockEntry{}, // the field Username might be the username of either the package originator or the supplier (unclear currently)
|
||||
@@ -56,6 +57,8 @@ func Test_OriginatorSupplier(t *testing.T) {
|
||||
pkg.YarnLockEntry{},
|
||||
pkg.TerraformLockProviderEntry{},
|
||||
pkg.GGUFFileHeader{},
|
||||
pkg.DenoLockEntry{},
|
||||
pkg.DenoRemoteLockEntry{},
|
||||
)
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -4,9 +4,10 @@ import (
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
const DefaultVersion = "2.3"
|
||||
|
||||
const (
|
||||
DefaultVersion = "2.3"
|
||||
V3_0 = "3.0"
|
||||
|
||||
JSONFormatID sbom.FormatID = "spdx-json"
|
||||
TagValueFormatID sbom.FormatID = "spdx-tag-value"
|
||||
)
|
||||
@@ -19,7 +20,10 @@ func SupportedVersions(id sbom.FormatID) []string {
|
||||
|
||||
if id != JSONFormatID {
|
||||
// JSON format is not supported in v2.1
|
||||
return append([]string{"2.1"}, versions...)
|
||||
versions = append([]string{"2.1"}, versions...)
|
||||
} else {
|
||||
// is JSON, v3 only supported in JSON format:
|
||||
versions = append(versions, V3_0)
|
||||
}
|
||||
|
||||
return versions
|
||||
|
||||
@@ -58,10 +58,11 @@ func TestDecoder_Decode(t *testing.T) {
|
||||
purl: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "sysv-rc",
|
||||
Version: "2.88dsf-59",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit",
|
||||
Name: "sysv-rc",
|
||||
Version: "2.88dsf-59",
|
||||
Type: pkg.DebPkg,
|
||||
PURL: "pkg:deb/debian/sysv-rc@2.88dsf-59?arch=all&distro=debian-jessie&upstream=sysvinit",
|
||||
Metadata: pkg.DpkgDBEntry{Architecture: "all"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -91,10 +92,11 @@ func TestDecoder_Decode(t *testing.T) {
|
||||
purl: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "systemd-x",
|
||||
Version: "239-82.el8_10.2",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm",
|
||||
Name: "systemd-x",
|
||||
Version: "239-82.el8_10.2",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/systemd-x@239-82.el8_10.2?arch=aarch64&distro=rhel-8.10&upstream=systemd-239-82.el8_10.2.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{Arch: "aarch64"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -102,10 +104,11 @@ func TestDecoder_Decode(t *testing.T) {
|
||||
purl: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "dbus-common",
|
||||
Version: "1:1.12.8-26.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm",
|
||||
Name: "dbus-common",
|
||||
Version: "1:1.12.8-26.el8",
|
||||
Type: pkg.RpmPkg,
|
||||
PURL: "pkg:rpm/redhat/dbus-common@1.12.8-26.el8?arch=noarch&distro=rhel-8.10&epoch=1&upstream=dbus-1.12.8-26.el8.src.rpm",
|
||||
Metadata: pkg.RpmDBEntry{Arch: "noarch"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -113,10 +116,11 @@ func TestDecoder_Decode(t *testing.T) {
|
||||
purl: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3",
|
||||
expected: []pkg.Package{
|
||||
{
|
||||
Name: "curl",
|
||||
Version: "7.61.1",
|
||||
Type: pkg.ApkPkg,
|
||||
PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3",
|
||||
Name: "curl",
|
||||
Version: "7.61.1",
|
||||
Type: pkg.ApkPkg,
|
||||
PURL: "pkg:apk/curl@7.61.1?arch=aarch64&distro=alpine-3.20.3",
|
||||
Metadata: pkg.ApkDBEntry{Architecture: "aarch64"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -44,6 +44,10 @@ func (d decoder) Decode(r io.Reader) (*sbom.SBOM, sbom.FormatID, string, error)
|
||||
return nil, "", "", fmt.Errorf("unable to seek to start of SPDX JSON SBOM: %+v", err)
|
||||
}
|
||||
|
||||
if strings.HasPrefix(version, "3") {
|
||||
return decodeSpdx3(version, reader)
|
||||
}
|
||||
|
||||
doc, err := spdxJson.Read(reader)
|
||||
if err != nil {
|
||||
return nil, id, version, fmt.Errorf("unable to decode spdx json: %w", err)
|
||||
@@ -62,11 +66,6 @@ func (d decoder) Identify(r io.Reader) (sbom.FormatID, string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
if _, err := reader.Seek(0, io.SeekStart); err != nil {
|
||||
log.Debugf("unable to seek to start of SPDX JSON SBOM: %+v", err)
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// Example JSON document
|
||||
// {
|
||||
// "spdxVersion": "SPDX-2.3",
|
||||
@@ -85,7 +84,18 @@ func (d decoder) Identify(r io.Reader) (sbom.FormatID, string) {
|
||||
|
||||
id, version := getFormatInfo(doc.SPDXVersion)
|
||||
if version == "" || id != ID {
|
||||
// not a spdx json document that we support
|
||||
// not a spdx 2 json document that we support, check for v3
|
||||
|
||||
if _, err = reader.Seek(0, io.SeekStart); err != nil {
|
||||
log.Debugf("unable to seek to start of SPDX JSON SBOM: %+v", err)
|
||||
return "", ""
|
||||
}
|
||||
|
||||
id3, version3 := identifySpdx3(reader)
|
||||
if id3 != "" && version3 != "" {
|
||||
return id3, version3
|
||||
}
|
||||
|
||||
return "", ""
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/spdx/tools-golang/spdx/v2/v2_1"
|
||||
"github.com/spdx/tools-golang/spdx/v2/v2_2"
|
||||
"github.com/spdx/tools-golang/spdx/v2/v2_3"
|
||||
"github.com/spdx/tools-golang/spdx/v3/v3_0"
|
||||
|
||||
"github.com/anchore/syft/syft/format/common/spdxhelpers"
|
||||
"github.com/anchore/syft/syft/format/internal/spdxutil"
|
||||
@@ -22,8 +23,9 @@ func SupportedVersions() []string {
|
||||
}
|
||||
|
||||
type EncoderConfig struct {
|
||||
Version string
|
||||
Pretty bool // don't include spaces and newlines; same as jq -c
|
||||
Version string
|
||||
Pretty bool // don't include spaces and newlines; same as jq -c
|
||||
DefaultVersion string
|
||||
}
|
||||
|
||||
type encoder struct {
|
||||
@@ -38,8 +40,9 @@ func NewFormatEncoderWithConfig(cfg EncoderConfig) (sbom.FormatEncoder, error) {
|
||||
|
||||
func DefaultEncoderConfig() EncoderConfig {
|
||||
return EncoderConfig{
|
||||
Version: spdxutil.DefaultVersion,
|
||||
Pretty: false,
|
||||
DefaultVersion: spdxutil.DefaultVersion,
|
||||
Version: spdxutil.DefaultVersion,
|
||||
Pretty: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +71,7 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
|
||||
doc := v2_1.Document{}
|
||||
err = convert.Document(latestDoc, &doc)
|
||||
encodeDoc = doc
|
||||
|
||||
case "2.2":
|
||||
doc := v2_2.Document{}
|
||||
err = convert.Document(latestDoc, &doc)
|
||||
@@ -77,6 +81,11 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
|
||||
doc := v2_3.Document{}
|
||||
err = convert.Document(latestDoc, &doc)
|
||||
encodeDoc = doc
|
||||
|
||||
case spdxutil.V3_0:
|
||||
doc := &v3_0.Document{}
|
||||
err = convert.Document(latestDoc, doc)
|
||||
encodeDoc = doc
|
||||
default:
|
||||
return fmt.Errorf("unsupported SPDX version %q", e.cfg.Version)
|
||||
}
|
||||
@@ -95,3 +104,7 @@ func (e encoder) Encode(writer io.Writer, s sbom.SBOM) error {
|
||||
|
||||
return enc.Encode(encodeDoc)
|
||||
}
|
||||
|
||||
func (e encoder) DefaultVersion() bool {
|
||||
return e.cfg.DefaultVersion == e.cfg.Version
|
||||
}
|
||||
|
||||
@@ -216,6 +216,10 @@ func redactor(values ...string) testutil.Redactor {
|
||||
// each SBOM reports a unique documentNamespace when generated, this is not useful for snapshot testing
|
||||
`"documentNamespace":\s+"[^"]*"`: `"documentNamespace":"redacted"`,
|
||||
|
||||
// spdx3 IDs are URI with the documentnamespace spdxId containing a UID; namespace is the equivalent documentNamespace with the same value
|
||||
`"spdxId":\s+"[^"]*"`: `"spdxId":"https://redacted"`,
|
||||
`"namespace":\s+"[^"]*"`: `"namespace":"https://redacted/"`,
|
||||
|
||||
// the license list will be updated periodically, the value here should not be directly tested in snapshot tests
|
||||
`"licenseListVersion":\s+"[^"]*"`: `"licenseListVersion":"redacted"`,
|
||||
},
|
||||
@@ -239,12 +243,14 @@ func TestSupportedVersions(t *testing.T) {
|
||||
"2.2": 2,
|
||||
// the source-to-package relationships can be removed since the primaryPackagePurpose info is available in 2.3
|
||||
"2.3": 0,
|
||||
"3.0": 0,
|
||||
}
|
||||
|
||||
pkgCountOffsetPerVersion := map[string]int{
|
||||
"2.1": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.2": 1, // the source is mapped as a package, but cannot distinguish it since the primaryPackagePurpose info is not available until 2.3
|
||||
"2.3": 0, // the source package can be removed since the primaryPackagePurpose info is available
|
||||
"3.0": 0,
|
||||
}
|
||||
|
||||
for _, enc := range encs {
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package spdxjson
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
|
||||
"github.com/spdx/tools-golang/spdx/v3/v3_0"
|
||||
|
||||
"github.com/anchore/syft/syft/format/common/spdxhelpers"
|
||||
"github.com/anchore/syft/syft/format/internal/spdxutil"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
var spdx3_0contextRegex = spdxContextRegex(spdxutil.V3_0)
|
||||
|
||||
func decodeSpdx3(version string, reader io.Reader) (*sbom.SBOM, sbom.FormatID, string, error) {
|
||||
switch version {
|
||||
case spdxutil.V3_0:
|
||||
doc := v3_0.NewDocument(v3_0.ProfileIdentifierType_Software, "", nil, nil)
|
||||
err := doc.FromJSON(reader)
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
sb, err := spdxhelpers.ToSyftModelV3(doc)
|
||||
return sb, spdxutil.JSONFormatID, spdxutil.V3_0, err
|
||||
default:
|
||||
return nil, "", "", fmt.Errorf("unsupported version: %v", version)
|
||||
}
|
||||
}
|
||||
|
||||
func identifySpdx3(reader io.Reader) (sbom.FormatID, string) {
|
||||
type Document struct {
|
||||
Context string `json:"@context"`
|
||||
}
|
||||
|
||||
dec := json.NewDecoder(reader)
|
||||
|
||||
var doc Document
|
||||
if err := dec.Decode(&doc); err != nil {
|
||||
// maybe not json? maybe not valid? doesn't matter, we won't process it.
|
||||
return "", ""
|
||||
}
|
||||
|
||||
formatID := sbom.FormatID("")
|
||||
spdxVersion := ""
|
||||
|
||||
switch {
|
||||
case doc.Context == "":
|
||||
case spdx3_0contextRegex.MatchString(doc.Context):
|
||||
formatID = spdxutil.JSONFormatID
|
||||
spdxVersion = spdxutil.V3_0
|
||||
default:
|
||||
}
|
||||
|
||||
return formatID, spdxVersion
|
||||
}
|
||||
|
||||
func spdxContextRegex(minorVersion string) *regexp.Regexp {
|
||||
// today this is "3.0.1", but is likely to be changed to only include the minor version "3.0"
|
||||
return regexp.MustCompile(regexp.QuoteMeta("https://spdx.org/rdf/") + minorVersion + `(\.\d+)?` + regexp.QuoteMeta("/spdx-context.jsonld"))
|
||||
}
|
||||
@@ -0,0 +1,426 @@
|
||||
package spdxjson
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/google/go-cmp/cmp/cmpopts"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/format/internal/spdxutil"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
// TestSPDX3JSONRoundTrip_AllPackageTypes encodes a syft SBOM as SPDX 3.0 JSON and decodes it back, asserting
|
||||
// (via a full struct diff) that every package field representable in SPDX 3.0 survives the trip.
|
||||
//
|
||||
// Each case provides the package to encode (input) and the package expected after decoding
|
||||
// (want). They differ because some information is not representable in the SPDX 3.0 model:
|
||||
// - Locations / FoundBy are not encoded per-package (ignored in the diff below).
|
||||
// - The package ID is derived from the SPDX element ID rather than the original syft ID
|
||||
// (the unexported id field is ignored in the diff below).
|
||||
// - Type and Language are reconstructed from the package URL type.
|
||||
// - Metadata is only reconstructed for the package types with dedicated handling in the
|
||||
// SPDX encoder/decoder (apk, deb, rpm, java-archive, go-module); for all other types the
|
||||
// decoded package carries no metadata.
|
||||
func TestSPDX3JSONRoundTrip_AllPackageTypes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
license := func(value string) pkg.LicenseSet {
|
||||
return pkg.NewLicenseSet(pkg.NewLicenseWithContext(ctx, value))
|
||||
}
|
||||
cpes := func(value string) []cpe.CPE {
|
||||
return []cpe.CPE{cpe.Must(value, cpe.DeclaredSource)}
|
||||
}
|
||||
|
||||
cases := []struct {
|
||||
name string
|
||||
input pkg.Package
|
||||
want pkg.Package
|
||||
}{
|
||||
{
|
||||
name: "python package (no reconstructed metadata)",
|
||||
input: pkg.Package{
|
||||
Name: "package-python",
|
||||
Version: "1.0.1",
|
||||
Type: pkg.PythonPkg,
|
||||
Language: pkg.Python,
|
||||
Licenses: license("MIT"),
|
||||
CPEs: cpes("cpe:2.3:a:python:package-python:1.0.1:*:*:*:*:*:*:*"),
|
||||
Locations: file.NewLocationSet(file.NewLocation("/python")),
|
||||
PURL: "pkg:pypi/package-python@1.0.1",
|
||||
Metadata: pkg.PythonPackage{Name: "package-python", Version: "1.0.1"},
|
||||
},
|
||||
want: pkg.Package{
|
||||
Name: "package-python",
|
||||
Version: "1.0.1",
|
||||
Type: pkg.PythonPkg,
|
||||
Language: pkg.Python,
|
||||
Licenses: license("MIT"),
|
||||
CPEs: cpes("cpe:2.3:a:python:package-python:1.0.1:*:*:*:*:*:*:*"),
|
||||
PURL: "pkg:pypi/package-python@1.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "npm package (no reconstructed metadata)",
|
||||
input: pkg.Package{
|
||||
Name: "package-npm",
|
||||
Version: "2.0.1",
|
||||
Type: pkg.NpmPkg,
|
||||
Language: pkg.JavaScript,
|
||||
Licenses: license("Apache-2.0"),
|
||||
CPEs: cpes("cpe:2.3:a:npm:package-npm:2.0.1:*:*:*:*:*:*:*"),
|
||||
Locations: file.NewLocationSet(file.NewLocation("/npm")),
|
||||
PURL: "pkg:npm/package-npm@2.0.1",
|
||||
Metadata: pkg.NpmPackage{Name: "package-npm", Version: "2.0.1"},
|
||||
},
|
||||
want: pkg.Package{
|
||||
Name: "package-npm",
|
||||
Version: "2.0.1",
|
||||
Type: pkg.NpmPkg,
|
||||
Language: pkg.JavaScript,
|
||||
Licenses: license("Apache-2.0"),
|
||||
CPEs: cpes("cpe:2.3:a:npm:package-npm:2.0.1:*:*:*:*:*:*:*"),
|
||||
PURL: "pkg:npm/package-npm@2.0.1",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "apk package",
|
||||
input: pkg.Package{
|
||||
Name: "package-apk",
|
||||
Version: "3.0",
|
||||
Type: pkg.ApkPkg,
|
||||
Licenses: license("GPL-2.0-only"),
|
||||
CPEs: cpes("cpe:2.3:a:alpine:package-apk:3.0:*:*:*:*:*:*:*"),
|
||||
Locations: file.NewLocationSet(file.NewLocation("/apk")),
|
||||
PURL: "pkg:apk/alpine/package-apk@3.0?arch=x86_64&upstream=apk-origin",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "package-apk",
|
||||
OriginPackage: "apk-origin",
|
||||
Maintainer: "Alpine Maintainer",
|
||||
Version: "3.0",
|
||||
Architecture: "x86_64",
|
||||
Description: "the apk package",
|
||||
},
|
||||
},
|
||||
want: pkg.Package{
|
||||
Name: "package-apk",
|
||||
Version: "3.0",
|
||||
Type: pkg.ApkPkg,
|
||||
Licenses: license("GPL-2.0-only"),
|
||||
CPEs: cpes("cpe:2.3:a:alpine:package-apk:3.0:*:*:*:*:*:*:*"),
|
||||
PURL: "pkg:apk/alpine/package-apk@3.0?arch=x86_64&upstream=apk-origin",
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "package-apk",
|
||||
OriginPackage: "apk-origin",
|
||||
Maintainer: "Alpine Maintainer",
|
||||
Version: "3.0",
|
||||
Architecture: "x86_64",
|
||||
Description: "the apk package",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deb package",
|
||||
input: pkg.Package{
|
||||
Name: "package-deb",
|
||||
Version: "4.0",
|
||||
Type: pkg.DebPkg,
|
||||
Licenses: license("LGPL-2.1-only"),
|
||||
CPEs: cpes("cpe:2.3:a:debian:package-deb:4.0:*:*:*:*:*:*:*"),
|
||||
Locations: file.NewLocationSet(file.NewLocation("/deb")),
|
||||
PURL: "pkg:deb/debian/package-deb@4.0?arch=amd64&upstream=deb-src%404.1",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "package-deb",
|
||||
Source: "deb-src",
|
||||
Version: "4.0",
|
||||
SourceVersion: "4.1",
|
||||
Architecture: "amd64",
|
||||
Maintainer: "Debian Maintainer",
|
||||
},
|
||||
},
|
||||
want: pkg.Package{
|
||||
Name: "package-deb",
|
||||
Version: "4.0",
|
||||
Type: pkg.DebPkg,
|
||||
Licenses: license("LGPL-2.1-only"),
|
||||
CPEs: cpes("cpe:2.3:a:debian:package-deb:4.0:*:*:*:*:*:*:*"),
|
||||
PURL: "pkg:deb/debian/package-deb@4.0?arch=amd64&upstream=deb-src%404.1",
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "package-deb",
|
||||
Source: "deb-src",
|
||||
Version: "4.0",
|
||||
SourceVersion: "4.1",
|
||||
Architecture: "amd64",
|
||||
Maintainer: "Debian Maintainer",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rpm package",
|
||||
input: pkg.Package{
|
||||
Name: "package-rpm",
|
||||
Version: "5.0",
|
||||
Type: pkg.RpmPkg,
|
||||
Licenses: license("BSD-3-Clause"),
|
||||
CPEs: cpes("cpe:2.3:a:redhat:package-rpm:5.0:*:*:*:*:*:*:*"),
|
||||
Locations: file.NewLocationSet(file.NewLocation("/rpm")),
|
||||
PURL: "pkg:rpm/redhat/package-rpm@5.0?arch=x86_64&upstream=rpm-src-5.0",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "package-rpm",
|
||||
Version: "5.0",
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "rpm-src-5.0",
|
||||
Vendor: "RedHat",
|
||||
},
|
||||
},
|
||||
want: pkg.Package{
|
||||
Name: "package-rpm",
|
||||
Version: "5.0",
|
||||
Type: pkg.RpmPkg,
|
||||
Licenses: license("BSD-3-Clause"),
|
||||
CPEs: cpes("cpe:2.3:a:redhat:package-rpm:5.0:*:*:*:*:*:*:*"),
|
||||
PURL: "pkg:rpm/redhat/package-rpm@5.0?arch=x86_64&upstream=rpm-src-5.0",
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
Name: "package-rpm",
|
||||
Version: "5.0",
|
||||
Arch: "x86_64",
|
||||
SourceRpm: "rpm-src-5.0",
|
||||
Vendor: "RedHat",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "java package",
|
||||
input: pkg.Package{
|
||||
Name: "package-java",
|
||||
Version: "6.0",
|
||||
Type: pkg.JavaPkg,
|
||||
Language: pkg.Java,
|
||||
Licenses: license("EPL-2.0"),
|
||||
CPEs: cpes("cpe:2.3:a:example:package-java:6.0:*:*:*:*:*:*:*"),
|
||||
Locations: file.NewLocationSet(file.NewLocation("/java")),
|
||||
PURL: "pkg:maven/com.example/package-java@6.0",
|
||||
Metadata: pkg.JavaArchive{
|
||||
ArchiveDigests: []file.Digest{
|
||||
{Algorithm: "sha1", Value: "3b4ab96c371d913e2a88c269844b6c5fb5cbe761"},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: pkg.Package{
|
||||
Name: "package-java",
|
||||
Version: "6.0",
|
||||
Type: pkg.JavaPkg,
|
||||
Language: pkg.Java,
|
||||
Licenses: license("EPL-2.0"),
|
||||
CPEs: cpes("cpe:2.3:a:example:package-java:6.0:*:*:*:*:*:*:*"),
|
||||
PURL: "pkg:maven/com.example/package-java@6.0",
|
||||
Metadata: pkg.JavaArchive{
|
||||
ArchiveDigests: []file.Digest{
|
||||
{Algorithm: "sha1", Value: "3b4ab96c371d913e2a88c269844b6c5fb5cbe761"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "go module package",
|
||||
input: pkg.Package{
|
||||
Name: "package-go",
|
||||
Version: "7.0",
|
||||
Type: pkg.GoModulePkg,
|
||||
Language: pkg.Go,
|
||||
Licenses: license("MPL-2.0"),
|
||||
CPEs: cpes("cpe:2.3:a:example:package-go:7.0:*:*:*:*:*:*:*"),
|
||||
Locations: file.NewLocationSet(file.NewLocation("/go")),
|
||||
PURL: "pkg:golang/example.com/package-go@7.0",
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
H1Digest: "h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=",
|
||||
},
|
||||
},
|
||||
want: pkg.Package{
|
||||
Name: "package-go",
|
||||
Version: "7.0",
|
||||
Type: pkg.GoModulePkg,
|
||||
Language: pkg.Go,
|
||||
Licenses: license("MPL-2.0"),
|
||||
CPEs: cpes("cpe:2.3:a:example:package-go:7.0:*:*:*:*:*:*:*"),
|
||||
PURL: "pkg:golang/example.com/package-go@7.0",
|
||||
Metadata: pkg.GolangBinaryBuildinfoEntry{
|
||||
H1Digest: "h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var inputs []pkg.Package
|
||||
var want []pkg.Package
|
||||
for _, c := range cases {
|
||||
in := c.input
|
||||
in.SetID()
|
||||
inputs = append(inputs, in)
|
||||
want = append(want, c.want)
|
||||
}
|
||||
|
||||
subject := sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(inputs...),
|
||||
},
|
||||
Descriptor: sbom.Descriptor{
|
||||
Name: "syft",
|
||||
Version: "v0.42.0-bogus",
|
||||
},
|
||||
Source: source.Description{
|
||||
Metadata: source.DirectoryMetadata{Path: "/home/app", Base: "/home/app"},
|
||||
},
|
||||
}
|
||||
|
||||
cfg := DefaultEncoderConfig()
|
||||
cfg.Pretty = true
|
||||
cfg.Version = spdxutil.V3_0
|
||||
|
||||
enc, err := NewFormatEncoderWithConfig(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, enc.Encode(&buf, subject))
|
||||
|
||||
dec := NewFormatDecoder()
|
||||
|
||||
id, version := dec.Identify(bytes.NewReader(buf.Bytes()))
|
||||
require.Equal(t, ID, id)
|
||||
require.Equal(t, spdxutil.V3_0, version)
|
||||
|
||||
s, decodeID, decodeVersion, err := dec.Decode(bytes.NewReader(buf.Bytes()))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, s)
|
||||
require.Equal(t, ID, decodeID)
|
||||
require.Equal(t, spdxutil.V3_0, decodeVersion)
|
||||
|
||||
// the directory source should be recognized as the document root and not surface as a package
|
||||
require.Equal(t, len(want), s.Artifacts.Packages.PackageCount())
|
||||
|
||||
got := s.Artifacts.Packages.Sorted()
|
||||
sortByName(want)
|
||||
sortByName(got)
|
||||
|
||||
diff := cmp.Diff(want, got,
|
||||
cmpopts.IgnoreUnexported(pkg.Package{}, pkg.LicenseSet{}, file.LocationSet{}),
|
||||
cmpopts.IgnoreFields(pkg.Package{}, "Locations", "FoundBy"),
|
||||
)
|
||||
require.Empty(t, diff, "decoded packages differ from expected (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
func sortByName(pkgs []pkg.Package) {
|
||||
sort.Slice(pkgs, func(i, j int) bool {
|
||||
return pkgs[i].Name < pkgs[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
// TestSPDX3JSONRoundTrip_FullSBOM encodes a more complete SBOM (a container image source, several
|
||||
// packages, and relationships between them) as SPDX 3.0 JSON and decodes it back, verifying that
|
||||
// the source description and the package relationships survive the round trip.
|
||||
func TestSPDX3JSONRoundTrip_FullSBOM(t *testing.T) {
|
||||
newPkg := func(name string) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: name,
|
||||
Version: "1.0",
|
||||
Type: pkg.NpmPkg,
|
||||
Language: pkg.JavaScript,
|
||||
PURL: "pkg:npm/" + name + "@1.0",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation("/" + name),
|
||||
),
|
||||
}
|
||||
p.SetID()
|
||||
return p
|
||||
}
|
||||
|
||||
app := newPkg("app")
|
||||
lib := newPkg("lib")
|
||||
dep := newPkg("dep")
|
||||
|
||||
subject := sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(app, lib, dep),
|
||||
},
|
||||
Relationships: []artifact.Relationship{
|
||||
// app contains lib
|
||||
{From: app, To: lib, Type: artifact.ContainsRelationship},
|
||||
// dep is a dependency of lib
|
||||
{From: dep, To: lib, Type: artifact.DependencyOfRelationship},
|
||||
},
|
||||
Descriptor: sbom.Descriptor{
|
||||
Name: "syft",
|
||||
Version: "v0.42.0-bogus",
|
||||
},
|
||||
Source: source.Description{
|
||||
Name: "some-image",
|
||||
Version: "some-tag",
|
||||
Supplier: "some-supplier",
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "some-image:some-tag",
|
||||
ManifestDigest: "sha256:abcdef0123456789",
|
||||
Architecture: "amd64",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cfg := DefaultEncoderConfig()
|
||||
cfg.Pretty = true
|
||||
cfg.Version = spdxutil.V3_0
|
||||
|
||||
enc, err := NewFormatEncoderWithConfig(cfg)
|
||||
require.NoError(t, err)
|
||||
|
||||
var buf bytes.Buffer
|
||||
require.NoError(t, enc.Encode(&buf, subject))
|
||||
|
||||
dec := NewFormatDecoder()
|
||||
s, decodeID, decodeVersion, err := dec.Decode(bytes.NewReader(buf.Bytes()))
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, s)
|
||||
require.Equal(t, ID, decodeID)
|
||||
require.Equal(t, spdxutil.V3_0, decodeVersion)
|
||||
|
||||
// the container image source is decoded as the document root rather than a package
|
||||
require.Equal(t, 3, s.Artifacts.Packages.PackageCount())
|
||||
|
||||
// source information survives, and the source is not surfaced as a package
|
||||
// (the ID is re-derived from the SPDX element ID and is therefore not compared)
|
||||
require.Equal(t, "some-image", s.Source.Name)
|
||||
require.Equal(t, "some-tag", s.Source.Version)
|
||||
require.Equal(t, "some-supplier", s.Source.Supplier)
|
||||
require.IsType(t, source.ImageMetadata{}, s.Source.Metadata)
|
||||
imageMetadata := s.Source.Metadata.(source.ImageMetadata)
|
||||
// the user input is reconstructed from the image name and tag (version)
|
||||
require.Equal(t, "some-image:some-tag", imageMetadata.UserInput)
|
||||
require.Equal(t, "sha256:abcdef0123456789", imageMetadata.ManifestDigest)
|
||||
|
||||
// relationships between packages survive, including direction
|
||||
type rel struct {
|
||||
from string
|
||||
to string
|
||||
typ artifact.RelationshipType
|
||||
}
|
||||
var got []rel
|
||||
for _, r := range s.Relationships {
|
||||
from, fromOk := r.From.(pkg.Package)
|
||||
to, toOk := r.To.(pkg.Package)
|
||||
require.True(t, fromOk, "relationship from should be a package: %+v", r.From)
|
||||
require.True(t, toOk, "relationship to should be a package: %+v", r.To)
|
||||
got = append(got, rel{from: from.Name, to: to.Name, typ: r.Type})
|
||||
}
|
||||
|
||||
require.ElementsMatch(t, []rel{
|
||||
{from: "app", to: "lib", typ: artifact.ContainsRelationship},
|
||||
{from: "dep", to: "lib", typ: artifact.DependencyOfRelationship},
|
||||
}, got)
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
{
|
||||
"@context" : "https://spdx.org/rdf/3.0.1/spdx-context.jsonld",
|
||||
"@graph" : [ {
|
||||
"@id" : "_:creationInfo_0",
|
||||
"type" : "CreationInfo",
|
||||
"specVersion" : "3.0.1",
|
||||
"createdBy" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd0" ],
|
||||
"createdUsing" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/additionalToolSPDXRef-gnrtd2", "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/additionalToolSPDXRef-gnrtd1" ],
|
||||
"created" : "2021-08-26T01:46:00Z"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd23",
|
||||
"type" : "Relationship",
|
||||
"relationshipType" : "hasDeclaredLicense",
|
||||
"to" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd8" ],
|
||||
"from" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd4",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/document0",
|
||||
"type" : "SpdxDocument",
|
||||
"dataLicense" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd24",
|
||||
"rootElement" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd4" ],
|
||||
"name" : "hello",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/additionalToolSPDXRef-gnrtd1",
|
||||
"type" : "Tool",
|
||||
"name" : "github.com/spdx/tools-golang/builder",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/additionalToolSPDXRef-gnrtd2",
|
||||
"type" : "Tool",
|
||||
"name" : "github.com/spdx/tools-golang/idsearcher",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd8",
|
||||
"type" : "simplelicensing_LicenseExpression",
|
||||
"simplelicensing_licenseExpression" : "GPL-3.0-or-later",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd20",
|
||||
"type" : "simplelicensing_LicenseExpression",
|
||||
"simplelicensing_licenseExpression" : "NOASSERTION",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd24",
|
||||
"type" : "simplelicensing_LicenseExpression",
|
||||
"simplelicensing_licenseExpression" : "CC0-1.0",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd14",
|
||||
"type" : "LifecycleScopedRelationship",
|
||||
"relationshipType" : "usesTool",
|
||||
"scope" : "build",
|
||||
"to" : [ "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd13" ],
|
||||
"completeness" : "noAssertion",
|
||||
"from" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd4",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd0",
|
||||
"type" : "Person",
|
||||
"externalIdentifier" : [ {
|
||||
"type" : "ExternalIdentifier",
|
||||
"identifier" : "steve@swinslow.net",
|
||||
"externalIdentifierType" : "email"
|
||||
} ],
|
||||
"name" : "Steve Winslow",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd6",
|
||||
"type" : "software_File",
|
||||
"software_copyrightText" : "Copyright Contributors to the spdx-examples project.",
|
||||
"verifiedUsing" : [ {
|
||||
"type" : "Hash",
|
||||
"algorithm" : "md5",
|
||||
"hashValue" : "935054fe899ca782e11003bbae5e166c"
|
||||
}, {
|
||||
"type" : "Hash",
|
||||
"algorithm" : "sha1",
|
||||
"hashValue" : "20862a6d08391d07d09344029533ec644fac6b21"
|
||||
}, {
|
||||
"type" : "Hash",
|
||||
"algorithm" : "sha256",
|
||||
"hashValue" : "b4e5ca56d1f9110ca94ed0bf4e6d9ac11c2186eb7cd95159c6fdb50e8db5a823"
|
||||
} ],
|
||||
"name" : "./src/hello.c",
|
||||
"software_primaryPurpose" : "source",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd11",
|
||||
"type" : "software_File",
|
||||
"software_copyrightText" : "NOASSERTION",
|
||||
"verifiedUsing" : [ {
|
||||
"type" : "Hash",
|
||||
"algorithm" : "sha1",
|
||||
"hashValue" : "20291a81ef065ff891b537b64d4fdccaf6f5ac02"
|
||||
}, {
|
||||
"type" : "Hash",
|
||||
"algorithm" : "sha256",
|
||||
"hashValue" : "83a33ff09648bb5fc5272baca88cf2b59fd81ac4cc6817b86998136af368708e"
|
||||
}, {
|
||||
"type" : "Hash",
|
||||
"algorithm" : "md5",
|
||||
"hashValue" : "08a12c966d776864cc1eb41fd03c3c3d"
|
||||
} ],
|
||||
"name" : "./build/hello",
|
||||
"contentType" : "application/octet-stream",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd13",
|
||||
"type" : "software_File",
|
||||
"software_copyrightText" : "NOASSERTION",
|
||||
"verifiedUsing" : [ {
|
||||
"type" : "Hash",
|
||||
"algorithm" : "sha1",
|
||||
"hashValue" : "69a2e85696fff1865c3f0686d6c3824b59915c80"
|
||||
}, {
|
||||
"type" : "Hash",
|
||||
"algorithm" : "sha256",
|
||||
"hashValue" : "5da19033ba058e322e21c90e6d6d859c90b1b544e7840859c12cae5da005e79c"
|
||||
}, {
|
||||
"type" : "Hash",
|
||||
"algorithm" : "md5",
|
||||
"hashValue" : "559424589a4f3f75fd542810473d8bc1"
|
||||
} ],
|
||||
"name" : "./src/Makefile",
|
||||
"software_primaryPurpose" : "source",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
}, {
|
||||
"spdxId" : "https://swinslow.net/spdx-examples/example1/hello-v3-specv3/SPDXRef-gnrtd4",
|
||||
"type" : "software_Package",
|
||||
"software_copyrightText" : "NOASSERTION",
|
||||
"software_downloadLocation" : "git+https://github.com/swinslow/spdx-examples.git#example1/content",
|
||||
"verifiedUsing" : [ {
|
||||
"type" : "PackageVerificationCode",
|
||||
"algorithm" : "sha1",
|
||||
"hashValue" : "9d20237bb72087e87069f96afb41c6ca2fa2a342"
|
||||
} ],
|
||||
"name" : "hello",
|
||||
"creationInfo" : "_:creationInfo_0"
|
||||
} ]
|
||||
}
|
||||
@@ -26,6 +26,8 @@ catalogers:
|
||||
- pkg.ApkDBEntry
|
||||
package_types: # AUTO-GENERATED
|
||||
- apk
|
||||
purl_types: # AUTO-GENERATED
|
||||
- apk
|
||||
json_schema_types: # AUTO-GENERATED
|
||||
- ApkDbEntry
|
||||
capabilities: # MANUAL - preserved across regeneration
|
||||
|
||||
@@ -27,6 +27,8 @@ catalogers:
|
||||
- pkg.AlpmDBEntry
|
||||
package_types: # AUTO-GENERATED
|
||||
- alpm
|
||||
purl_types: # AUTO-GENERATED
|
||||
- alpm
|
||||
json_schema_types: # AUTO-GENERATED
|
||||
- AlpmDbEntry
|
||||
capabilities: # MANUAL - preserved across regeneration
|
||||
|
||||
@@ -66,6 +66,16 @@ catalogers:
|
||||
cpes:
|
||||
- cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/julia'
|
||||
packages:
|
||||
- class: julia-binary
|
||||
name: julia
|
||||
purl: pkg:generic/julia
|
||||
cpes:
|
||||
- cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/helm'
|
||||
@@ -242,6 +252,77 @@ catalogers:
|
||||
- cpe:2.3:a:percona:percona_server:*:*:*:*:*:*:*:*
|
||||
- cpe:2.3:a:percona:xtradb_cluster:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/mysqld'
|
||||
packages:
|
||||
- class: mysqld-mysql-cluster-legacy-binary
|
||||
name: mysql-server
|
||||
purl: pkg:generic/mysql-server
|
||||
cpes:
|
||||
- cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*
|
||||
- cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/mysqld'
|
||||
packages:
|
||||
- class: mysqld-binary
|
||||
name: ""
|
||||
purl: pkg:/
|
||||
cpes: []
|
||||
type: BinaryPkg
|
||||
- class: mysqld-mysql-cluster-legacy-binary
|
||||
name: mysql-cluster
|
||||
purl: pkg:generic/mysql-cluster
|
||||
cpes:
|
||||
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- class: mysqld-mysql-cluster-binary
|
||||
name: mysql-cluster
|
||||
purl: pkg:generic/mysql-cluster
|
||||
cpes:
|
||||
- cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*
|
||||
- cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*
|
||||
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- class: mysqld-mysql-server-binary
|
||||
name: mysql-server
|
||||
purl: pkg:generic/mysql-server
|
||||
cpes:
|
||||
- cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*
|
||||
- cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/ndbd'
|
||||
packages:
|
||||
- class: ndbd-binary
|
||||
name: mysql-cluster
|
||||
purl: pkg:generic/mysql-cluster
|
||||
cpes:
|
||||
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/ndbmtd'
|
||||
packages:
|
||||
- class: ndbmtd-binary
|
||||
name: mysql-cluster
|
||||
purl: pkg:generic/mysql-cluster
|
||||
cpes:
|
||||
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/ndb_mgmd'
|
||||
packages:
|
||||
- class: ndb_mgmd-binary
|
||||
name: mysql-cluster
|
||||
purl: pkg:generic/mysql-cluster
|
||||
cpes:
|
||||
- cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/xtrabackup'
|
||||
@@ -427,6 +508,17 @@ catalogers:
|
||||
criteria:
|
||||
- '**/openssl'
|
||||
packages:
|
||||
- class: openssl-binary
|
||||
name: ""
|
||||
purl: pkg:/
|
||||
cpes: []
|
||||
type: BinaryPkg
|
||||
- class: openssl-binary-aws-lc
|
||||
name: aws-lc
|
||||
purl: pkg:generic/aws-lc
|
||||
cpes:
|
||||
- cpe:2.3:a:amazon:aws_libcrypto:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- class: openssl-binary
|
||||
name: openssl
|
||||
purl: pkg:generic/openssl
|
||||
@@ -684,6 +776,76 @@ catalogers:
|
||||
cpes:
|
||||
- cpe:2.3:a:mongodb:mongodb:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/nginx-ingress-controller'
|
||||
packages:
|
||||
- class: ingress-nginx-binary
|
||||
name: nginx-ingress-controller
|
||||
purl: pkg:generic/nginx-ingress-controller
|
||||
cpes:
|
||||
- cpe:2.3:a:kubernetes:ingress-nginx:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/filebeat'
|
||||
packages:
|
||||
- class: filebeat-binary
|
||||
name: filebeat
|
||||
purl: pkg:generic/filebeat
|
||||
cpes:
|
||||
- cpe:2.3:a:elastic:filebeat:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/metricbeat'
|
||||
packages:
|
||||
- class: metricbeat-binary
|
||||
name: metricbeat
|
||||
purl: pkg:generic/metricbeat
|
||||
cpes:
|
||||
- cpe:2.3:a:elastic:metricbeat:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/heartbeat'
|
||||
packages:
|
||||
- class: heartbeat-binary
|
||||
name: heartbeat
|
||||
purl: pkg:generic/heartbeat
|
||||
cpes:
|
||||
- cpe:2.3:a:elastic:heartbeat:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/packetbeat'
|
||||
packages:
|
||||
- class: packetbeat-binary
|
||||
name: packetbeat
|
||||
purl: pkg:generic/packetbeat
|
||||
cpes:
|
||||
- cpe:2.3:a:elastic:packetbeat:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/auditbeat'
|
||||
packages:
|
||||
- class: auditbeat-binary
|
||||
name: auditbeat
|
||||
purl: pkg:generic/auditbeat
|
||||
cpes:
|
||||
- cpe:2.3:a:elastic:auditbeat:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/elastic-agent'
|
||||
packages:
|
||||
- class: elastic-agent-binary
|
||||
name: elastic-agent
|
||||
purl: pkg:generic/elastic-agent
|
||||
cpes:
|
||||
- cpe:2.3:a:elastic:elastic_agent:*:*:*:*:*:*:*:*
|
||||
type: BinaryPkg
|
||||
- method: glob
|
||||
criteria:
|
||||
- '**/java'
|
||||
@@ -830,6 +992,9 @@ catalogers:
|
||||
- apk
|
||||
- binary
|
||||
- rpm
|
||||
purl_types: # AUTO-GENERATED
|
||||
- apk
|
||||
- rpm
|
||||
json_schema_types: # AUTO-GENERATED
|
||||
- ElfBinaryPackageNoteJsonPayload
|
||||
capabilities: # MANUAL - edit capabilities here
|
||||
@@ -855,6 +1020,7 @@ catalogers:
|
||||
function: NewPEPackageCataloger
|
||||
selectors: # AUTO-GENERATED
|
||||
- binary
|
||||
- bpl
|
||||
- declared
|
||||
- directory
|
||||
- dll
|
||||
@@ -871,6 +1037,7 @@ catalogers:
|
||||
criteria: # AUTO-GENERATED
|
||||
- '**/*.dll'
|
||||
- '**/*.exe'
|
||||
- '**/*.bpl'
|
||||
metadata_types: # AUTO-GENERATED
|
||||
- pkg.PEBinary
|
||||
package_types: # AUTO-GENERATED
|
||||
|
||||
@@ -36,8 +36,9 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
// or testdata/classifiers/bin directory . Snippets are searched for first, and if not found, then existing binaries are
|
||||
// used. If no binary or snippet is found the test will fail. If '-must-use-original-binaries' is used the only
|
||||
// full binaries are tested (no snippets), and if no binary is found the test will be skipped.
|
||||
logicalFixture string
|
||||
expected pkg.Package
|
||||
logicalFixture string
|
||||
expected pkg.Package
|
||||
expectedPackages []pkg.Package
|
||||
}{
|
||||
{
|
||||
logicalFixture: "arangodb/3.11.8/linux-amd64",
|
||||
@@ -206,6 +207,96 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("mariadb-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// RHEL / MariaDB.org tarball builds do not embed the "-MariaDB" marker; the version is only
|
||||
// present in the build path (e.g. mariadb-11.8.5-2-redhat-x86_64). The release suffix ("-2")
|
||||
// must not leak into the version. Regression for anchore/grype#3452.
|
||||
logicalFixture: "mariadb/11.8.5/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "mariadb",
|
||||
Version: "11.8.5",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mariadb@11.8.5",
|
||||
Locations: locations("mariadb"),
|
||||
Metadata: metadata("mariadb-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "mysqld/9.7.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "mysql-server",
|
||||
Version: "9.7.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mysql-server@9.7.0",
|
||||
Locations: locations("mysqld"),
|
||||
Metadata: metadata("mysqld-mysql-server-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "mysql-cluster/9.7.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "mysql-cluster",
|
||||
Version: "9.7.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mysql-cluster@9.7.0",
|
||||
Locations: locations("mysqld"),
|
||||
Metadata: metadata("mysqld-mysql-cluster-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "mysql-cluster/7.6.17/linux-amd64",
|
||||
expectedPackages: []pkg.Package{
|
||||
{
|
||||
Name: "mysql-server",
|
||||
Version: "5.7.33",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mysql-server@5.7.33",
|
||||
Locations: locations("mysqld"),
|
||||
Metadata: metadata("mysqld-mysql-cluster-legacy-binary"),
|
||||
},
|
||||
{
|
||||
Name: "mysql-cluster",
|
||||
Version: "7.6.17",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mysql-cluster@7.6.17",
|
||||
Locations: locations("mysqld"),
|
||||
Metadata: metadata("mysqld-mysql-cluster-legacy-binary"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "ndbd/9.7.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "mysql-cluster",
|
||||
Version: "9.7.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mysql-cluster@9.7.0",
|
||||
Locations: locations("ndbd"),
|
||||
Metadata: metadata("ndbd-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "ndbmtd/9.7.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "mysql-cluster",
|
||||
Version: "9.7.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mysql-cluster@9.7.0",
|
||||
Locations: locations("ndbmtd"),
|
||||
Metadata: metadata("ndbmtd-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "ndb_mgmd/9.7.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "mysql-cluster",
|
||||
Version: "9.7.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/mysql-cluster@9.7.0",
|
||||
Locations: locations("ndb_mgmd"),
|
||||
Metadata: metadata("ndb_mgmd-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "mongodb/8.0.17/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -432,6 +523,39 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("haproxy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "helm/4.1.4/linux-s390x",
|
||||
expected: pkg.Package{
|
||||
Name: "helm",
|
||||
Version: "4.1.4",
|
||||
Type: "binary",
|
||||
PURL: "pkg:golang/helm.sh/helm@4.1.4",
|
||||
Locations: locations("helm"),
|
||||
Metadata: metadata("helm"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "helm/3.15.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "helm",
|
||||
Version: "3.15.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:golang/helm.sh/helm@3.15.2",
|
||||
Locations: locations("helm"),
|
||||
Metadata: metadata("helm"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "helm/3.12.0/linux-s390x",
|
||||
expected: pkg.Package{
|
||||
Name: "helm",
|
||||
Version: "3.12.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:golang/helm.sh/helm@3.12.0",
|
||||
Locations: locations("helm"),
|
||||
Metadata: metadata("helm"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "helm/3.11.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -443,6 +567,39 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("helm"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "helm/3.0.0-alpha.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "helm",
|
||||
Version: "3.0.0-alpha.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:golang/helm.sh/helm@3.0.0-alpha.1",
|
||||
Locations: locations("helm"),
|
||||
Metadata: metadata("helm"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "helm/2.17.0-rc.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "helm",
|
||||
Version: "2.17.0-rc.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:golang/helm.sh/helm@2.17.0-rc.1",
|
||||
Locations: locations("helm"),
|
||||
Metadata: metadata("helm"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "helm/2.0.0-beta.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "helm",
|
||||
Version: "2.0.0-beta.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:golang/helm.sh/helm@2.0.0-beta.2",
|
||||
Locations: locations("helm"),
|
||||
Metadata: metadata("helm"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "helm/3.10.3/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -1331,6 +1488,50 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("dart-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "deno/1.10.3/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "deno",
|
||||
Version: "1.10.3",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/deno@1.10.3",
|
||||
Locations: locations("deno"),
|
||||
Metadata: metadata("deno-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "deno/1.16.4/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "deno",
|
||||
Version: "1.16.4",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/deno@1.16.4",
|
||||
Locations: locations("deno"),
|
||||
Metadata: metadata("deno-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "deno/1.28.3/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "deno",
|
||||
Version: "1.28.3",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/deno@1.28.3",
|
||||
Locations: locations("deno"),
|
||||
Metadata: metadata("deno-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "deno/1.29.4/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "deno",
|
||||
Version: "1.29.4",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/deno@1.29.4",
|
||||
Locations: locations("deno"),
|
||||
Metadata: metadata("deno-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "deno/1.41.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -1485,6 +1686,17 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("openssl-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "aws-lc/1.69.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "aws-lc",
|
||||
Version: "1.69.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/aws-lc@1.69.0",
|
||||
Locations: locations("openssl"),
|
||||
Metadata: metadata("openssl-binary-aws-lc"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "openldap/2.6.10/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -1754,6 +1966,25 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// release-candidate elixir image — pre-fix the matchers stripped the
|
||||
// "-rc.1" suffix from the elixir-library result and missed the
|
||||
// elixir-binary entirely (#4819).
|
||||
logicalFixture: "elixir/1.12.0-rc.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "elixir",
|
||||
Version: "1.12.0-rc.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/elixir@1.12.0-rc.1",
|
||||
Locations: locations("elixir", "lib/elixir/ebin/elixir.app"),
|
||||
Metadata: pkg.BinarySignature{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
match("elixir-binary", "elixir"),
|
||||
match("elixir-library", "lib/elixir/ebin/elixir.app"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "istio_pilot-discovery/1.29.0-alpha.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -2227,6 +2458,447 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "nginx-ingress-controller/1.15.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "nginx-ingress-controller",
|
||||
Version: "1.15.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/nginx-ingress-controller@1.15.1",
|
||||
Locations: locations("nginx-ingress-controller"),
|
||||
Metadata: metadata("ingress-nginx-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "nginx-ingress-controller/1.11.8/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "nginx-ingress-controller",
|
||||
Version: "1.11.8",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/nginx-ingress-controller@1.11.8",
|
||||
Locations: locations("nginx-ingress-controller"),
|
||||
Metadata: metadata("ingress-nginx-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "nginx-ingress-controller/1.9.6/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "nginx-ingress-controller",
|
||||
Version: "1.9.6",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/nginx-ingress-controller@1.9.6",
|
||||
Locations: locations("nginx-ingress-controller"),
|
||||
Metadata: metadata("ingress-nginx-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "nginx-ingress-controller/1.7.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "nginx-ingress-controller",
|
||||
Version: "1.7.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/nginx-ingress-controller@1.7.1",
|
||||
Locations: locations("nginx-ingress-controller"),
|
||||
Metadata: metadata("ingress-nginx-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "nginx-ingress-controller/1.12.0-beta.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "nginx-ingress-controller",
|
||||
Version: "1.12.0-beta.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/nginx-ingress-controller@1.12.0-beta.0",
|
||||
Locations: locations("nginx-ingress-controller"),
|
||||
Metadata: metadata("ingress-nginx-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "nginx-ingress-controller/1.2.0-beta.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "nginx-ingress-controller",
|
||||
Version: "1.2.0-beta.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/nginx-ingress-controller@1.2.0-beta.1",
|
||||
Locations: locations("nginx-ingress-controller"),
|
||||
Metadata: metadata("ingress-nginx-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "nginx-ingress-controller/1.0.0-alpha.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "nginx-ingress-controller",
|
||||
Version: "1.0.0-alpha.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/nginx-ingress-controller@1.0.0-alpha.2",
|
||||
Locations: locations("nginx-ingress-controller"),
|
||||
Metadata: metadata("ingress-nginx-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "nginx-ingress-controller/0.34.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "nginx-ingress-controller",
|
||||
Version: "0.34.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/nginx-ingress-controller@0.34.0",
|
||||
Locations: locations("nginx-ingress-controller"),
|
||||
Metadata: metadata("ingress-nginx-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "nginx-ingress-controller/0.33.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "nginx-ingress-controller",
|
||||
Version: "0.33.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/nginx-ingress-controller@0.33.0",
|
||||
Locations: locations("nginx-ingress-controller"),
|
||||
Metadata: metadata("ingress-nginx-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "filebeat/9.4.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "filebeat",
|
||||
Version: "9.4.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/filebeat@9.4.2",
|
||||
Locations: locations("filebeat"),
|
||||
Metadata: metadata("filebeat-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "filebeat/8.18.4/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "filebeat",
|
||||
Version: "8.18.4",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/filebeat@8.18.4",
|
||||
Locations: locations("filebeat"),
|
||||
Metadata: metadata("filebeat-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "filebeat/8.11.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "filebeat",
|
||||
Version: "8.11.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/filebeat@8.11.2",
|
||||
Locations: locations("filebeat"),
|
||||
Metadata: metadata("filebeat-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "metricbeat/9.4.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "metricbeat",
|
||||
Version: "9.4.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/metricbeat@9.4.2",
|
||||
Locations: locations("metricbeat"),
|
||||
Metadata: metadata("metricbeat-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "heartbeat/9.4.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "heartbeat",
|
||||
Version: "9.4.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/heartbeat@9.4.2",
|
||||
Locations: locations("heartbeat"),
|
||||
Metadata: metadata("heartbeat-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "packetbeat/9.4.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "packetbeat",
|
||||
Version: "9.4.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/packetbeat@9.4.2",
|
||||
Locations: locations("packetbeat"),
|
||||
Metadata: metadata("packetbeat-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "auditbeat/9.4.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "auditbeat",
|
||||
Version: "9.4.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/auditbeat@9.4.2",
|
||||
Locations: locations("auditbeat"),
|
||||
Metadata: metadata("auditbeat-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "elastic-agent/9.4.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "elastic-agent",
|
||||
Version: "9.4.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/elastic-agent@9.4.2",
|
||||
Locations: locations("elastic-agent"),
|
||||
Metadata: metadata("elastic-agent-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "elastic-agent/9.0.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "elastic-agent",
|
||||
Version: "9.0.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/elastic-agent@9.0.0",
|
||||
Locations: locations("elastic-agent"),
|
||||
Metadata: metadata("elastic-agent-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "elastic-agent/8.19.4/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "elastic-agent",
|
||||
Version: "8.19.4",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/elastic-agent@8.19.4",
|
||||
Locations: locations("elastic-agent"),
|
||||
Metadata: metadata("elastic-agent-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "elastic-agent/8.11.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "elastic-agent",
|
||||
Version: "8.11.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/elastic-agent@8.11.2",
|
||||
Locations: locations("elastic-agent"),
|
||||
Metadata: metadata("elastic-agent-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "julia/1.13.0-alpha2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "julia",
|
||||
Version: "1.13.0-alpha2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/julia@1.13.0-alpha2",
|
||||
Locations: locations("libjulia-internal.so.1.13.0"),
|
||||
Metadata: metadata("julia-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "julia/1.12.6/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "julia",
|
||||
Version: "1.12.6",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/julia@1.12.6",
|
||||
Locations: locations("libjulia-internal.so.1.12.6"),
|
||||
Metadata: metadata("julia-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "julia/1.11.9/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "julia",
|
||||
Version: "1.11.9",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/julia@1.11.9",
|
||||
Locations: locations("libjulia-internal.so.1.11.9"),
|
||||
Metadata: metadata("julia-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "julia/1.10.11/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "julia",
|
||||
Version: "1.10.11",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/julia@1.10.11",
|
||||
Locations: locations("libjulia-internal.so.1.10.11"),
|
||||
Metadata: metadata("julia-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "julia/1.9.0-alpha1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "julia",
|
||||
Version: "1.9.0-alpha1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/julia@1.9.0-alpha1",
|
||||
Locations: locations("libjulia-internal.so.1.9"),
|
||||
Metadata: metadata("julia-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "julia/1.8.5/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "julia",
|
||||
Version: "1.8.5",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/julia@1.8.5",
|
||||
Locations: locations("libjulia-internal.so.1.8"),
|
||||
Metadata: metadata("julia-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
// note: dynamic (non-snippet) test case
|
||||
logicalFixture: "julia/1.5.4/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "julia",
|
||||
Version: "1.5.4",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/julia@1.5.4",
|
||||
Locations: locations("julia", "libjulia.so.1.5"),
|
||||
Metadata: pkg.BinarySignature{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
match("julia-binary", "julia"),
|
||||
match("julia-binary", "libjulia.so.1.5"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// note: dynamic (non-snippet) test case
|
||||
logicalFixture: "julia/1.3.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "julia",
|
||||
Version: "1.3.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/julia@1.3.1",
|
||||
Locations: locations("julia", "libjulia.so.1.3"),
|
||||
Metadata: pkg.BinarySignature{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
match("julia-binary", "julia"),
|
||||
match("julia-binary", "libjulia.so.1.3"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.22.10/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.22.10",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.22.10",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.24.5/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.24.5",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.24.5",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.30.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.30.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.30.2",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.31.2/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.31.2",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.31.2",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.34.12/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.34.12",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.34.12",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.35.4/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.35.4",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.35.4",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.23.7/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.23.7",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.23.7",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.24.9/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.24.9",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.24.9",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.30.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.30.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.30.1",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.30.6/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.30.6",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.30.6",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "envoy/1.36.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "envoy",
|
||||
Version: "1.36.1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/envoy@1.36.1",
|
||||
Locations: locations("envoy"),
|
||||
Metadata: metadata("envoy-binary"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -2248,9 +2920,16 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
packages, _, err := c.Catalog(context.Background(), resolver)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, packages, 1, "mismatched package count")
|
||||
expected := test.expectedPackages
|
||||
if len(expected) == 0 {
|
||||
expected = []pkg.Package{test.expected}
|
||||
}
|
||||
|
||||
assertPackagesAreEqual(t, test.expected, packages[0])
|
||||
require.Len(t, packages, len(expected), "mismatched package count")
|
||||
|
||||
for i := range expected {
|
||||
assertPackagesAreEqual(t, expected[i], packages[i])
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,18 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
// ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [x86_64-linux]
|
||||
`(?m)ruby (?P<version>[0-9]+\.[0-9]+\.[0-9]+((p|preview|rc|dev)[0-9]*)?) `)
|
||||
|
||||
// all Elastic Beats (filebeat, metricbeat, heartbeat, packetbeat, auditbeat)
|
||||
var elasticBeatsMatcher = binutils.MatchAny(
|
||||
// 9.x: forcestdinsetupTest 9.4.2%s %w (filebeat/metricbeat/auditbeat)
|
||||
// forcestdinsetupTest 9.4.2input (heartbeat/packetbeat)
|
||||
m.FileContentsVersionMatcher(`Test (?P<version>[0-9]+\.[0-9]+\.[0-9]+)[a-z%]`),
|
||||
// 9.x: exportconfigcreateplugin9.4.2-globalclient
|
||||
// 8.18.x: exportconfigcreateplugin8.18.4globalclient
|
||||
m.FileContentsVersionMatcher(`plugin(?:output)?(?P<version>[0-9]+\.[0-9]+\.[0-9]+)[-a-z]`),
|
||||
// 8.11.x: 5m.rate8.11.2-9765625
|
||||
m.FileContentsVersionMatcher(`5m\.rate(?P<version>[0-9]+\.[0-9]+\.[0-9]+)-`),
|
||||
)
|
||||
|
||||
classifiers := []binutils.Classifier{
|
||||
{
|
||||
Class: "python-binary",
|
||||
@@ -88,8 +100,43 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "julia-binary",
|
||||
FileGlob: "**/libjulia-internal.so",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)__init__\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00verify`),
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// �1.13.0-beta3[NUL]branch
|
||||
// [NUL]GIT_VERSION_INFO[NUL]jl_image_unpack[NUL]1.13.0-alpha2[NUL]branch
|
||||
// [NUL]GIT_VERSION_INFO[NUL]__init__[NUL]1.12.6[NUL]branch[NUL]commit
|
||||
// [NUL]GIT_VERSION_INFO[NUL]__init__[NUL]verify_methods[NUL]1.11.9[NUL]branch[NUL]commit
|
||||
// [NUL][NUL]__init__[NUL]1.10.11[NUL]verify_methods[NUL]
|
||||
m.FileContentsVersionMatcher(`\x00__init__\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00(branch|verify_methods)`),
|
||||
m.FileContentsVersionMatcher(`(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00branch\x00`),
|
||||
// [NUL]verify_methods[NUL]Task cannot be serialized[NUL]1.9.0-alpha1[NUL]BigInt[NUL]
|
||||
m.FileContentsVersionMatcher(`\x00verify_methods\x00.{0,30}(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00BigInt`),
|
||||
// unknown option `%s`[NUL]1.8.5[NUL]julia version %s
|
||||
m.FileContentsVersionMatcher(`\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00julia version`),
|
||||
),
|
||||
Package: "julia",
|
||||
PURL: mustPURL("pkg:generic/julia@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "julia-binary",
|
||||
FileGlob: "**/julia",
|
||||
EvidenceMatcher: binutils.SharedLibraryLookup(
|
||||
// libjulia.so.1
|
||||
// libjulia.so.0.6
|
||||
// libjulia.so
|
||||
`^libjulia\.so(\.[0-9])?(\.[0-9])?$`,
|
||||
binutils.MatchAny(
|
||||
// unknown option `%s`[NUL]1.5.4[NUL]julia version %s
|
||||
m.FileContentsVersionMatcher(`\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00julia version`),
|
||||
// [NUL]#kw#[NUL]1.3.1[NUL]BigInt
|
||||
// [NUL]#kw#[NUL]0.7.0-beta2[NUL]_require_dependencies
|
||||
m.FileContentsVersionMatcher(`\x00#kw#\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00(BigInt|_require_dependencies)`),
|
||||
// [NUL]ObjectIdDict[NUL]0.6.4[NUL]jl_sysimg_cpu_target
|
||||
m.FileContentsVersionMatcher(`\x00ObjectIdDict\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00jl_sysimg_cpu_target`),
|
||||
// [NUL]require[NUL]0.4.6[NUL]core2
|
||||
m.FileContentsVersionMatcher(`\x00require\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00core2`),
|
||||
),
|
||||
),
|
||||
Package: "julia",
|
||||
PURL: mustPURL("pkg:generic/julia@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
@@ -97,8 +144,17 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "helm",
|
||||
FileGlob: "**/helm",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`),
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// [NUL]v1.21.2[NUL].......[NUL][NUL]v4.1.4[NUL][NUL][NUL]
|
||||
// [NUL]v2.0.0-beta.2[NUL][NUL][NUL]
|
||||
m.FileContentsVersionMatcher(`\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]|-beta\.[0-9]|-rc\.[0-9])?)\x00{2,}`),
|
||||
// [NUK]'[DLE]v3.12.0[NUL][NUL]...go1.20.3[NUL][NUL]
|
||||
m.FileContentsVersionMatcher(`v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]|-beta\.[0-9]|-rc\.[0-9])?)\x00+.{1,500}go[0-9]+\.[0-9]+\.[0-9]+\x00+`),
|
||||
// [NUL]v3.11.1[NUL]�[NUL]
|
||||
m.FileContentsVersionMatcher(`\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]|-beta\.[0-9]|-rc\.[0-9])?)\x00`),
|
||||
// [NUL]@�@v3.15.2[NUL][NUL]
|
||||
m.FileContentsVersionMatcher(`@v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha\.[0-9]|-beta\.[0-9]|-rc\.[0-9])?)\x00`),
|
||||
),
|
||||
Package: "helm",
|
||||
PURL: mustPURL("pkg:golang/helm.sh/helm@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:helm:helm:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
@@ -106,13 +162,19 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "redis-binary",
|
||||
FileGlob: "**/redis-server",
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// matches most recent versions of redis (~v7), e.g. "7.0.14buildkitsandbox-1702957741000000000"
|
||||
m.FileContentsVersionMatcher(`[^\d](?P<version>\d+.\d+\.\d+)buildkitsandbox-\d+`),
|
||||
// matches against older versions of redis (~v3 - v6), e.g. "4.0.11841ce7054bd9-1542359302000000000"
|
||||
m.FileContentsVersionMatcher(`[^\d](?P<version>[0-9]+\.[0-9]+\.[0-9]+)\w{12}-\d+`),
|
||||
// matches against older versions of redis (~v2), e.g. "Server started, Redis version 2.8.23"
|
||||
m.FileContentsVersionMatcher(`Redis version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
// Negative Matchers to exclude valkey-server
|
||||
binutils.MatchNone(
|
||||
binutils.MatchPath("**/valkey-server"),
|
||||
),
|
||||
binutils.MatchAny(
|
||||
// matches most recent versions of redis (~v7), e.g. "7.0.14buildkitsandbox-1702957741000000000"
|
||||
m.FileContentsVersionMatcher(`[^\d](?P<version>\d+.\d+\.\d+)buildkitsandbox-\d+`),
|
||||
// matches against older versions of redis (~v3 - v6), e.g. "4.0.11841ce7054bd9-1542359302000000000"
|
||||
m.FileContentsVersionMatcher(`[^\d](?P<version>[0-9]+\.[0-9]+\.[0-9]+)\w{12}-\d+`),
|
||||
// matches against older versions of redis (~v2), e.g. "Server started, Redis version 2.8.23"
|
||||
m.FileContentsVersionMatcher(`Redis version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
),
|
||||
),
|
||||
Package: "redis",
|
||||
PURL: mustPURL("pkg:generic/redis@version"),
|
||||
@@ -288,6 +350,96 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
cpe.Must("cpe:2.3:a:percona:xtradb_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
// Legacy MySQL Cluster contains both MySQL Server and MySQL Cluster versions (Example: 5.7.33-ndb-7.5.21)
|
||||
// This classifier identifies the MySQL Server version of the mysqld binary (5.7.33 in the example above).
|
||||
Class: "mysqld-mysql-cluster-legacy-binary",
|
||||
FileGlob: "**/mysqld",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`cluster-gpl\x00(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?)\-ndb\-[0-9]+(\.[0-9]+)?(\.[0-9]+)?`),
|
||||
Package: "mysql-server",
|
||||
PURL: mustPURL("pkg:generic/mysql-server@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
Class: "mysqld-binary",
|
||||
FileGlob: "**/mysqld",
|
||||
EvidenceMatcher: binutils.BranchingEvidenceMatcher([]binutils.Classifier{
|
||||
{
|
||||
// Legacy MySQL Cluster contains both MySQL Server and MySQL Cluster versions (Example: 5.7.33-ndb-7.5.21)
|
||||
// This classifier identifies the MySQL Cluster version of the mysqld binary (7.5.21 in the example above).
|
||||
Class: "mysqld-mysql-cluster-legacy-binary",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`cluster-gpl\x00[0-9]+(\.[0-9]+)?(\.[0-9]+)?\-ndb\-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?)`),
|
||||
Package: "mysql-cluster",
|
||||
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
// mysqld from MySQL Cluster after versioning was aligned with MySQL Server
|
||||
Class: "mysqld-mysql-cluster-binary",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`/mysql-cluster-gpl-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
|
||||
Package: "mysql-cluster",
|
||||
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
// mysqld from MySQL Server
|
||||
Class: "mysqld-mysql-server-binary",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`/mysql-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
|
||||
Package: "mysql-server",
|
||||
PURL: mustPURL("pkg:generic/mysql-server@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_server:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
}...),
|
||||
},
|
||||
{
|
||||
Class: "ndbd-binary",
|
||||
FileGlob: "**/ndbd",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`/mysql-cluster-gpl-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
|
||||
Package: "mysql-cluster",
|
||||
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
Class: "ndbmtd-binary",
|
||||
FileGlob: "**/ndbmtd",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`/mysql-cluster-gpl-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
|
||||
Package: "mysql-cluster",
|
||||
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
Class: "ndb_mgmd-binary",
|
||||
FileGlob: "**/ndb_mgmd",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`/mysql-cluster-gpl-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)/`),
|
||||
Package: "mysql-cluster",
|
||||
PURL: mustPURL("pkg:generic/mysql-cluster@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:oracle:mysql_cluster:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
Class: "xtrabackup-binary",
|
||||
FileGlob: "**/xtrabackup",
|
||||
@@ -300,9 +452,17 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "mariadb-binary",
|
||||
FileGlob: "**/{mariadb,mysql}",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// 10.6.15-MariaDB
|
||||
`(?m)(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-MariaDB`),
|
||||
m.FileContentsVersionMatcher(`(?m)(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-MariaDB`),
|
||||
// MariaDB.org / RHEL tarball builds embed the release directory name, which does not contain the
|
||||
// "-MariaDB" marker. The version is in the build path instead, e.g.:
|
||||
// mariadb-11.8.5-2-redhat-x86_64/rhel-8/bin/mariadb
|
||||
// mariadb-11.8.5-linux-systemd-x86_64
|
||||
// Without this the older matcher misses the version and a later release suffix (e.g. "2") can be
|
||||
// picked up instead, producing false-positive matches against ancient CVEs (see anchore/grype#3452).
|
||||
m.FileContentsVersionMatcher(`(?m)(?:^|/)mariadb-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-`),
|
||||
),
|
||||
Package: "mariadb",
|
||||
PURL: mustPURL("pkg:generic/mariadb@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:mariadb:mariadb:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
@@ -427,10 +587,26 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "deno-binary",
|
||||
FileGlob: "**/deno",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
// Deno/2.6.3
|
||||
// Deno/1.41.0
|
||||
`Deno/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
// Deno/2.6.3
|
||||
// Deno/1.41.0
|
||||
`Deno/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
|
||||
),
|
||||
m.FileContentsVersionMatcher(
|
||||
// deno::tools::standalonedeno-65db94feba9d4d51a09b74629f566dbc90484fbarelease/v1.29.4windows
|
||||
// cli/tools/standalone.rsdeno-74064c9d8c222b33b2a552ea0af1054f57002a96release/v1.28.3windows
|
||||
`deno-[0-9a-z]{40}release/v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
|
||||
),
|
||||
m.FileContentsVersionMatcher(
|
||||
// cli/tools/standalone.rsdeno-ab286750a8c87215a9651efb11fcc620f29140051.16.4release/vdlwindows
|
||||
`deno-[0-9a-z]{40}(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
|
||||
),
|
||||
m.FileContentsVersionMatcher(
|
||||
// 1.10.31567c1013cc8ff12cf039137792da66a1d0015b5DENO_UNSTABLE_COVERAGE_DIRNo current directorycli/main
|
||||
`(?P<version>[0-9]+\.[0-9]+\.[0-9]+)[0-9a-z]{40}DENO`,
|
||||
),
|
||||
),
|
||||
Package: "deno",
|
||||
PURL: mustPURL("pkg:generic/deno@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:deno:deno:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
@@ -558,14 +734,29 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "openssl-binary",
|
||||
FileGlob: "**/openssl",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
// [NUL]OpenSSL 3.1.4'
|
||||
// [NUL]OpenSSL 1.1.1w'
|
||||
`\x00OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+([a-z]+|-alpha[0-9]|-beta[0-9]|-rc[0-9])?)`,
|
||||
),
|
||||
Package: "openssl",
|
||||
PURL: mustPURL("pkg:generic/openssl@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
EvidenceMatcher: binutils.BranchingEvidenceMatcher([]binutils.Classifier{
|
||||
{
|
||||
Class: "openssl-binary-aws-lc",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
// [NUL]OpenSSL 1.1.1 (compatible; AWS-LC 1.69.0)[NUL]
|
||||
`AWS-LC (?P<version>[0-9]+\.[0-9]+\.[0-9]+)\)\x00`,
|
||||
),
|
||||
Package: "aws-lc",
|
||||
PURL: mustPURL("pkg:generic/aws-lc@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:amazon:aws_libcrypto:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "openssl-binary",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
// [NUL]OpenSSL 3.1.4'
|
||||
// [NUL]OpenSSL 1.1.1w'
|
||||
`\x00OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+([a-z]+|-alpha[0-9]|-beta[0-9]|-rc[0-9])?)`,
|
||||
),
|
||||
Package: "openssl",
|
||||
PURL: mustPURL("pkg:generic/openssl@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
}...),
|
||||
},
|
||||
{
|
||||
Class: "openldap-search-binary",
|
||||
@@ -762,7 +953,9 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
Class: "elixir-binary",
|
||||
FileGlob: "**/elixir",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)ELIXIR_VERSION=(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
// Capture optional pre-release suffix (-rc.1, -alpha.0, -beta.2,
|
||||
// etc.) so release-candidate elixir images (#4819) match.
|
||||
`(?m)ELIXIR_VERSION=(?P<version>[0-9]+\.[0-9]+\.[0-9]+(?:-[a-z0-9]+(?:\.[0-9]+)?)?)`),
|
||||
Package: "elixir",
|
||||
PURL: mustPURL("pkg:generic/elixir@version"),
|
||||
CPEs: []cpe.CPE{
|
||||
@@ -773,7 +966,8 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
Class: "elixir-library",
|
||||
FileGlob: "**/elixir/ebin/elixir.app",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)\{vsn,"(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-[a-z0-9]+)?)"\}`),
|
||||
// Same pre-release extension as elixir-binary above.
|
||||
`(?m)\{vsn,"(?P<version>[0-9]+\.[0-9]+\.[0-9]+(?:-[a-z0-9]+(?:\.[0-9]+)?)?)"\}`),
|
||||
Package: "elixir",
|
||||
PURL: mustPURL("pkg:generic/elixir@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:elixir-lang:elixir:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
@@ -882,6 +1076,20 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
m.FileContentsVersionMatcher(`(?s)\[source/.{0,200}\x00(?P<version>1\.1[0-9]\.[0-9]+(-dev)?)\x00`),
|
||||
// 1.x [NUL]1.6.0[NUL]RELEASE
|
||||
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.[0-9]\.[0-9]+(-dev)?)\x00.{0,20}RELEASE`),
|
||||
// contrib 1.3x [NUL]1.34.12[NUL]envoy_quiche... or [NUL]1.30.2[NUL]envoy/extensions... or [NUL]1.30.8[NUL]envoy://
|
||||
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.3[0-9]\.[0-9]+(-dev)?)\x00envoy[/._:]`),
|
||||
// contrib 1.3x [NUL]1.30.7[NUL]...envoy.service or [NUL]1.30.6[NUL]...envoy.data
|
||||
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.3[0-9]\.[0-9]+(-dev)?)\x00.{0,200}envoy[./]`),
|
||||
// contrib1.3x [NUL]1.31.2[NUL]...envoy_quic or [NUL]1.36.x[NUL]...envoy_quic
|
||||
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.3[0-9]\.[0-9]+(-dev)?)\x00.{0,1000}envoy_quic`),
|
||||
// contrib 1.3x [NUL]1.30.1[NUL]TLS client
|
||||
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.3[0-9]\.[0-9]+(-dev)?)\x00TLS `),
|
||||
// contrib 1.2x [NUL]1.24.9[NUL]envoy/extensions...
|
||||
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.2[0-9]\.[0-9]+(-dev)?)\x00envoy[/._]`),
|
||||
// contrib 1.2x [NUL]1.22.10[NUL]...envoy. or [NUL]1.24.5[NUL]...envoy.
|
||||
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.2[0-9]\.[0-9]+(-dev)?)\x00.{0,500}envoy\.`),
|
||||
// contrib 1.2x [NUL]1.23.7[NUL]...envoy_internal
|
||||
m.FileContentsVersionMatcher(`(?s)\x00(?P<version>1\.2[0-9]\.[0-9]+(-dev)?)\x00.{0,300}envoy_`),
|
||||
),
|
||||
Package: "envoy",
|
||||
PURL: mustPURL("pkg:generic/envoy@version"),
|
||||
@@ -905,6 +1113,84 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
PURL: mustPURL("pkg:generic/mongodb@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:mongodb:mongodb:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "ingress-nginx-binary",
|
||||
FileGlob: "**/nginx-ingress-controller",
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// [NUL][NUL]v1.15.1[NUL][NUL]@e[ETX][NUL][NUL][NUL][NUL]go1.26.1[NUL][NUL][NUL]
|
||||
// �v1.15.1[NUL][NUL]�z[ETX][NUL][NUL][NUL][NUL]go1.24.4[NUL][NUL][NUL]
|
||||
m.FileContentsVersionMatcher(`v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00+.{0,50}go[0-9]+\.[0-9]+(\-(alpha|beta)\.[0-9])?\.[0-9]+\x00+`),
|
||||
// �Lv1.9.6[NUL][NUL]$a�c[SOH][NUL][NUL][NUL]
|
||||
// [NUL][NUL]v0.34.0[NUL]......�$a�...[NUL]
|
||||
m.FileContentsVersionMatcher(`v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\-(alpha|beta)\.[0-9])?)\x00+.{0,800}\$a.{0,10}\x00+`),
|
||||
// [NUL][NUL]v1.7.1[NUL][NUL][NUL]...S=v<y5...
|
||||
// [NUL]0.33.0[NUL][NUL]...[NUL][NUL]...S=v<y5
|
||||
m.FileContentsVersionMatcher(`\x00+v?(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\-(alpha|beta)\.[0-9])?)\x00+.{0,100}S=v<y5`),
|
||||
// [NUL][NUL]go1.22.8[NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL][NUL]v1.12.0-beta.0[NUL][NUL]
|
||||
m.FileContentsVersionMatcher(`\x00+go[0-9]+\.[0-9]+\.[0-9]+\x00+v(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\-(alpha|beta)\.[0-9])?)\x00+`),
|
||||
// [NUL][NUL]v1.2.0-beta.1[NUL][NUL]
|
||||
// [NUL][NUL]v1.0.0-alpha.2[NUL][NUL]
|
||||
m.FileContentsVersionMatcher(`\x00+v(?P<version>[0-9]+\.[0-9]+\.[0-9]+\-(alpha|beta)\.[0-9])\x00+`),
|
||||
),
|
||||
Package: "nginx-ingress-controller",
|
||||
PURL: mustPURL("pkg:generic/nginx-ingress-controller@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:kubernetes:ingress-nginx:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "filebeat-binary",
|
||||
FileGlob: "**/filebeat",
|
||||
EvidenceMatcher: elasticBeatsMatcher,
|
||||
Package: "filebeat",
|
||||
PURL: mustPURL("pkg:generic/filebeat@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:elastic:filebeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "metricbeat-binary",
|
||||
FileGlob: "**/metricbeat",
|
||||
EvidenceMatcher: elasticBeatsMatcher,
|
||||
Package: "metricbeat",
|
||||
PURL: mustPURL("pkg:generic/metricbeat@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:elastic:metricbeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "heartbeat-binary",
|
||||
FileGlob: "**/heartbeat",
|
||||
EvidenceMatcher: elasticBeatsMatcher,
|
||||
Package: "heartbeat",
|
||||
PURL: mustPURL("pkg:generic/heartbeat@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:elastic:heartbeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "packetbeat-binary",
|
||||
FileGlob: "**/packetbeat",
|
||||
EvidenceMatcher: elasticBeatsMatcher,
|
||||
Package: "packetbeat",
|
||||
PURL: mustPURL("pkg:generic/packetbeat@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:elastic:packetbeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "auditbeat-binary",
|
||||
FileGlob: "**/auditbeat",
|
||||
EvidenceMatcher: elasticBeatsMatcher,
|
||||
Package: "auditbeat",
|
||||
PURL: mustPURL("pkg:generic/auditbeat@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:elastic:auditbeat:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "elastic-agent-binary",
|
||||
FileGlob: "**/elastic-agent",
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// 9.4.x: config/statsenroll: true9.4.2-headeruint16secret
|
||||
// 9.0.x: configenroll9.0.0-headeruint16secret
|
||||
// 8.19.x: config/statsenroll8.19.4headeruint16secret
|
||||
m.FileContentsVersionMatcher(`enroll(?:: true)?(?P<version>[0-9]+\.[0-9]+\.[0-9]+)-?header`),
|
||||
// 8.11.x: 3:04PM8.11.2:https
|
||||
m.FileContentsVersionMatcher(`PM(?P<version>[0-9]+\.[0-9]+\.[0-9]+):https`),
|
||||
),
|
||||
Package: "elastic-agent",
|
||||
PURL: mustPURL("pkg:generic/elastic-agent@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:elastic:elastic_agent:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
}
|
||||
|
||||
return append(classifiers, defaultJavaClassifiers()...)
|
||||
|
||||
@@ -275,9 +275,15 @@ func copyBinaryFromContainer(containerName, containerPath, destinationPath, dige
|
||||
return err
|
||||
}
|
||||
|
||||
// ensure permissions are 600 for destination
|
||||
if err := os.Chmod(destinationPath, 0600); err != nil {
|
||||
return fmt.Errorf("unable to set permissions on file %q: %w", destinationPath, err)
|
||||
// ensure permissions are 600 for destination (if it is not a symlink)
|
||||
info, err := os.Lstat(destinationPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to stat file %q: %w", destinationPath, err)
|
||||
}
|
||||
if info.Mode()&os.ModeSymlink == 0 {
|
||||
if err := os.Chmod(destinationPath, 0600); err != nil {
|
||||
return fmt.Errorf("unable to set permissions on file %q: %w", destinationPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
// capture digest file
|
||||
|
||||
@@ -12,10 +12,11 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pe"
|
||||
)
|
||||
|
||||
// NewPEPackageCataloger returns a cataloger that interprets packages from DLL and EXE files.
|
||||
// NewPEPackageCataloger returns a cataloger that interprets packages from DLL, EXE, and BPL files.
|
||||
// BPL (Borland Package Library) files are PE-format binaries used by Delphi and C++Builder.
|
||||
func NewPEPackageCataloger() pkg.Cataloger {
|
||||
return generic.NewCataloger("pe-binary-package-cataloger").
|
||||
WithParserByGlobs(parsePE, "**/*.dll", "**/*.exe")
|
||||
WithParserByGlobs(parsePE, "**/*.dll", "**/*.exe", "**/*.bpl")
|
||||
}
|
||||
|
||||
func parsePE(_ context.Context, _ file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
|
||||
@@ -62,3 +62,30 @@ func Test_PEPackageCataloger(t *testing.T) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func Test_PEPackageCataloger_Globs(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
expected []string
|
||||
}{
|
||||
{
|
||||
name: "obtain PE binary files (dll, exe, bpl)",
|
||||
fixture: "testdata/glob-paths",
|
||||
expected: []string{
|
||||
"src/library.dll",
|
||||
"src/program.exe",
|
||||
"src/archive.bpl",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, test.fixture).
|
||||
ExpectsResolverContentQueries(test.expected).
|
||||
TestCataloger(t, NewPEPackageCataloger())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user