mirror of
https://github.com/anchore/syft.git
synced 2026-10-17 00:27:21 +02:00
Compare commits
166
Commits
+8
-8
@@ -18,7 +18,7 @@ tools:
|
||||
# used to sign mac binaries at release
|
||||
- name: quill
|
||||
version:
|
||||
want: v0.5.0
|
||||
want: v0.5.1
|
||||
method: github-release
|
||||
with:
|
||||
repo: anchore/quill
|
||||
@@ -26,7 +26,7 @@ tools:
|
||||
# used for linting
|
||||
- name: golangci-lint
|
||||
version:
|
||||
want: v1.62.0
|
||||
want: v1.64.5
|
||||
method: github-release
|
||||
with:
|
||||
repo: golangci/golangci-lint
|
||||
@@ -42,7 +42,7 @@ tools:
|
||||
# used for signing the checksums file at release
|
||||
- name: cosign
|
||||
version:
|
||||
want: v2.4.1
|
||||
want: v2.4.3
|
||||
method: github-release
|
||||
with:
|
||||
repo: sigstore/cosign
|
||||
@@ -58,7 +58,7 @@ tools:
|
||||
# used to release all artifacts
|
||||
- name: goreleaser
|
||||
version:
|
||||
want: v2.4.5
|
||||
want: v2.7.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: goreleaser/goreleaser
|
||||
@@ -103,7 +103,7 @@ tools:
|
||||
# used for running all local and CI tasks
|
||||
- name: task
|
||||
version:
|
||||
want: v3.40.0
|
||||
want: v3.41.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: go-task/task
|
||||
@@ -111,7 +111,7 @@ tools:
|
||||
# used for triggering a release
|
||||
- name: gh
|
||||
version:
|
||||
want: v2.62.0
|
||||
want: v2.67.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: cli/cli
|
||||
@@ -119,7 +119,7 @@ tools:
|
||||
# used to upload test fixture cache
|
||||
- name: oras
|
||||
version:
|
||||
want: v1.2.0
|
||||
want: v1.2.2
|
||||
method: github-release
|
||||
with:
|
||||
repo: oras-project/oras
|
||||
@@ -127,7 +127,7 @@ tools:
|
||||
# used to upload test fixture cache
|
||||
- name: yq
|
||||
version:
|
||||
want: v4.44.3
|
||||
want: v4.45.1
|
||||
method: github-release
|
||||
with:
|
||||
repo: mikefarah/yq
|
||||
|
||||
@@ -5,7 +5,7 @@ inputs:
|
||||
go-version:
|
||||
description: "Go version to install"
|
||||
required: true
|
||||
default: "1.22.x"
|
||||
default: "1.24.x"
|
||||
go-dependencies:
|
||||
description: "Download go dependencies"
|
||||
required: true
|
||||
@@ -18,6 +18,9 @@ inputs:
|
||||
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"
|
||||
@@ -26,20 +29,22 @@ runs:
|
||||
using: "composite"
|
||||
steps:
|
||||
# note: go mod and build is automatically cached on default with v4+
|
||||
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
|
||||
- uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
|
||||
if: inputs.go-version != ''
|
||||
with:
|
||||
go-version: ${{ inputs.go-version }}
|
||||
|
||||
- name: Restore tool cache
|
||||
if: inputs.tools == 'true'
|
||||
id: tool-cache
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tool
|
||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
|
||||
|
||||
- name: Install project tools
|
||||
shell: bash
|
||||
if: inputs.tools == 'true'
|
||||
run: make tools
|
||||
|
||||
- name: Install go dependencies
|
||||
@@ -55,7 +60,7 @@ runs:
|
||||
|
||||
- name: Restore ORAS cache from github actions
|
||||
if: inputs.download-test-fixture-cache == 'true'
|
||||
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f # v4.2.1
|
||||
with:
|
||||
path: ${{ github.workspace }}/.tmp/oras-cache
|
||||
key: ${{ inputs.cache-key-prefix }}-oras-cache
|
||||
|
||||
@@ -19,7 +19,7 @@ updates:
|
||||
- "dependencies"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/.github/actions/boostrap"
|
||||
directory: "/.github/actions/bootstrap"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
@@ -5,7 +5,7 @@ or link to an issue where this is explained.
|
||||
|
||||
<!-- If this completes an issue, please include: -->
|
||||
|
||||
- Fixes #
|
||||
- Fixes
|
||||
|
||||
## Type of change
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
|
||||
Benchmark-Test:
|
||||
name: "Benchmark tests"
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
# note: we want benchmarks to run on pull_request events in order to publish results to a sticky comment, and
|
||||
# we also want to run on push such that merges to main are recorded to the cache. For this reason we don't filter
|
||||
# the job by event.
|
||||
@@ -23,7 +23,7 @@ jobs:
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Restore base benchmark result
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
uses: actions/cache@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
|
||||
with:
|
||||
path: test/results/benchmark-main.txt
|
||||
# use base sha for PR or new commit hash for main push in benchmark result key
|
||||
@@ -39,13 +39,13 @@ jobs:
|
||||
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
|
||||
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
- uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # v4.6.1
|
||||
with:
|
||||
name: benchmark-test-results
|
||||
path: test/results/**/*
|
||||
|
||||
- name: Update PR benchmark results comment
|
||||
uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 #v2.9.0
|
||||
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 #v2.9.1
|
||||
continue-on-error: true
|
||||
with:
|
||||
header: benchmark
|
||||
|
||||
@@ -39,13 +39,13 @@ jobs:
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed #v5.1.0
|
||||
uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 #v5.3.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f #v3.27.4
|
||||
uses: github/codeql-action/init@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d #v3.28.10
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f #v3.27.4
|
||||
uses: github/codeql-action/autobuild@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d #v3.28.10
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -70,4 +70,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f #v3.27.4
|
||||
uses: github/codeql-action/analyze@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d #v3.28.10
|
||||
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
|
||||
- name: Delete existing comment
|
||||
if: ${{ hashFiles( env.CI_COMMENT_FILE ) == '' }}
|
||||
uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 #v2.9.0
|
||||
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 #v2.9.1
|
||||
with:
|
||||
header: ${{ env.COMMENT_HEADER }}
|
||||
hide: true
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
|
||||
- name: Add comment
|
||||
if: ${{ hashFiles( env.CI_COMMENT_FILE ) != '' }}
|
||||
uses: marocchino/sticky-pull-request-comment@331f8f5b4215f0445d3c07b4967662a32a2d3e31 #v2.9.0
|
||||
uses: marocchino/sticky-pull-request-comment@52423e01640425a022ef5fd42c6fb5f633a02728 #v2.9.1
|
||||
with:
|
||||
header: ${{ env.COMMENT_HEADER }}
|
||||
path: ${{ env.CI_COMMENT_FILE }}
|
||||
|
||||
@@ -18,7 +18,7 @@ on:
|
||||
jobs:
|
||||
|
||||
release:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ on:
|
||||
jobs:
|
||||
quality-gate:
|
||||
environment: release
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
@@ -98,7 +98,7 @@ jobs:
|
||||
|
||||
release:
|
||||
needs: [quality-gate]
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
@@ -148,7 +148,7 @@ jobs:
|
||||
# for updating brew formula in anchore/homebrew-syft
|
||||
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
|
||||
|
||||
- uses: anchore/sbom-action@fc46e51fd3cb168ffb36c6d1915723c47db58abb #v0.17.7
|
||||
- uses: anchore/sbom-action@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 #v0.18.0
|
||||
continue-on-error: true
|
||||
with:
|
||||
file: go.mod
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
name: PR to update Anchore dependencies
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
repos:
|
||||
description: "List of dependencies to update"
|
||||
required: true
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
update:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'anchore' # only run for main repo (not forks)
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
with:
|
||||
tools: false
|
||||
bootstrap-apt-packages: ""
|
||||
|
||||
- name: Update dependencies
|
||||
id: update
|
||||
uses: anchore/workflows/.github/actions/update-go-dependencies@main
|
||||
with:
|
||||
repos: ${{ github.event.inputs.repos }}
|
||||
|
||||
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
|
||||
id: generate-token
|
||||
with:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@dd2324fc52d5d43c699a5636bcf19fceaa70c284 #v7.0.7
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
draft: ${{ steps.update.outputs.draft }}
|
||||
# do not change this branch, as other workflows depend on it
|
||||
branch: auto/integration
|
||||
labels: dependencies,pre-release
|
||||
commit-message: "chore(deps): update anchore dependencies"
|
||||
title: "chore(deps): update anchore dependencies"
|
||||
body: ${{ steps.update.outputs.summary }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
@@ -49,7 +49,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f #v7.0.5
|
||||
- uses: peter-evans/create-pull-request@dd2324fc52d5d43c699a5636bcf19fceaa70c284 #v7.0.7
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f #v7.0.5
|
||||
- uses: peter-evans/create-pull-request@dd2324fc52d5d43c699a5636bcf19fceaa70c284 #v7.0.7
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
name: PR for latest Stereoscope release
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 8 * * *" # 3 AM EST
|
||||
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
GO_VERSION: "1.21.x"
|
||||
GO_STABLE_VERSION: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
upgrade-stereoscope:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'anchore/syft' # only run for main repo
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed #v5.1.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- run: |
|
||||
LATEST_VERSION=$(git ls-remote https://github.com/anchore/stereoscope main | head -n1 | awk '{print $1;}')
|
||||
|
||||
# update go.mod
|
||||
go get github.com/anchore/stereoscope@$LATEST_VERSION
|
||||
go mod tidy
|
||||
|
||||
# export the version for use with create-pull-request
|
||||
echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||
id: latest-version
|
||||
|
||||
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
|
||||
id: generate-token
|
||||
with:
|
||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||
|
||||
- uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f #v7.0.5
|
||||
with:
|
||||
signoff: true
|
||||
delete-branch: true
|
||||
branch: auto/latest-stereoscope
|
||||
labels: dependencies
|
||||
commit-message: "chore(deps): update stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}"
|
||||
title: "chore(deps): update stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}"
|
||||
body: |
|
||||
Update Stereoscope to ${{ steps.latest-version.outputs.LATEST_VERSION }}
|
||||
token: ${{ steps.generate-token.outputs.token }}
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
Static-Analysis:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Static analysis"
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
@@ -29,7 +29,7 @@ jobs:
|
||||
Unit-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Unit tests"
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
@@ -45,7 +45,7 @@ jobs:
|
||||
Integration-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Integration tests"
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
Build-Snapshot-Artifacts:
|
||||
name: "Build snapshot artifacts"
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
||||
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
||||
- name: Upload snapshot artifacts
|
||||
uses: actions/cache/save@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
uses: actions/cache/save@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
|
||||
with:
|
||||
# we need to preserve the snapshot data itself as well as the task data that confirms if the
|
||||
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
|
||||
@@ -93,7 +93,7 @@ jobs:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "Acceptance tests (Linux)"
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -139,7 +139,7 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@v3.7.0
|
||||
uses: sigstore/cosign-installer@v3.8.1
|
||||
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -176,7 +176,7 @@ jobs:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
name: "CLI tests (Linux)"
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
@@ -187,7 +187,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
uses: actions/cache/restore@0c907a75c2c80ebcb7f088228285e798b750cf8f #v4.2.1
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -207,7 +207,7 @@ jobs:
|
||||
Cleanup-Cache:
|
||||
name: "Cleanup snapshot cache"
|
||||
if: github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
permissions:
|
||||
actions: write
|
||||
needs:
|
||||
|
||||
+1
-2
@@ -1,5 +1,6 @@
|
||||
issues:
|
||||
max-same-issues: 25
|
||||
uniq-by-line: false
|
||||
|
||||
# TODO: enable this when we have coverage on docstring comments
|
||||
# # The list of ids of default excludes to include or disable.
|
||||
@@ -60,8 +61,6 @@ linters-settings:
|
||||
gosec:
|
||||
excludes:
|
||||
- G115
|
||||
output:
|
||||
uniq-by-line: false
|
||||
run:
|
||||
timeout: 10m
|
||||
tests: false
|
||||
|
||||
+5
-1
@@ -8,4 +8,8 @@ A cataloger contributed by Oracle Corporation that extracts packages given withi
|
||||
|
||||
## Swift Package Manager
|
||||
|
||||
A cataloger contributed by Axis Communications that catalogs packages resolved by Swift Package Manager.
|
||||
A cataloger contributed by Axis Communications that catalogs packages resolved by Swift Package Manager.
|
||||
|
||||
## Bitnami Packages
|
||||
|
||||
A cataloger contributed by Bitnami developer that catalogs packages described in Bitnami SBOMs.
|
||||
|
||||
+3
-3
@@ -367,11 +367,11 @@ package under test and should always be updated by invoking `go test` on the spe
|
||||
update flag provided.
|
||||
|
||||
Many of the `Format` tests make use of this approach, where the raw SBOM report is saved in the repo and the test
|
||||
compares that SBOM with what is generated from the latest presenter code. For instance, at the time of this writing
|
||||
the CycloneDX presenter snapshots can be updated by running:
|
||||
compares that SBOM with what is generated from the latest presenter code. The following command can be used to
|
||||
update the golden files for the various snapshot tests:
|
||||
|
||||
```bash
|
||||
go test ./internal/formats -update-cyclonedx
|
||||
make update-format-golden-files
|
||||
```
|
||||
|
||||
These flags are defined at the top of the test files that have tests that use the snapshot files.
|
||||
|
||||
@@ -67,7 +67,7 @@ choco install syft -y
|
||||
|
||||
### Nix
|
||||
|
||||
**Note**: Nix packaging of Syft is [community maintained](https://github.com/NixOS/nixpkgs/blob/master/pkgs/tools/admin/syft/default.nix). Syft is available in the [stable channel](https://wiki.nixos.org/wiki/Nix_channels#The_official_channels) since NixOS `22.05`.
|
||||
**Note**: Nix packaging of Syft is [community maintained](https://github.com/NixOS/nixpkgs/blob/master/pkgs/by-name/sy/syft/package.nix). Syft is available in the [stable channel](https://wiki.nixos.org/wiki/Nix_channels#The_official_channels) since NixOS `22.05`.
|
||||
|
||||
```bash
|
||||
nix-env -i syft
|
||||
@@ -123,6 +123,7 @@ Note that flags using the @<version> can be used for earlier versions of each sp
|
||||
### Supported Ecosystems
|
||||
|
||||
- Alpine (apk)
|
||||
- Bitnami packages
|
||||
- C (conan)
|
||||
- C++ (conan)
|
||||
- Dart (pubs)
|
||||
@@ -146,6 +147,7 @@ Note that flags using the @<version> can be used for earlier versions of each sp
|
||||
- Rust (cargo.lock)
|
||||
- Swift (cocoapods, swift-package-manager)
|
||||
- Wordpress plugins
|
||||
- Terraform providers (.terraform.lock.hcl)
|
||||
|
||||
## Documentation
|
||||
|
||||
|
||||
+12
-5
@@ -27,7 +27,7 @@ vars:
|
||||
sh: uname -s | tr '[:upper:]' '[:lower:]'
|
||||
ARCH:
|
||||
sh: |
|
||||
[ "$(uname -m)" = "x86_64" ] && echo "amd64_v1" || echo $(uname -m)
|
||||
[ "$(uname -m)" = "x86_64" ] && echo "amd64_v1" || { [ "$(uname -m)" = "aarch64" ] && echo "arm64_v8.0" || echo $(uname -m); }
|
||||
PROJECT_ROOT:
|
||||
sh: echo $PWD
|
||||
# note: the snapshot dir must be a relative path starting with ./
|
||||
@@ -191,6 +191,14 @@ tasks:
|
||||
|
||||
|
||||
## Testing tasks #################################
|
||||
update-format-golden-files:
|
||||
desc: "Update golden (i.e. snapshot) files used by unit tests"
|
||||
cmds:
|
||||
- go test ./syft/format/spdxjson -update-spdx-json
|
||||
- go test ./syft/format/spdxtagvalue -update-spdx-tv
|
||||
- go test ./syft/format/cyclonedxxml -update-cyclonedx-xml
|
||||
- go test ./syft/format/cyclonedxjson -update-cyclonedx-json
|
||||
- go test ./syft/format/syftjson -update-json
|
||||
|
||||
unit:
|
||||
desc: Run unit tests
|
||||
@@ -229,8 +237,7 @@ tasks:
|
||||
cmds:
|
||||
- cmd: "echo 'testing binary: {{ .SNAPSHOT_BIN }}'"
|
||||
silent: true
|
||||
|
||||
- cmd: "test -f {{ .SNAPSHOT_BIN }} || (find {{ .SNAPSHOT_DIR }} && echo '\nno snapshot found' && false)"
|
||||
- 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"
|
||||
@@ -444,7 +451,7 @@ tasks:
|
||||
total_size=$(find . | grep cache | grep tar | xargs du -c | grep total | awk '{print $1}')
|
||||
find . | grep cache | grep tar | xargs du
|
||||
echo "total $total_size KB"
|
||||
|
||||
|
||||
if [ "$total_size" -gt 1048576 ]; then
|
||||
echo 'docker cache is larger than 1GB'
|
||||
exit 1
|
||||
@@ -574,7 +581,7 @@ tasks:
|
||||
- "{{ .NEXT_VERSION }}"
|
||||
cmds:
|
||||
- "{{ .TOOL_DIR }}/chronicle -vv -n --version-file {{ .NEXT_VERSION }} > {{ .CHANGELOG }}"
|
||||
- "{{ .TOOL_DIR }}/glow {{ .CHANGELOG }}"
|
||||
- "{{ .TOOL_DIR }}/glow -w 0 {{ .CHANGELOG }}"
|
||||
|
||||
|
||||
## Release targets #################################
|
||||
|
||||
@@ -61,7 +61,7 @@ type backgroundLineReader struct {
|
||||
func (m *Handler) handleAttestationStarted(e partybus.Event) []tea.Model {
|
||||
reader, prog, taskInfo, err := syftEventParsers.ParseAttestationStartedEvent(e)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warn("unable to parse event")
|
||||
log.WithFields("error", err).Debug("unable to parse event")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ func (cts catalogerTaskModel) View() string {
|
||||
func (m *Handler) handleCatalogerTaskStarted(e partybus.Event) ([]tea.Model, tea.Cmd) {
|
||||
mon, info, err := syftEventParsers.ParseCatalogerTaskStarted(e)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warn("unable to parse event")
|
||||
log.WithFields("error", err).Debug("unable to parse event")
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func (m *Handler) handleFetchImage(e partybus.Event) []tea.Model {
|
||||
imgName, prog, err := stereoEventParsers.ParseFetchImage(e)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warn("unable to parse event")
|
||||
log.WithFields("error", err).Debug("unable to parse event")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func (m *Handler) handleFileIndexingStarted(e partybus.Event) []tea.Model {
|
||||
path, prog, err := syftEventParsers.ParseFileIndexingStarted(e)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warn("unable to parse event")
|
||||
log.WithFields("error", err).Debug("unable to parse event")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ type containerdPullStatusFormatter struct {
|
||||
func (m *Handler) handlePullContainerdImage(e partybus.Event) []tea.Model {
|
||||
_, pullStatus, err := stereoscopeParsers.ParsePullContainerdImage(e)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warn("unable to parse event")
|
||||
log.WithFields("error", err).Debug("unable to parse event")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ type dockerPullStatusFormatter struct {
|
||||
func (m *Handler) handlePullDockerImage(e partybus.Event) []tea.Model {
|
||||
_, pullStatus, err := stereoscopeParsers.ParsePullDockerImage(e)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warn("unable to parse event")
|
||||
log.WithFields("error", err).Debug("unable to parse event")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
func (m *Handler) handleReadImage(e partybus.Event) []tea.Model {
|
||||
imgMetadata, prog, err := stereoEventParsers.ParseReadImage(e)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warn("unable to parse event")
|
||||
log.WithFields("error", err).Debug("unable to parse event")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/stereoscope"
|
||||
ui2 "github.com/anchore/syft/cmd/syft/cli/ui"
|
||||
handler "github.com/anchore/syft/cmd/syft/cli/ui"
|
||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
@@ -28,7 +28,7 @@ func AppClioSetupConfig(id clio.Identification, out io.Writer) *clio.SetupConfig
|
||||
|
||||
return clio.NewUICollection(
|
||||
ui.New(out, cfg.Log.Quiet,
|
||||
ui2.New(ui2.DefaultHandlerConfig()),
|
||||
handler.New(handler.DefaultHandlerConfig()),
|
||||
),
|
||||
noUI,
|
||||
), nil
|
||||
|
||||
@@ -3,6 +3,7 @@ package commands
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -14,7 +15,15 @@ import (
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"github.com/anchore/syft/internal/task"
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
)
|
||||
|
||||
var (
|
||||
activelyAddedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("10")) // hi green
|
||||
deselectedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("8")) // dark grey
|
||||
activelyRemovedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("9")) // high red
|
||||
defaultStyle = lipgloss.NewStyle().Underline(true)
|
||||
deselectedDefaultStyle = lipgloss.NewStyle().Inherit(deselectedStyle).Underline(true)
|
||||
)
|
||||
|
||||
type catalogerListOptions struct {
|
||||
@@ -44,8 +53,9 @@ func CatalogerList(app clio.Application) *cobra.Command {
|
||||
opts := defaultCatalogerListOptions()
|
||||
|
||||
return app.SetupCommand(&cobra.Command{
|
||||
Use: "list [OPTIONS]",
|
||||
Short: "List available catalogers",
|
||||
Use: "list [OPTIONS]",
|
||||
Short: "List available catalogers",
|
||||
PreRunE: disableUI(app, os.Stdout),
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return runCatalogerList(opts)
|
||||
},
|
||||
@@ -53,13 +63,19 @@ func CatalogerList(app clio.Application) *cobra.Command {
|
||||
}
|
||||
|
||||
func runCatalogerList(opts *catalogerListOptions) error {
|
||||
factories := task.DefaultPackageTaskFactories()
|
||||
allTasks, err := factories.Tasks(task.DefaultCatalogingFactoryConfig())
|
||||
pkgTaskFactories := task.DefaultPackageTaskFactories()
|
||||
fileTaskFactories := task.DefaultFileTaskFactories()
|
||||
allPkgTasks, err := pkgTaskFactories.Tasks(task.DefaultCatalogingFactoryConfig())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create cataloger tasks: %w", err)
|
||||
return fmt.Errorf("unable to create pkg cataloger tasks: %w", err)
|
||||
}
|
||||
|
||||
report, err := catalogerListReport(opts, allTasks)
|
||||
allFileTasks, err := fileTaskFactories.Tasks(task.DefaultCatalogingFactoryConfig())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create file cataloger tasks: %w", err)
|
||||
}
|
||||
|
||||
report, err := catalogerListReport(opts, [][]task.Task{allPkgTasks, allFileTasks})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to generate cataloger list report: %w", err)
|
||||
}
|
||||
@@ -69,9 +85,10 @@ func runCatalogerList(opts *catalogerListOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func catalogerListReport(opts *catalogerListOptions, allTasks []task.Task) (string, error) {
|
||||
selectedTasks, selectionEvidence, err := task.Select(allTasks,
|
||||
pkgcataloging.NewSelectionRequest().
|
||||
func catalogerListReport(opts *catalogerListOptions, allTaskGroups [][]task.Task) (string, error) {
|
||||
selectedTaskGroups, selectionEvidence, err := task.SelectInGroups(
|
||||
allTaskGroups,
|
||||
cataloging.NewSelectionRequest().
|
||||
WithDefaults(opts.DefaultCatalogers...).
|
||||
WithExpression(opts.SelectCatalogers...),
|
||||
)
|
||||
@@ -82,12 +99,12 @@ func catalogerListReport(opts *catalogerListOptions, allTasks []task.Task) (stri
|
||||
|
||||
switch opts.Output {
|
||||
case "json":
|
||||
report, err = renderCatalogerListJSON(selectedTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
|
||||
report, err = renderCatalogerListJSON(flattenTaskGroups(selectedTaskGroups), selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
|
||||
case "table", "":
|
||||
if opts.ShowHidden {
|
||||
report = renderCatalogerListTable(allTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
|
||||
report = renderCatalogerListTables(allTaskGroups, selectionEvidence)
|
||||
} else {
|
||||
report = renderCatalogerListTable(selectedTasks, selectionEvidence, opts.DefaultCatalogers, opts.SelectCatalogers)
|
||||
report = renderCatalogerListTables(selectedTaskGroups, selectionEvidence)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +115,14 @@ func catalogerListReport(opts *catalogerListOptions, allTasks []task.Task) (stri
|
||||
return report, nil
|
||||
}
|
||||
|
||||
func flattenTaskGroups(taskGroups [][]task.Task) []task.Task {
|
||||
var allTasks []task.Task
|
||||
for _, tasks := range taskGroups {
|
||||
allTasks = append(allTasks, tasks...)
|
||||
}
|
||||
return allTasks
|
||||
}
|
||||
|
||||
func renderCatalogerListJSON(tasks []task.Task, selection task.Selection, defaultSelections, selections []string) (string, error) {
|
||||
type node struct {
|
||||
Name string `json:"name"`
|
||||
@@ -109,7 +134,12 @@ func renderCatalogerListJSON(tasks []task.Task, selection task.Selection, defaul
|
||||
nodesByName := make(map[string]node)
|
||||
|
||||
for name := range tagsByName {
|
||||
tagsSelected := selection.TokensByTask[name].SelectedOn.List()
|
||||
tokensByTask, ok := selection.TokensByTask[name]
|
||||
|
||||
var tagsSelected []string
|
||||
if ok {
|
||||
tagsSelected = tokensByTask.SelectedOn.List()
|
||||
}
|
||||
|
||||
if len(tagsSelected) == 1 && tagsSelected[0] == "all" {
|
||||
tagsSelected = tagsByName[name]
|
||||
@@ -153,10 +183,56 @@ func renderCatalogerListJSON(tasks []task.Task, selection task.Selection, defaul
|
||||
return string(by), err
|
||||
}
|
||||
|
||||
func renderCatalogerListTable(tasks []task.Task, selection task.Selection, defaultSelections, selections []string) string {
|
||||
func renderCatalogerListTables(taskGroups [][]task.Task, selection task.Selection) string {
|
||||
pkgCatalogerTable := renderCatalogerListTable(taskGroups[0], selection, "Package Cataloger")
|
||||
fileCatalogerTable := renderCatalogerListTable(taskGroups[1], selection, "File Cataloger")
|
||||
|
||||
report := fileCatalogerTable + "\n" + pkgCatalogerTable + "\n"
|
||||
|
||||
hasAdditions := len(selection.Request.AddNames) > 0
|
||||
hasDefaults := len(selection.Request.DefaultNamesOrTags) > 0
|
||||
hasRemovals := len(selection.Request.RemoveNamesOrTags) > 0
|
||||
hasSubSelections := len(selection.Request.SubSelectTags) > 0
|
||||
expressions := len(selection.Request.SubSelectTags) + len(selection.Request.AddNames) + len(selection.Request.RemoveNamesOrTags)
|
||||
|
||||
var header string
|
||||
|
||||
header += fmt.Sprintf("Default selections: %d\n", len(selection.Request.DefaultNamesOrTags))
|
||||
if hasDefaults {
|
||||
for _, expr := range selection.Request.DefaultNamesOrTags {
|
||||
header += fmt.Sprintf(" • '%s'\n", expr)
|
||||
}
|
||||
}
|
||||
|
||||
header += fmt.Sprintf("Selection expressions: %d\n", expressions)
|
||||
|
||||
if hasSubSelections {
|
||||
for _, n := range selection.Request.SubSelectTags {
|
||||
header += fmt.Sprintf(" • '%s' (intersect)\n", n)
|
||||
}
|
||||
}
|
||||
if hasRemovals {
|
||||
for _, n := range selection.Request.RemoveNamesOrTags {
|
||||
header += fmt.Sprintf(" • '-%s' (remove)\n", n)
|
||||
}
|
||||
}
|
||||
if hasAdditions {
|
||||
for _, n := range selection.Request.AddNames {
|
||||
header += fmt.Sprintf(" • '+%s' (add)\n", n)
|
||||
}
|
||||
}
|
||||
|
||||
return header + report
|
||||
}
|
||||
|
||||
func renderCatalogerListTable(tasks []task.Task, selection task.Selection, kindTitle string) string {
|
||||
if len(tasks) == 0 {
|
||||
return activelyRemovedStyle.Render(fmt.Sprintf("No %ss selected", strings.ToLower(kindTitle)))
|
||||
}
|
||||
|
||||
t := table.NewWriter()
|
||||
t.SetStyle(table.StyleLight)
|
||||
t.AppendHeader(table.Row{"Cataloger", "Tags"})
|
||||
t.AppendHeader(table.Row{kindTitle, "Tags"})
|
||||
|
||||
names, tagsByName := extractTaskInfo(tasks)
|
||||
|
||||
@@ -172,27 +248,12 @@ func renderCatalogerListTable(tasks []task.Task, selection task.Selection, defau
|
||||
|
||||
report := t.Render()
|
||||
|
||||
if len(selections) > 0 {
|
||||
header := "Selected by expressions:\n"
|
||||
for _, expr := range selections {
|
||||
header += fmt.Sprintf(" - %q\n", expr)
|
||||
}
|
||||
report = header + report
|
||||
}
|
||||
|
||||
if len(defaultSelections) > 0 {
|
||||
header := "Default selections:\n"
|
||||
for _, expr := range defaultSelections {
|
||||
header += fmt.Sprintf(" - %q\n", expr)
|
||||
}
|
||||
report = header + report
|
||||
}
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
func formatRow(name string, tags []string, selection task.Selection) table.Row {
|
||||
isIncluded := selection.Result.Has(name)
|
||||
defaults := strset.New(selection.Request.DefaultNamesOrTags...)
|
||||
var selections *task.TokenSelection
|
||||
if s, exists := selection.TokensByTask[name]; exists {
|
||||
selections = &s
|
||||
@@ -200,35 +261,32 @@ func formatRow(name string, tags []string, selection task.Selection) table.Row {
|
||||
|
||||
var formattedTags []string
|
||||
for _, tag := range tags {
|
||||
formattedTags = append(formattedTags, formatToken(tag, selections, isIncluded))
|
||||
formattedTags = append(formattedTags, formatToken(tag, selections, isIncluded, defaults))
|
||||
}
|
||||
|
||||
var tagStr string
|
||||
if isIncluded {
|
||||
tagStr = strings.Join(formattedTags, ", ")
|
||||
} else {
|
||||
tagStr = strings.Join(formattedTags, grey.Render(", "))
|
||||
tagStr = strings.Join(formattedTags, deselectedStyle.Render(", "))
|
||||
}
|
||||
|
||||
// TODO: selection should keep warnings (non-selections) in struct
|
||||
|
||||
return table.Row{
|
||||
formatToken(name, selections, isIncluded),
|
||||
formatToken(name, selections, isIncluded, defaults),
|
||||
tagStr,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
green = lipgloss.NewStyle().Foreground(lipgloss.Color("10")) // hi green
|
||||
grey = lipgloss.NewStyle().Foreground(lipgloss.Color("8")) // dark grey
|
||||
red = lipgloss.NewStyle().Foreground(lipgloss.Color("9")) // high red
|
||||
)
|
||||
|
||||
func formatToken(token string, selection *task.TokenSelection, included bool) string {
|
||||
func formatToken(token string, selection *task.TokenSelection, included bool, defaults *strset.Set) string {
|
||||
if included && selection != nil {
|
||||
// format all tokens in selection in green
|
||||
if selection.SelectedOn.Has(token) {
|
||||
return green.Render(token)
|
||||
if defaults.Has(token) {
|
||||
return defaultStyle.Render(token)
|
||||
}
|
||||
return activelyAddedStyle.Render(token)
|
||||
}
|
||||
|
||||
return token
|
||||
@@ -236,10 +294,12 @@ func formatToken(token string, selection *task.TokenSelection, included bool) st
|
||||
|
||||
// format all tokens in selection in red, all others in grey
|
||||
if selection != nil && selection.DeselectedOn.Has(token) {
|
||||
return red.Render(token)
|
||||
return activelyRemovedStyle.Render(token)
|
||||
}
|
||||
|
||||
return grey.Render(token)
|
||||
if defaults.Has(token) {
|
||||
return deselectedDefaultStyle.Render(token)
|
||||
}
|
||||
return deselectedStyle.Render(token)
|
||||
}
|
||||
|
||||
func extractTaskInfo(tasks []task.Task) ([]string, map[string][]string) {
|
||||
|
||||
@@ -40,23 +40,35 @@ func (d dummyTask) Execute(_ context.Context, _ file.Resolver, _ sbomsync.Builde
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
func testTasks() []task.Task {
|
||||
return []task.Task{
|
||||
dummyTask{
|
||||
name: "task1",
|
||||
selectors: []string{"image", "a", "b", "1"},
|
||||
func testTasks() [][]task.Task {
|
||||
return [][]task.Task{
|
||||
{
|
||||
dummyTask{
|
||||
name: "task1",
|
||||
selectors: []string{"image", "a", "b", "1"},
|
||||
},
|
||||
dummyTask{
|
||||
name: "task2",
|
||||
selectors: []string{"image", "b", "c", "2"},
|
||||
},
|
||||
dummyTask{
|
||||
name: "task3",
|
||||
selectors: []string{"directory", "c", "d", "3"},
|
||||
},
|
||||
dummyTask{
|
||||
name: "task4",
|
||||
selectors: []string{"directory", "d", "e", "4"},
|
||||
},
|
||||
},
|
||||
dummyTask{
|
||||
name: "task2",
|
||||
selectors: []string{"image", "b", "c", "2"},
|
||||
},
|
||||
dummyTask{
|
||||
name: "task3",
|
||||
selectors: []string{"directory", "c", "d", "3"},
|
||||
},
|
||||
dummyTask{
|
||||
name: "task4",
|
||||
selectors: []string{"directory", "d", "e", "4"},
|
||||
{
|
||||
dummyTask{
|
||||
name: "file-task1",
|
||||
selectors: []string{"file", "ft", "ft-1-b"},
|
||||
},
|
||||
dummyTask{
|
||||
name: "file-task2",
|
||||
selectors: []string{"file", "ft", "ft-2-b"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -76,16 +88,23 @@ func Test_catalogerListReport(t *testing.T) {
|
||||
return c
|
||||
}(),
|
||||
want: `
|
||||
Default selections:
|
||||
- "all"
|
||||
┌───────────┬────────────────────┐
|
||||
│ CATALOGER │ TAGS │
|
||||
├───────────┼────────────────────┤
|
||||
│ task1 │ 1, a, b, image │
|
||||
│ task2 │ 2, b, c, image │
|
||||
│ task3 │ 3, c, d, directory │
|
||||
│ task4 │ 4, d, directory, e │
|
||||
└───────────┴────────────────────┘
|
||||
Default selections: 1
|
||||
• 'all'
|
||||
Selection expressions: 0
|
||||
┌────────────────┬──────────────────┐
|
||||
│ FILE CATALOGER │ TAGS │
|
||||
├────────────────┼──────────────────┤
|
||||
│ file-task1 │ file, ft, ft-1-b │
|
||||
│ file-task2 │ file, ft, ft-2-b │
|
||||
└────────────────┴──────────────────┘
|
||||
┌───────────────────┬────────────────────┐
|
||||
│ PACKAGE CATALOGER │ TAGS │
|
||||
├───────────────────┼────────────────────┤
|
||||
│ task1 │ 1, a, b, image │
|
||||
│ task2 │ 2, b, c, image │
|
||||
│ task3 │ 3, c, d, directory │
|
||||
│ task4 │ 4, d, directory, e │
|
||||
└───────────────────┴────────────────────┘
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -96,7 +115,7 @@ Default selections:
|
||||
return c
|
||||
}(),
|
||||
want: `
|
||||
{"default":["all"],"selection":[],"catalogers":[{"name":"task1","tags":["1","a","b","image"]},{"name":"task2","tags":["2","b","c","image"]},{"name":"task3","tags":["3","c","d","directory"]},{"name":"task4","tags":["4","d","directory","e"]}]}
|
||||
{"default":["all"],"selection":[],"catalogers":[{"name":"file-task1","tags":["file","ft","ft-1-b"]},{"name":"file-task2","tags":["file","ft","ft-2-b"]},{"name":"task1","tags":["1","a","b","image"]},{"name":"task2","tags":["2","b","c","image"]},{"name":"task3","tags":["3","c","d","directory"]},{"name":"task4","tags":["4","d","directory","e"]}]}
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -105,19 +124,27 @@ Default selections:
|
||||
c := defaultCatalogerListOptions()
|
||||
c.Output = "table"
|
||||
c.DefaultCatalogers = []string{
|
||||
"image",
|
||||
"image", // note: for backwards compatibility file will automatically be added
|
||||
}
|
||||
return c
|
||||
}(),
|
||||
want: `
|
||||
Default selections:
|
||||
- "image"
|
||||
┌───────────┬────────────────┐
|
||||
│ CATALOGER │ TAGS │
|
||||
├───────────┼────────────────┤
|
||||
│ task1 │ 1, a, b, image │
|
||||
│ task2 │ 2, b, c, image │
|
||||
└───────────┴────────────────┘
|
||||
Default selections: 2
|
||||
• 'image'
|
||||
• 'file'
|
||||
Selection expressions: 0
|
||||
┌────────────────┬──────────────────┐
|
||||
│ FILE CATALOGER │ TAGS │
|
||||
├────────────────┼──────────────────┤
|
||||
│ file-task1 │ file, ft, ft-1-b │
|
||||
│ file-task2 │ file, ft, ft-2-b │
|
||||
└────────────────┴──────────────────┘
|
||||
┌───────────────────┬────────────────┐
|
||||
│ PACKAGE CATALOGER │ TAGS │
|
||||
├───────────────────┼────────────────┤
|
||||
│ task1 │ 1, a, b, image │
|
||||
│ task2 │ 2, b, c, image │
|
||||
└───────────────────┴────────────────┘
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -131,7 +158,7 @@ Default selections:
|
||||
return c
|
||||
}(),
|
||||
want: `
|
||||
{"default":["image"],"selection":[],"catalogers":[{"name":"task1","tags":["image"]},{"name":"task2","tags":["image"]}]}
|
||||
{"default":["image"],"selection":[],"catalogers":[{"name":"file-task1","tags":["file"]},{"name":"file-task2","tags":["file"]},{"name":"task1","tags":["image"]},{"name":"task2","tags":["image"]}]}
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -147,23 +174,32 @@ Default selections:
|
||||
"+task3",
|
||||
"-c",
|
||||
"b",
|
||||
"-file",
|
||||
"+file-task1",
|
||||
}
|
||||
return c
|
||||
}(),
|
||||
want: `
|
||||
Default selections:
|
||||
- "image"
|
||||
Selected by expressions:
|
||||
- "-directory"
|
||||
- "+task3"
|
||||
- "-c"
|
||||
- "b"
|
||||
┌───────────┬────────────────────┐
|
||||
│ CATALOGER │ TAGS │
|
||||
├───────────┼────────────────────┤
|
||||
│ task1 │ 1, a, b, image │
|
||||
│ task3 │ 3, c, d, directory │
|
||||
└───────────┴────────────────────┘
|
||||
Default selections: 1
|
||||
• 'image'
|
||||
Selection expressions: 6
|
||||
• 'b' (intersect)
|
||||
• '-directory' (remove)
|
||||
• '-c' (remove)
|
||||
• '-file' (remove)
|
||||
• '+task3' (add)
|
||||
• '+file-task1' (add)
|
||||
┌────────────────┬──────────────────┐
|
||||
│ FILE CATALOGER │ TAGS │
|
||||
├────────────────┼──────────────────┤
|
||||
│ file-task1 │ file, ft, ft-1-b │
|
||||
└────────────────┴──────────────────┘
|
||||
┌───────────────────┬────────────────────┐
|
||||
│ PACKAGE CATALOGER │ TAGS │
|
||||
├───────────────────┼────────────────────┤
|
||||
│ task1 │ 1, a, b, image │
|
||||
│ task3 │ 3, c, d, directory │
|
||||
└───────────────────┴────────────────────┘
|
||||
`,
|
||||
},
|
||||
{
|
||||
@@ -183,7 +219,7 @@ Selected by expressions:
|
||||
return c
|
||||
}(),
|
||||
want: `
|
||||
{"default":["image"],"selection":["-directory","+task3","-c","b"],"catalogers":[{"name":"task1","tags":["b","image"]},{"name":"task3","tags":["task3"]}]}
|
||||
{"default":["image"],"selection":["-directory","+task3","-c","b"],"catalogers":[{"name":"file-task1","tags":["file"]},{"name":"file-task2","tags":["file"]},{"name":"task1","tags":["b","image"]},{"name":"task3","tags":["task3"]}]}
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package commands
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
||||
)
|
||||
|
||||
func disableUI(app clio.Application, out io.Writer) func(*cobra.Command, []string) error {
|
||||
return func(_ *cobra.Command, _ []string) error {
|
||||
type Stater interface {
|
||||
State() *clio.State
|
||||
}
|
||||
|
||||
state := app.(Stater).State()
|
||||
state.UI = clio.NewUICollection(ui.None(out, state.Config.Log.Quiet))
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/anchore/syft/syft/file/cataloger/executable"
|
||||
"github.com/anchore/syft/syft/file/cataloger/filecontent"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/dotnet"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/golang"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/java"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
|
||||
@@ -33,6 +34,7 @@ type Catalog struct {
|
||||
DefaultCatalogers []string `yaml:"default-catalogers" json:"default-catalogers" mapstructure:"default-catalogers"`
|
||||
SelectCatalogers []string `yaml:"select-catalogers" json:"select-catalogers" mapstructure:"select-catalogers"`
|
||||
Package packageConfig `yaml:"package" json:"package" mapstructure:"package"`
|
||||
License licenseConfig `yaml:"license" json:"license" mapstructure:"license"`
|
||||
File fileConfig `yaml:"file" json:"file" mapstructure:"file"`
|
||||
Scope string `yaml:"scope" json:"scope" mapstructure:"scope"`
|
||||
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
|
||||
@@ -41,6 +43,7 @@ type Catalog struct {
|
||||
Enrich []string `yaml:"enrich" json:"enrich" mapstructure:"enrich"`
|
||||
|
||||
// ecosystem-specific cataloger configuration
|
||||
Dotnet dotnetConfig `yaml:"dotnet" json:"dotnet" mapstructure:"dotnet"`
|
||||
Golang golangConfig `yaml:"golang" json:"golang" mapstructure:"golang"`
|
||||
Java javaConfig `yaml:"java" json:"java" mapstructure:"java"`
|
||||
JavaScript javaScriptConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"`
|
||||
@@ -69,7 +72,9 @@ func DefaultCatalog() Catalog {
|
||||
Compliance: defaultComplianceConfig(),
|
||||
Scope: source.SquashedScope.String(),
|
||||
Package: defaultPackageConfig(),
|
||||
License: defaultLicenseConfig(),
|
||||
LinuxKernel: defaultLinuxKernelConfig(),
|
||||
Dotnet: defaultDotnetConfig(),
|
||||
Golang: defaultGolangConfig(),
|
||||
Java: defaultJavaConfig(),
|
||||
File: defaultFileConfig(),
|
||||
@@ -89,9 +94,10 @@ func (cfg Catalog) ToSBOMConfig(id clio.Identification) *syft.CreateSBOMConfig {
|
||||
WithUnknownsConfig(cfg.ToUnknownsConfig()).
|
||||
WithSearchConfig(cfg.ToSearchConfig()).
|
||||
WithPackagesConfig(cfg.ToPackagesConfig()).
|
||||
WithLicenseConfig(cfg.ToLicenseConfig()).
|
||||
WithFilesConfig(cfg.ToFilesConfig()).
|
||||
WithCatalogerSelection(
|
||||
pkgcataloging.NewSelectionRequest().
|
||||
cataloging.NewSelectionRequest().
|
||||
WithDefaults(cfg.DefaultCatalogers...).
|
||||
WithExpression(cfg.SelectCatalogers...),
|
||||
)
|
||||
@@ -146,6 +152,13 @@ func (cfg Catalog) ToFilesConfig() filecataloging.Config {
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg Catalog) ToLicenseConfig() cataloging.LicenseConfig {
|
||||
return cataloging.LicenseConfig{
|
||||
IncludeUnkownLicenseContent: cfg.License.IncludeUnknownLicenseContent,
|
||||
Coverage: cfg.License.LicenseCoverage,
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
||||
archiveSearch := cataloging.ArchiveSearchConfig{
|
||||
IncludeIndexedArchives: cfg.Package.SearchIndexedArchives,
|
||||
@@ -153,9 +166,13 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
||||
}
|
||||
return pkgcataloging.Config{
|
||||
Binary: binary.DefaultClassifierCatalogerConfig(),
|
||||
Dotnet: dotnet.DefaultCatalogerConfig().
|
||||
WithCertificateValidation(cfg.Dotnet.EnableCertificateValidation),
|
||||
Golang: golang.DefaultCatalogerConfig().
|
||||
WithSearchLocalModCacheLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchLocalModCacheLicenses)).
|
||||
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
|
||||
WithSearchLocalVendorLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchLocalVendorLicenses)).
|
||||
WithLocalVendorDir(cfg.Golang.LocalVendorDir).
|
||||
WithSearchRemoteLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Go, task.Golang), cfg.Golang.SearchRemoteLicenses)).
|
||||
WithProxy(cfg.Golang.Proxy).
|
||||
WithNoProxy(cfg.Golang.NoProxy).
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/dotnet"
|
||||
)
|
||||
|
||||
type dotnetConfig struct {
|
||||
EnableCertificateValidation bool `json:"enable-certificate-validation" yaml:"enable-certificate-validation" mapstructure:"enable-certificate-validation"`
|
||||
}
|
||||
|
||||
var _ interface {
|
||||
clio.FieldDescriber
|
||||
} = (*dotnetConfig)(nil)
|
||||
|
||||
func (o *dotnetConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&o.EnableCertificateValidation, `enable certificate validation -- this requires an active internet connection to download certificates and CRLs`)
|
||||
}
|
||||
|
||||
func defaultDotnetConfig() dotnetConfig {
|
||||
def := dotnet.DefaultCatalogerConfig()
|
||||
return dotnetConfig{
|
||||
EnableCertificateValidation: def.EnableCertificateValidation,
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,8 @@ import (
|
||||
type golangConfig struct {
|
||||
SearchLocalModCacheLicenses *bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
|
||||
LocalModCacheDir string `json:"local-mod-cache-dir" yaml:"local-mod-cache-dir" mapstructure:"local-mod-cache-dir"`
|
||||
SearchLocalVendorLicenses *bool `json:"search-local-vendor-licenses" yaml:"search-local-vendor-licenses" mapstructure:"search-local-vendor-licenses"`
|
||||
LocalVendorDir string `json:"local-vendor-dir" yaml:"local-vendor-dir" mapstructure:"local-vendor-dir"`
|
||||
SearchRemoteLicenses *bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
|
||||
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
|
||||
@@ -24,6 +26,9 @@ func (o *golangConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&o.SearchLocalModCacheLicenses, `search for go package licences in the GOPATH of the system running Syft, note that this is outside the
|
||||
container filesystem and potentially outside the root of a local directory scan`)
|
||||
descriptions.Add(&o.LocalModCacheDir, `specify an explicit go mod cache directory, if unset this defaults to $GOPATH/pkg/mod or $HOME/go/pkg/mod`)
|
||||
descriptions.Add(&o.SearchLocalVendorLicenses, `search for go package licences in the vendor folder on the system running Syft, note that this is outside the
|
||||
container filesystem and potentially outside the root of a local directory scan`)
|
||||
descriptions.Add(&o.LocalVendorDir, `specify an explicit go vendor directory, if unset this defaults to ./vendor`)
|
||||
descriptions.Add(&o.SearchRemoteLicenses, `search for go package licences by retrieving the package from a network proxy`)
|
||||
descriptions.Add(&o.Proxy, `remote proxy to use when retrieving go packages from the network,
|
||||
if unset this defaults to $GOPROXY followed by https://proxy.golang.org`)
|
||||
@@ -49,6 +54,8 @@ func defaultGolangConfig() golangConfig {
|
||||
return golangConfig{
|
||||
SearchLocalModCacheLicenses: nil, // this defaults to false, which is the API default
|
||||
LocalModCacheDir: def.LocalModCacheDir,
|
||||
SearchLocalVendorLicenses: nil, // this defaults to false, which is the API default
|
||||
LocalVendorDir: def.LocalVendorDir,
|
||||
SearchRemoteLicenses: nil, // this defaults to false, which is the API default
|
||||
Proxy: strings.Join(def.Proxies, ","),
|
||||
NoProxy: strings.Join(def.NoProxy, ","),
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/anchore/clio"
|
||||
)
|
||||
|
||||
type licenseConfig struct {
|
||||
IncludeUnknownLicenseContent bool `yaml:"include-unknown-license-content" json:"include-unknown-license-content" mapstructure:"include-unknown-license-content"`
|
||||
LicenseCoverage float64 `yaml:"license-coverage" json:"license-coverage" mapstructure:"license-coverage"`
|
||||
}
|
||||
|
||||
var _ interface {
|
||||
clio.FieldDescriber
|
||||
} = (*licenseConfig)(nil)
|
||||
|
||||
func (o *licenseConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&o.IncludeUnknownLicenseContent, `include the content of a license in the SBOM when syft
|
||||
cannot determine a valid SPDX ID for the given license`)
|
||||
descriptions.Add(&o.LicenseCoverage, `adjust the percent as a fraction of the total text, in normalized words, that
|
||||
matches any valid license for the given inputs, expressed as a percentage across all of the licenses matched.`)
|
||||
}
|
||||
|
||||
func defaultLicenseConfig() licenseConfig {
|
||||
return licenseConfig{
|
||||
IncludeUnknownLicenseContent: false,
|
||||
LicenseCoverage: 75,
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,9 @@ var _ interface {
|
||||
} = (*packageConfig)(nil)
|
||||
|
||||
func (o *packageConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&o.SearchUnindexedArchives, `search within archives that do contain a file index to search against (zip)
|
||||
descriptions.Add(&o.SearchIndexedArchives, `search within archives that do contain a file index to search against (zip)
|
||||
note: for now this only applies to the java package cataloger`)
|
||||
descriptions.Add(&o.SearchIndexedArchives, `search within archives that do not contain a file index to search against (tar, tar.gz, tar.bz2, etc)
|
||||
descriptions.Add(&o.SearchUnindexedArchives, `search within archives that do not contain a file index to search against (tar, tar.gz, tar.bz2, etc)
|
||||
note: enabling this may result in a performance impact since all discovered compressed tars will be decompressed
|
||||
note: for now this only applies to the java package cataloger`)
|
||||
descriptions.Add(&o.ExcludeBinaryOverlapByOwnership, `allows users to exclude synthetic binary packages from the sbom
|
||||
|
||||
@@ -5,9 +5,11 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/dustin/go-humanize"
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/syft/source/sourceproviders"
|
||||
)
|
||||
|
||||
@@ -27,6 +29,8 @@ var _ interface {
|
||||
clio.FieldDescriber
|
||||
} = (*sourceConfig)(nil)
|
||||
|
||||
var _ clio.PostLoader = (*imageSource)(nil)
|
||||
|
||||
func (o *sourceConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&o.File.Digests, `the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")`)
|
||||
descriptions.Add(&o.Image.DefaultPullSource, `allows users to specify which image source should be used to generate the sbom
|
||||
@@ -35,6 +39,7 @@ valid values are: registry, docker, podman`)
|
||||
|
||||
type imageSource struct {
|
||||
DefaultPullSource string `json:"default-pull-source" yaml:"default-pull-source" mapstructure:"default-pull-source"`
|
||||
MaxLayerSize string `json:"max-layer-size" yaml:"max-layer-size" mapstructure:"max-layer-size"`
|
||||
}
|
||||
|
||||
func defaultSourceConfig() sourceConfig {
|
||||
@@ -56,7 +61,14 @@ func (c *fileSource) PostLoad() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c imageSource) PostLoad() error {
|
||||
func (c *imageSource) PostLoad() error {
|
||||
if c.MaxLayerSize != "" {
|
||||
perFileReadLimit, err := humanize.ParseBytes(c.MaxLayerSize)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
stereoscopeFile.SetPerFileReadLimit(int64(perFileReadLimit))
|
||||
}
|
||||
return checkDefaultSourceValues(c.DefaultPullSource)
|
||||
}
|
||||
|
||||
|
||||
@@ -418,6 +418,14 @@ var dirOnlyTestCases = []testCase{
|
||||
"ocaml-base-compiler": "4.14.0",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find terraform packages",
|
||||
pkgType: pkg.TerraformPkg,
|
||||
pkgLanguage: pkg.Go,
|
||||
pkgInfo: map[string]string{
|
||||
"registry.terraform.io/hashicorp/aws": "5.72.1",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var commonTestCases = []testCase{
|
||||
|
||||
@@ -74,6 +74,8 @@ func TestPkgCoverageImage(t *testing.T) {
|
||||
definedPkgs.Remove(string(pkg.ConanPkg))
|
||||
definedPkgs.Remove(string(pkg.HackagePkg))
|
||||
definedPkgs.Remove(string(pkg.BinaryPkg))
|
||||
definedPkgs.Remove(string(pkg.BitnamiPkg))
|
||||
definedPkgs.Remove(string(pkg.GraalVMNativeImagePkg))
|
||||
definedPkgs.Remove(string(pkg.HexPkg))
|
||||
definedPkgs.Remove(string(pkg.LinuxKernelPkg))
|
||||
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
|
||||
@@ -82,6 +84,7 @@ func TestPkgCoverageImage(t *testing.T) {
|
||||
definedPkgs.Remove(string(pkg.OpamPkg))
|
||||
definedPkgs.Remove(string(pkg.GithubActionPkg))
|
||||
definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg))
|
||||
definedPkgs.Remove(string(pkg.TerraformPkg))
|
||||
|
||||
var cases []testCase
|
||||
cases = append(cases, commonTestCases...)
|
||||
@@ -218,6 +221,8 @@ func TestPkgCoverageDirectory(t *testing.T) {
|
||||
definedLanguages.Remove(pkg.R.String())
|
||||
observedPkgs.Remove(string(pkg.UnknownPkg))
|
||||
definedPkgs.Remove(string(pkg.BinaryPkg))
|
||||
definedPkgs.Remove(string(pkg.BitnamiPkg))
|
||||
definedPkgs.Remove(string(pkg.GraalVMNativeImagePkg))
|
||||
definedPkgs.Remove(string(pkg.LinuxKernelPkg))
|
||||
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
|
||||
definedPkgs.Remove(string(pkg.Rpkg))
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/anchore/syft/internal/spdxlicense"
|
||||
)
|
||||
|
||||
func TestSPDXLicenseListIsTheLatest(t *testing.T) {
|
||||
resp, err := http.Get("https://spdx.org/licenses/licenses.json")
|
||||
if err != nil {
|
||||
t.Fatalf("unable to get licenses list: %+v", err)
|
||||
}
|
||||
|
||||
type licenseList struct {
|
||||
Version string `json:"licenseListVersion"`
|
||||
Licenses []struct {
|
||||
ID string `json:"licenseId"`
|
||||
Name string `json:"name"`
|
||||
Text string `json:"licenseText"`
|
||||
Deprecated bool `json:"isDeprecatedLicenseId"`
|
||||
OSIApproved bool `json:"isOsiApproved"`
|
||||
SeeAlso []string `json:"seeAlso"`
|
||||
} `json:"licenses"`
|
||||
}
|
||||
|
||||
var latest licenseList
|
||||
if err = json.NewDecoder(resp.Body).Decode(&latest); err != nil {
|
||||
t.Fatalf("unable to decode license list: %+v", err)
|
||||
}
|
||||
|
||||
assert.Equal(t, latest.Version, spdxlicense.Version)
|
||||
}
|
||||
Generated
+25
@@ -0,0 +1,25 @@
|
||||
# This file is maintained automatically by "terraform init".
|
||||
# Manual edits may be lost in future updates.
|
||||
|
||||
provider "registry.terraform.io/hashicorp/aws" {
|
||||
version = "5.72.1"
|
||||
constraints = ">= 5.72.0"
|
||||
hashes = [
|
||||
"h1:jhd5O5o0CfZCNEwwN0EiDAzb7ApuFrtxJqa6HXW4EKE=",
|
||||
"zh:0dea6843836e926d33469b48b948744079023816d16a2ff7666bcfb6aa3522d4",
|
||||
"zh:195fa9513f75800a0d62797ebec75ee73e9b8c28d713fe9b63d3b1d1eec129b3",
|
||||
"zh:1ed92f3961715bf0e024bcde3c12dfbdc50b00c1f8a43cc00802cfc45a256208",
|
||||
"zh:2ac687e3a52606466cae4a6813e81d923042488df88d2424e28d3f8530f091bb",
|
||||
"zh:32e7ca75f9314557daada3c44628fe1f3bf964a4f833bfb4b2295d833fe64b6f",
|
||||
"zh:374ee0e6b4327cc6ef666908ce5d6450a3a56e90cd2b785e83c2bcfc100021d2",
|
||||
"zh:5500fd6fdac44f96411fcf9c6d01691159ec35455ed127eb4c3a498e1cc92a64",
|
||||
"zh:723a2dc4b064c12e7ee62ad4fbfd72fa5e025206ea47b735994ef53f3c373152",
|
||||
"zh:89d97b87605f1d734f27e642567cbecf785b521af8ea81dac55c77ccde876221",
|
||||
"zh:951ee1e5731e8d65d521d71b95927e55055b3c4656eef6d46fa580a63328befc",
|
||||
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
|
||||
"zh:9b2b362470b64ec227b2da64762ab8bc4111c6b80365fd9d82fc5e1e33f44038",
|
||||
"zh:aa6e57d0cb974ff0da5dee5d43ad2745cbbc4a2b507d4c799839b9fa96daf688",
|
||||
"zh:ba0d14c4a6b7aa844a830d47c0bf995b632e37f0795394b5b60c638b62b7fc03",
|
||||
"zh:c9764065a9c5d324db0b02bd201b9e3a2118e49c4960884acdeea377173302e9",
|
||||
]
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/cmd/syft/internal/options"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
@@ -20,7 +21,7 @@ func catalogFixtureImage(t *testing.T, fixtureImageName string, scope source.Sco
|
||||
Name: "syft-tester",
|
||||
Version: "v0.99.0",
|
||||
}).WithCatalogerSelection(
|
||||
pkgcataloging.NewSelectionRequest().
|
||||
cataloging.NewSelectionRequest().
|
||||
WithExpression(catalogerSelection...),
|
||||
)
|
||||
cfg.Search.Scope = scope
|
||||
@@ -55,7 +56,7 @@ func catalogDirectory(t *testing.T, dir string, catalogerSelection ...string) (s
|
||||
Name: "syft-tester",
|
||||
Version: "v0.99.0",
|
||||
}).WithCatalogerSelection(
|
||||
pkgcataloging.NewSelectionRequest().
|
||||
cataloging.NewSelectionRequest().
|
||||
WithExpression(catalogerSelection...),
|
||||
)
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
case partybus.Event:
|
||||
log.WithFields("component", "ui").Tracef("event: %q", msg.Type)
|
||||
log.WithFields("component", "ui", "event", msg.Type).Trace("event")
|
||||
|
||||
switch msg.Type {
|
||||
case event.CLIReport, event.CLINotification, event.CLIAppUpdateAvailable:
|
||||
|
||||
@@ -74,7 +74,7 @@ func getSBOM(src source.Source) sbom.SBOM {
|
||||
// only use OS related catalogers that would have been used with the kind of
|
||||
// source type (container image or directory), but also add a specific python cataloger
|
||||
WithCatalogerSelection(
|
||||
pkgcataloging.NewSelectionRequest().
|
||||
cataloging.NewSelectionRequest().
|
||||
WithSubSelections("os").
|
||||
WithAdditions("python-package-cataloger"),
|
||||
).
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
@@ -55,7 +56,7 @@ func getSBOM(src source.Source, defaultTags ...string) sbom.SBOM {
|
||||
WithCatalogerSelection(
|
||||
// here you can sub-select, add, remove catalogers from the default selection...
|
||||
// or replace the default selection entirely!
|
||||
pkgcataloging.NewSelectionRequest().
|
||||
cataloging.NewSelectionRequest().
|
||||
WithDefaults(defaultTags...),
|
||||
)
|
||||
|
||||
|
||||
@@ -1,56 +1,55 @@
|
||||
module github.com/anchore/syft
|
||||
|
||||
go 1.22.9
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.1
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.2
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/Masterminds/sprig/v3 v3.3.0
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/acobaugh/osrelease v0.1.0
|
||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9
|
||||
github.com/anchore/clio v0.0.0-20241015191535-f538a9016e10
|
||||
github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837
|
||||
github.com/anchore/fangs v0.0.0-20241014201141-b6e4b3469f10
|
||||
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537
|
||||
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a
|
||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f
|
||||
github.com/anchore/stereoscope v0.0.9-0.20241113211114-aa3a3ef4efe8
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115
|
||||
github.com/anchore/stereoscope v0.0.13
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
// we are hinting brotli to latest due to warning when installing archiver v3:
|
||||
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
|
||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1
|
||||
github.com/charmbracelet/bubbles v0.20.0
|
||||
github.com/charmbracelet/bubbletea v1.2.2
|
||||
github.com/charmbracelet/bubbletea v1.2.4
|
||||
github.com/charmbracelet/lipgloss v1.0.0
|
||||
github.com/dave/jennifer v1.7.1
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/docker/docker v28.0.0+incompatible
|
||||
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.3.2
|
||||
github.com/gkampitakis/go-snaps v0.5.7
|
||||
github.com/go-git/go-billy/v5 v5.6.0
|
||||
github.com/go-git/go-git/v5 v5.12.0
|
||||
github.com/gkampitakis/go-snaps v0.5.11
|
||||
github.com/go-git/go-billy/v5 v5.6.2
|
||||
github.com/go-git/go-git/v5 v5.13.2
|
||||
github.com/go-test/deep v1.1.1
|
||||
github.com/google/go-cmp v0.6.0
|
||||
github.com/google/go-containerregistry v0.20.2
|
||||
github.com/google/go-containerregistry v0.20.3
|
||||
github.com/google/licensecheck v0.3.1
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gookit/color v1.5.4
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/invopop/jsonschema v0.7.0
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.1
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.6
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||
github.com/knqyf263/go-rpmdb v0.1.1
|
||||
github.com/mholt/archiver/v3 v3.5.1
|
||||
github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5
|
||||
github.com/mitchellh/go-homedir v1.1.0
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||
@@ -60,17 +59,17 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22
|
||||
github.com/saferwall/pe v1.5.5
|
||||
github.com/saferwall/pe v1.5.6
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
|
||||
github.com/sanity-io/litter v1.5.5
|
||||
github.com/sanity-io/litter v1.5.8
|
||||
github.com/sassoftware/go-rpmutils v0.4.0
|
||||
// pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5
|
||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
|
||||
github.com/spdx/tools-golang v0.5.5
|
||||
github.com/spf13/afero v1.11.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/spf13/afero v1.12.0
|
||||
github.com/spf13/cobra v1.9.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/vbatts/go-mtree v0.5.4
|
||||
github.com/vifraa/gopom v1.0.0
|
||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
|
||||
@@ -78,19 +77,22 @@ require (
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
|
||||
go.uber.org/goleak v1.3.0
|
||||
golang.org/x/mod v0.22.0
|
||||
golang.org/x/net v0.31.0
|
||||
golang.org/x/mod v0.23.0
|
||||
golang.org/x/net v0.35.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.33.1
|
||||
modernc.org/sqlite v1.35.0
|
||||
)
|
||||
|
||||
require google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirect
|
||||
require google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.0
|
||||
github.com/OneOfOne/xxhash v1.2.8
|
||||
github.com/adrg/xdg v0.5.3
|
||||
github.com/magiconair/properties v1.8.7
|
||||
github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51
|
||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
|
||||
github.com/hashicorp/hcl/v2 v2.23.0
|
||||
github.com/magiconair/properties v1.8.9
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
)
|
||||
|
||||
@@ -103,10 +105,13 @@ require (
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.7 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.5 // indirect
|
||||
github.com/agext/levenshtein v1.2.1 // indirect; indirectt
|
||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
|
||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.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
|
||||
@@ -115,53 +120,52 @@ require (
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.8 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/containerd/containerd v1.7.23 // indirect
|
||||
github.com/containerd/containerd v1.7.24 // indirect
|
||||
github.com/containerd/containerd/api v1.7.19 // indirect
|
||||
github.com/containerd/continuity v0.4.2 // indirect
|
||||
github.com/containerd/errdefs v0.3.0 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect
|
||||
github.com/containerd/ttrpc v1.2.5 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.1.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/cli v27.3.1+incompatible // indirect
|
||||
github.com/docker/cli v27.5.0+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.8.2 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect
|
||||
github.com/felixge/fgprof v0.9.3 // indirect
|
||||
github.com/felixge/fgprof v0.9.5 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
|
||||
github.com/gkampitakis/ciinfo v0.3.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gkampitakis/ciinfo v0.3.1 // indirect
|
||||
github.com/gkampitakis/go-diff v1.3.2 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // 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.15.13 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/huandu/xstrings v1.5.0 // indirect
|
||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // 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.17.8 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // 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/logrusorgru/aurora v2.0.3+incompatible // indirect
|
||||
@@ -173,6 +177,7 @@ require (
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 // 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
|
||||
@@ -184,75 +189,73 @@ require (
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.15.2 // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/nwaples/rardecode v1.1.0 // indirect
|
||||
github.com/nwaples/rardecode v1.1.3 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||
github.com/pborman/indent v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.19 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // 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/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.12.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.13.1 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sahilm/fuzzy v0.1.1 // indirect
|
||||
github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d // indirect
|
||||
github.com/shopspring/decimal v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.2.2 // indirect
|
||||
github.com/skeema/knownhosts v1.3.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/cast v1.7.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/sylabs/sif/v2 v2.20.0 // indirect
|
||||
github.com/sylabs/squashfs v1.0.0 // indirect
|
||||
github.com/sylabs/sif/v2 v2.20.2 // indirect
|
||||
github.com/sylabs/squashfs v1.0.4 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // indirect
|
||||
github.com/tidwall/gjson v1.17.0 // 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.12 // indirect
|
||||
github.com/vbatts/tar-split v0.11.3 // indirect
|
||||
github.com/vbatts/tar-split v0.11.6 // 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.13.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
|
||||
go.opentelemetry.io/otel v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.24.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
|
||||
go.opentelemetry.io/otel v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.33.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.33.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/term v0.26.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
golang.org/x/crypto v0.33.0 // indirect
|
||||
golang.org/x/sync v0.11.0 // indirect
|
||||
golang.org/x/sys v0.30.0 // indirect
|
||||
golang.org/x/term v0.29.0 // indirect
|
||||
golang.org/x/text v0.22.0 // indirect
|
||||
golang.org/x/tools v0.29.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
|
||||
google.golang.org/grpc v1.67.3 // indirect
|
||||
google.golang.org/protobuf v1.36.3 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
modernc.org/libc v1.55.3 // indirect
|
||||
modernc.org/mathutil v1.6.0 // indirect
|
||||
modernc.org/memory v1.8.0 // indirect
|
||||
modernc.org/strutil v1.2.0 // indirect
|
||||
modernc.org/token v1.1.0 // indirect
|
||||
modernc.org/libc v1.61.13 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.8.2 // indirect
|
||||
)
|
||||
|
||||
retract (
|
||||
v0.53.2
|
||||
v0.53.1 // Published accidentally with incorrect license in depdencies
|
||||
)
|
||||
|
||||
replace github.com/mholt/archiver/v3 v3.5.1 => github.com/anchore/archiver/v3 v3.5.2
|
||||
|
||||
@@ -56,12 +56,11 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.1 h1:yffaWOZsv77oTJa/SdVZYdgAgFioCeycBUKkqS2qzQM=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.1/go.mod h1:NE/EWvzELOFlG6+ljX/QeMlVt9VKcTwu8u0ccsACEsw=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.2 h1:688QHn2X/5nRezKe2ueIVCt+NRqf7fl3AVQk+vaFcIo=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.2/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg=
|
||||
github.com/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=
|
||||
@@ -81,24 +80,26 @@ github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA
|
||||
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.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0=
|
||||
github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
|
||||
github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
|
||||
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
|
||||
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
|
||||
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
|
||||
github.com/agext/levenshtein v1.2.1 h1:QmvMAjj2aEICytGiWzmxoE0x2KZvE0fvmqMOfy2tjT8=
|
||||
github.com/agext/levenshtein v1.2.1/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
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/archiver/v3 v3.5.2 h1:Bjemm2NzuRhmHy3m0lRe5tNoClB9A4zYyDV58PaB6aA=
|
||||
github.com/anchore/archiver/v3 v3.5.2/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51 h1:yhk+P8lF3ZiROjmaVRao9WGTRo4b/wYjoKEiAHWrKwc=
|
||||
github.com/anchore/archiver/v3 v3.5.3-0.20241210171143-5b1d8d1c7c51/go.mod h1:nwuGSd7aZp0rtYt79YggCGafz1RYsclE7pi3fhLwvuw=
|
||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 h1:p0ZIe0htYOX284Y4axJaGBvXHU0VCCzLN5Wf5XbKStU=
|
||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9/go.mod h1:3ZsFB9tzW3vl4gEiUeuSOMDnwroWxIxJelOOHUp8dSw=
|
||||
github.com/anchore/clio v0.0.0-20241015191535-f538a9016e10 h1:3xmanFdoQEH0REvPA+gLm3Km0/981F4z2a/7ADTlv8k=
|
||||
github.com/anchore/clio v0.0.0-20241015191535-f538a9016e10/go.mod h1:h6Ly2hlKjQoPtI3rA8oB5afSmB/XimhcY55xbuW4Dwo=
|
||||
github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837 h1:bIG3WsfosZsJ5LMC7PB9J/ekFM3a0j0ZEDvN3ID6GTI=
|
||||
github.com/anchore/clio v0.0.0-20241115144204-29e89f9fa837/go.mod h1:tRQVKkjYeejrh9AdM0s1esbwtMU7rdHAHSQWkv4qskE=
|
||||
github.com/anchore/fangs v0.0.0-20241014201141-b6e4b3469f10 h1:w+HibE+e/heP6ysADh7sWxg5LhYdVqrpB1A4Hmgjyx8=
|
||||
github.com/anchore/fangs v0.0.0-20241014201141-b6e4b3469f10/go.mod h1:s0L1//Sxn6Rq0Dcxx+dmT/RRmD9HhsaJjJkPUJHLJLM=
|
||||
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 h1:GjNGuwK5jWjJMyVppBjYS54eOiiSNv4Ba869k4wh72Q=
|
||||
@@ -113,21 +114,24 @@ github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0v
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
|
||||
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.1.1-0.20241018175412-5c22e6360c4f h1:dAQPIrQ3a5PBqZeZ+B9NGZsGmodk4NO9OjDIsQmQyQM=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
|
||||
github.com/anchore/stereoscope v0.0.9-0.20241113211114-aa3a3ef4efe8 h1:gYed/fx5OeW+isRay2q60JGt8+ED/odEPiJ/SWlbw8s=
|
||||
github.com/anchore/stereoscope v0.0.9-0.20241113211114-aa3a3ef4efe8/go.mod h1:c2oGDU0R+llJObsatBSenjYPV1raKhMq9GEqe8J56EI=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115 h1:ZyRCmiEjnoGJZ1+Ah0ZZ/mKKqNhGcUZBl0s7PTTDzvY=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20250220190351-d62adb6e1115/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
|
||||
github.com/anchore/stereoscope v0.0.13 h1:9Ivkh7k+vOeG3JHrt44jOg/8UdZrCvMsSjLQ7trHBig=
|
||||
github.com/anchore/stereoscope v0.0.13/go.mod h1:QfhhFc2pezp5aX/dVJ5qnBFpBUv5+KUTphwaQLxMUig=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46 h1:vmXNl+HDfqqXgr0uY1UgK1GAhps8nbAAtqHNBcgyf+4=
|
||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46/go.mod h1:olhPNdiiAAMiSujemd1O/sc6GcyePr23f/6uGKtthNg=
|
||||
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 h1:rcEG5HI490FF0a7zuvxOxen52ddygCfNVjP0XOCMl+M=
|
||||
github.com/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492/go.mod h1:9Beu8XsUNNfzml7WBf3QmyPToP1wm1Gj/Vc5UJKqTzU=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw=
|
||||
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
|
||||
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1 h1:8VKKQtH2aV61+0hovZS3T//rUF+6GDn18paFTVS0h0M=
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1/go.mod h1:3naPe+Bp6wi3n4l5iBFCZgS0JG8vY6FT0H4NGhFJ+i4=
|
||||
github.com/aquasecurity/go-version v0.0.1 h1:4cNl516agK0TCn5F7mmYN+xVs1E3S45LkgZk3cbaW2E=
|
||||
github.com/aquasecurity/go-version v0.0.1/go.mod h1:s1UU6/v2hctXcOa3OLwfj5d9yoXHa3ahf+ipSwEvGT0=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc=
|
||||
@@ -145,8 +149,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef h1:TSFnfbbu2oAOuWbeDDTtwXWE6z+PmpgbSsMBeV7l0ww=
|
||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef/go.mod h1:9iglf1GG4oNRJ39bZ5AZrjgAFD2RwQbXw6Qf7Cs47wo=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
|
||||
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
@@ -158,8 +164,8 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
|
||||
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
|
||||
github.com/charmbracelet/bubbletea v1.2.2 h1:EMz//Ky/aFS2uLcKqpCst5UOE6z5CFDGRsUpyXz0chs=
|
||||
github.com/charmbracelet/bubbletea v1.2.2/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM=
|
||||
github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE=
|
||||
github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM=
|
||||
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.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
|
||||
@@ -168,9 +174,15 @@ github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSe
|
||||
github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
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=
|
||||
@@ -188,8 +200,8 @@ 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/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ=
|
||||
github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
|
||||
github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA=
|
||||
github.com/containerd/containerd v1.7.24/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
|
||||
github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
|
||||
github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig=
|
||||
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
|
||||
@@ -202,21 +214,19 @@ 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 v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU=
|
||||
github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU=
|
||||
github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
|
||||
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/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.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
|
||||
github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM=
|
||||
github.com/cyphar/filepath-securejoin v0.3.6/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
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=
|
||||
@@ -229,16 +239,16 @@ github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4i
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
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/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ=
|
||||
github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM=
|
||||
github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
|
||||
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/docker v28.0.0+incompatible h1:Olh0KS820sJ7nPsBKChVhk5pzqcwDR15fumfAd/p9hM=
|
||||
github.com/docker/docker v28.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.8.2 h1:bX3YxiGzFP5sOXWc3bTPEXdEaZSeVMrFgOr3T+zrFAo=
|
||||
github.com/docker/docker-credential-helpers v0.8.2/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M=
|
||||
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
|
||||
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8=
|
||||
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
|
||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||
@@ -250,8 +260,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
||||
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
|
||||
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
|
||||
github.com/elazarl/goproxy v1.4.0 h1:4GyuSbFa+s26+3rmYNSuUVsx+HgPrV1bk1jXI0l9wjM=
|
||||
github.com/elazarl/goproxy v1.4.0/go.mod h1:X/5W/t+gzDyLfHW4DrMdpjqYjpXsURlBt9lpBDxZZZQ=
|
||||
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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
@@ -277,8 +287,9 @@ github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
|
||||
github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
|
||||
github.com/felixge/fgprof v0.9.3 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
|
||||
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
||||
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
|
||||
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
@@ -286,29 +297,29 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
|
||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/github/go-spdx/v2 v2.3.2 h1:IfdyNHTqzs4zAJjXdVQfRnxt1XMfycXoHBE2Vsm1bjs=
|
||||
github.com/github/go-spdx/v2 v2.3.2/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
|
||||
github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8=
|
||||
github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/ciinfo v0.3.1 h1:lzjbemlGI4Q+XimPg64ss89x8Mf3xihJqy/0Mgagapo=
|
||||
github.com/gkampitakis/ciinfo v0.3.1/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
|
||||
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
|
||||
github.com/gkampitakis/go-snaps v0.5.7 h1:uVGjHR4t4pPHU944udMx7VKHpwepZXmvDMF+yDmI0rg=
|
||||
github.com/gkampitakis/go-snaps v0.5.7/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
|
||||
github.com/gkampitakis/go-snaps v0.5.11 h1:LFG0ggUKR+KEiiaOvFCmLgJ5NO2zf93AxxddkBn3LdQ=
|
||||
github.com/gkampitakis/go-snaps v0.5.11/go.mod h1:PcKmy8q5Se7p48ywpogN5Td13reipz1Iivah4wrTIvY=
|
||||
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.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||
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.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
|
||||
github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||
github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
|
||||
github.com/go-git/go-git/v5 v5.13.2 h1:7O7xvsK7K+rZPKW6AQR1YyNhfywkv7B8/FsP3ki6Zv0=
|
||||
github.com/go-git/go-git/v5 v5.13.2/go.mod h1:hWdW5P4YZRjmpGHwRH2v3zkWcNl6HeXaXQEMGb3NJ9A=
|
||||
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=
|
||||
@@ -317,8 +328,8 @@ github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc=
|
||||
@@ -327,6 +338,11 @@ github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
|
||||
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
|
||||
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
|
||||
github.com/goccy/go-yaml v1.15.13 h1:Xd87Yddmr2rC1SLLTm2MNDcTjeO/GYo0JGiww6gSTDg=
|
||||
github.com/goccy/go-yaml v1.15.13/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
@@ -363,9 +379,6 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
@@ -386,8 +399,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo=
|
||||
github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8=
|
||||
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
|
||||
github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI=
|
||||
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=
|
||||
@@ -410,6 +423,7 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg=
|
||||
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
@@ -453,10 +467,10 @@ github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos=
|
||||
github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.1/go.mod h1:4gW7WsVCke5TE7EPeYliwHlRUyBtfCwuFwuMg2DmyNY=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
@@ -474,6 +488,7 @@ github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
||||
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=
|
||||
@@ -481,10 +496,11 @@ github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy77
|
||||
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
|
||||
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.6.1 h1:iJ65Xjb680rHcikRj6DSIbzCex2huitmc7bDtxYVWyc=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.1/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.6 h1:LyezkL+1SuqH2z47e5IMQkYUIcs2BD+MnpdPRiRcN0c=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.6/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=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
@@ -499,12 +515,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.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/klauspost/pgzip v1.2.6 h1:8RXeL5crjEUFnR2/Sn6GJNWtSQ3Dk8pq4CL3jvdDyjU=
|
||||
github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/knqyf263/go-rpmdb v0.1.1 h1:oh68mTCvp1XzxdU7EfafcWzzfstUZAEa3MW0IJye584=
|
||||
github.com/knqyf263/go-rpmdb v0.1.1/go.mod h1:9LQcoMCMQ9vrF7HcDtXfvqGO4+ddxFQ8+YF/0CVGDww=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
@@ -520,6 +535,7 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
|
||||
@@ -527,8 +543,9 @@ github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
|
||||
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
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/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
@@ -566,6 +583,8 @@ github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HK
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM=
|
||||
github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
|
||||
github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
@@ -607,8 +626,8 @@ github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1n
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
|
||||
github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
@@ -621,6 +640,7 @@ github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bl
|
||||
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
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/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
|
||||
@@ -630,11 +650,10 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
|
||||
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/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/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=
|
||||
@@ -673,13 +692,12 @@ github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/saferwall/pe v1.5.5 h1:GGbzKjXDm7i+1K6riOgtgblyTdRmTbr3r11IzjovAK8=
|
||||
github.com/saferwall/pe v1.5.5/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ=
|
||||
github.com/saferwall/pe v1.5.6 h1:DrRLnoQFxHWJ5lJUmrH7X2L0xeUu6SUS95Dc61eW2Yc=
|
||||
github.com/saferwall/pe v1.5.6/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ=
|
||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
@@ -689,8 +707,8 @@ github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
|
||||
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/sanity-io/litter v1.5.8 h1:uM/2lKrWdGbRXDrIq08Lh9XtVYoeGtcQxk9rtQ7+rYg=
|
||||
github.com/sanity-io/litter v1.5.8/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/sassoftware/go-rpmutils v0.4.0 h1:ojND82NYBxgwrV+mX1CWsd5QJvvEZTKddtCdFLPWhpg=
|
||||
github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jNYPjT5mVcQcIsYzI=
|
||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
|
||||
@@ -706,15 +724,13 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
|
||||
github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
|
||||
github.com/skeema/knownhosts v1.3.0 h1:AM+y0rI04VksttfwjkSTNQorvGqmwATnvnAHpSgc0LY=
|
||||
github.com/skeema/knownhosts v1.3.0/go.mod h1:sPINvnADmT/qYH1kfv+ePMmOBTH6Tbl7b5LvTDjFK7M=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
@@ -723,17 +739,18 @@ github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XO
|
||||
github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
|
||||
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.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM=
|
||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||
@@ -755,22 +772,23 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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.20.0 h1:RfDHEltUrchZbp/XGcWaw3nRSbufoNWqvwmf91/Q2gI=
|
||||
github.com/sylabs/sif/v2 v2.20.0/go.mod h1:z6dq3B7QXK0pD71n15kAapven+gE+PZAIPOewBTNDpU=
|
||||
github.com/sylabs/squashfs v1.0.0 h1:xAyMS21ogglkuR5HaY55PCfqY3H32ma9GkasTYo28Zg=
|
||||
github.com/sylabs/squashfs v1.0.0/go.mod h1:rhWzvgefq1X+R+LZdts10hfMsTg3g74OfGunW8tvg/4=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.5/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
|
||||
github.com/sylabs/sif/v2 v2.20.2 h1:HGEPzauCHhIosw5o6xmT3jczuKEuaFzSfdjAsH33vYw=
|
||||
github.com/sylabs/sif/v2 v2.20.2/go.mod h1:WyYryGRaR4Wp21SAymm5pK0p45qzZCSRiZMFvUZiuhc=
|
||||
github.com/sylabs/squashfs v1.0.4 h1:uFSw7WXv7zjutPvU+JzY0nY494Vw8s4FAf4+7DhoMdI=
|
||||
github.com/sylabs/squashfs v1.0.4/go.mod h1:PDgf8YmCntvN4d9Y8hBUBDCZL6qZOzOQwRGxnIdbERk=
|
||||
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.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
||||
github.com/tidwall/gjson v1.17.0/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/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=
|
||||
@@ -780,15 +798,12 @@ github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
|
||||
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||
github.com/vbatts/go-mtree v0.5.4 h1:OMAb8jaCyiFA7zXj0Zc/oARcxBDBoeu2LizjB8BVJl0=
|
||||
github.com/vbatts/go-mtree v0.5.4/go.mod h1:5GqJbVhm9BBiCc4K5uc/c42FPgXulHaQs4sFUEfIWMo=
|
||||
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
|
||||
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
|
||||
github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs=
|
||||
github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI=
|
||||
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=
|
||||
@@ -808,12 +823,18 @@ github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofm
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zclconf/go-cty v1.13.0 h1:It5dfKTTZHe9aeppbNOda3mN7Ag7sg6QkBNm6TkyFa0=
|
||||
github.com/zclconf/go-cty v1.13.0/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo=
|
||||
github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM=
|
||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1 h1:V+UsotZpAVvfj3X/LMoEytoLzSiP6Lg0F7wdVyu9gGg=
|
||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis=
|
||||
go.etcd.io/etcd/api/v3 v3.5.1/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
@@ -828,20 +849,22 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
|
||||
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
|
||||
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
|
||||
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
|
||||
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0=
|
||||
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
|
||||
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM=
|
||||
go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM=
|
||||
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
|
||||
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
|
||||
go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
|
||||
@@ -866,8 +889,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.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||
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=
|
||||
@@ -907,8 +930,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.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
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=
|
||||
@@ -952,8 +975,8 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||
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=
|
||||
@@ -983,8 +1006,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.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
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=
|
||||
@@ -1054,18 +1077,18 @@ golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
|
||||
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
|
||||
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=
|
||||
@@ -1076,13 +1099,13 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||
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=
|
||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -1138,14 +1161,14 @@ 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.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
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-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
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=
|
||||
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=
|
||||
@@ -1248,12 +1271,12 @@ google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ6
|
||||
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 h1:9+tzLLstTlPTRyJTh+ah5wIMsBW5c4tQwGTN3thOW9Y=
|
||||
google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:mqHbVIp48Muh7Ywss/AD6I5kNVKZMmAa/QEW58Gxp2s=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c h1:lfpJ/2rWPa/kJgxyyXM8PrNnfCzcmxJ265mADgwmvLI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
|
||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA=
|
||||
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=
|
||||
@@ -1281,8 +1304,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.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
|
||||
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
|
||||
google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8=
|
||||
google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s=
|
||||
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=
|
||||
@@ -1297,8 +1320,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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
|
||||
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
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=
|
||||
@@ -1331,30 +1354,28 @@ 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.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
|
||||
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
|
||||
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
|
||||
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
|
||||
modernc.org/cc/v4 v4.24.4 h1:TFkx1s6dCkQpd6dKurBNmpo+G8Zl4Sq/ztJ+2+DEsh0=
|
||||
modernc.org/cc/v4 v4.24.4/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.23.16 h1:Z2N+kk38b7SfySC1ZkpGLN2vthNJP1+ZzGZIlH7uBxo=
|
||||
modernc.org/ccgo/v4 v4.23.16/go.mod h1:nNma8goMTY7aQZQNTyN9AIoJfxav4nvTnvKThAeMDdo=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
|
||||
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
|
||||
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
|
||||
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
|
||||
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
|
||||
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
|
||||
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
|
||||
modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU=
|
||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
|
||||
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
|
||||
modernc.org/sqlite v1.33.1 h1:trb6Z3YYoeM9eDL1O8do81kP+0ejv+YzgyFo+Gwy0nM=
|
||||
modernc.org/sqlite v1.33.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k=
|
||||
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
|
||||
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
|
||||
modernc.org/gc/v2 v2.6.3 h1:aJVhcqAte49LF+mGveZ5KPlsp4tdGdAOT4sipJXADjw=
|
||||
modernc.org/gc/v2 v2.6.3/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.61.13 h1:3LRd6ZO1ezsFiX1y+bHd1ipyEHIJKvuprv0sLTBwLW8=
|
||||
modernc.org/libc v1.61.13/go.mod h1:8F/uJWL/3nNil0Lgt1Dpz+GgkApWh04N3el3hxJcA6E=
|
||||
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.8.2 h1:cL9L4bcoAObu4NkxOlKWBWtNHIsnnACGF/TbqQ6sbcI=
|
||||
modernc.org/memory v1.8.2/go.mod h1:ZbjSvMO5NQ1A2i3bWeDiVMxIorXwdClKE/0SZ+BMotU=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.35.0 h1:yQps4fegMnZFdphtzlfQTCNBWtS0CZv48pRpW3RFHRw=
|
||||
modernc.org/sqlite v1.35.0/go.mod h1:9cr2sicr7jIaWTBKQmAxQLfBv9LL0su4ZTEV+utt3ic=
|
||||
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=
|
||||
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
||||
@@ -3,5 +3,5 @@ package internal
|
||||
const (
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "16.0.18"
|
||||
JSONSchemaVersion = "16.0.22"
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
@@ -12,7 +13,11 @@ import (
|
||||
// CloseAndLogError closes the given io.Closer and reports any errors found as a warning in the log
|
||||
func CloseAndLogError(closer io.Closer, location string) {
|
||||
if err := closer.Close(); err != nil {
|
||||
log.Warnf("unable to close file for location=%q: %+v", location, err)
|
||||
// suppress "file already closed" log messages
|
||||
if errors.Is(err, fs.ErrClosed) {
|
||||
return
|
||||
}
|
||||
log.Debugf("unable to close file for location=%q: %+v", location, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/bmatcuk/doublestar/v4"
|
||||
"github.com/mholt/archiver/v3"
|
||||
|
||||
"github.com/anchore/archiver/v3"
|
||||
)
|
||||
|
||||
// ExtractGlobsFromTarToUniqueTempFile extracts paths matching the given globs within the given archive to a temporary directory, returning file openers for each file extracted.
|
||||
|
||||
@@ -10,9 +10,9 @@ func SetContextLicenseScanner(ctx context.Context, s Scanner) context.Context {
|
||||
return context.WithValue(ctx, licenseScannerKey{}, s)
|
||||
}
|
||||
|
||||
func ContextLicenseScanner(ctx context.Context) Scanner {
|
||||
func ContextLicenseScanner(ctx context.Context) (Scanner, error) {
|
||||
if s, ok := ctx.Value(licenseScannerKey{}).(Scanner); ok {
|
||||
return s
|
||||
return s, nil
|
||||
}
|
||||
return NewDefaultScanner()
|
||||
}
|
||||
|
||||
@@ -2,67 +2,84 @@ package licenses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/google/licensecheck"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
const coverageThreshold = 75 // determined by experimentation
|
||||
const (
|
||||
DefaultCoverageThreshold = 75 // determined by experimentation
|
||||
DefaultIncludeLicenseContent = false
|
||||
)
|
||||
|
||||
type Scanner interface {
|
||||
IdentifyLicenseIDs(context.Context, io.Reader) ([]string, error)
|
||||
IdentifyLicenseIDs(context.Context, io.Reader) ([]string, []byte, error)
|
||||
FileSearch(context.Context, file.LocationReadCloser) ([]file.License, error)
|
||||
PkgSearch(context.Context, file.LocationReadCloser) ([]pkg.License, error)
|
||||
}
|
||||
|
||||
var _ Scanner = (*scanner)(nil)
|
||||
|
||||
type scanner struct {
|
||||
coverageThreshold float64 // between 0 and 100
|
||||
scanner func([]byte) licensecheck.Coverage
|
||||
coverageThreshold float64 // between 0 and 100
|
||||
includeLicenseContent bool
|
||||
scanner func([]byte) licensecheck.Coverage
|
||||
}
|
||||
|
||||
type ScannerConfig struct {
|
||||
CoverageThreshold float64
|
||||
IncludeLicenseContent bool
|
||||
Scanner func([]byte) licensecheck.Coverage
|
||||
}
|
||||
|
||||
type Option func(*scanner)
|
||||
|
||||
func WithCoverage(coverage float64) Option {
|
||||
return func(s *scanner) {
|
||||
s.coverageThreshold = coverage
|
||||
}
|
||||
}
|
||||
|
||||
func WithIncludeLicenseContent(includeLicenseContent bool) Option {
|
||||
return func(s *scanner) {
|
||||
s.includeLicenseContent = includeLicenseContent
|
||||
}
|
||||
}
|
||||
|
||||
// NewDefaultScanner returns a scanner that uses a new instance of the default licensecheck package scanner.
|
||||
func NewDefaultScanner() Scanner {
|
||||
func NewDefaultScanner(o ...Option) (Scanner, error) {
|
||||
s, err := licensecheck.NewScanner(licensecheck.BuiltinLicenses())
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to create default license scanner")
|
||||
s = nil
|
||||
return nil, fmt.Errorf("unable to create default license scanner: %w", err)
|
||||
}
|
||||
newScanner := &scanner{
|
||||
coverageThreshold: DefaultCoverageThreshold,
|
||||
includeLicenseContent: DefaultIncludeLicenseContent,
|
||||
scanner: s.Scan,
|
||||
}
|
||||
|
||||
for _, opt := range o {
|
||||
opt(newScanner)
|
||||
}
|
||||
return newScanner, nil
|
||||
}
|
||||
|
||||
// NewScanner generates a license Scanner with the given ScannerConfig
|
||||
// if config is nil NewDefaultScanner is used
|
||||
func NewScanner(c *ScannerConfig) (Scanner, error) {
|
||||
if c == nil {
|
||||
return NewDefaultScanner()
|
||||
}
|
||||
|
||||
return &scanner{
|
||||
coverageThreshold: coverageThreshold,
|
||||
scanner: s.Scan,
|
||||
}
|
||||
}
|
||||
|
||||
// TestingOnlyScanner returns a scanner that uses the built-in license scanner from the licensecheck package.
|
||||
// THIS IS ONLY MEANT FOR TEST CODE, NOT PRODUCTION CODE.
|
||||
func TestingOnlyScanner() Scanner {
|
||||
return &scanner{
|
||||
coverageThreshold: coverageThreshold,
|
||||
scanner: licensecheck.Scan,
|
||||
}
|
||||
}
|
||||
|
||||
func (s scanner) IdentifyLicenseIDs(_ context.Context, reader io.Reader) ([]string, error) {
|
||||
if s.scanner == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cov := s.scanner(content)
|
||||
if cov.Percent < s.coverageThreshold {
|
||||
// unknown or no licenses here?
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var ids []string
|
||||
for _, m := range cov.Match {
|
||||
ids = append(ids, m.ID)
|
||||
}
|
||||
return ids, nil
|
||||
coverageThreshold: c.CoverageThreshold,
|
||||
includeLicenseContent: c.IncludeLicenseContent,
|
||||
scanner: c.Scanner,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/google/licensecheck"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestIdentifyLicenseIDs(t *testing.T) {
|
||||
type expectation struct {
|
||||
yieldError bool
|
||||
ids []string
|
||||
content []byte
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
expected expectation
|
||||
}{
|
||||
{
|
||||
name: "apache license 2.0",
|
||||
in: `test-fixtures/apache-license-2.0`,
|
||||
expected: expectation{
|
||||
yieldError: false,
|
||||
ids: []string{"Apache-2.0"},
|
||||
content: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom license includes content for IdentifyLicenseIDs",
|
||||
in: "test-fixtures/nvidia-software-and-cuda-supplement",
|
||||
expected: expectation{
|
||||
yieldError: false,
|
||||
ids: []string{},
|
||||
content: mustOpen("test-fixtures/nvidia-software-and-cuda-supplement"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
content, err := os.ReadFile(test.in)
|
||||
require.NoError(t, err)
|
||||
ids, content, err := testScanner(false).IdentifyLicenseIDs(context.TODO(), bytes.NewReader(content))
|
||||
if test.expected.yieldError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, ids, len(test.expected.ids))
|
||||
require.Len(t, content, len(test.expected.content))
|
||||
|
||||
if len(test.expected.ids) > 0 {
|
||||
require.Equal(t, ids, test.expected.ids)
|
||||
}
|
||||
|
||||
if len(test.expected.content) > 0 {
|
||||
require.Equal(t, content, test.expected.content)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testScanner(includeLicenseContent bool) Scanner {
|
||||
return &scanner{
|
||||
coverageThreshold: DefaultCoverageThreshold,
|
||||
includeLicenseContent: includeLicenseContent,
|
||||
scanner: licensecheck.Scan,
|
||||
}
|
||||
}
|
||||
|
||||
func mustOpen(fixture string) []byte {
|
||||
content, err := os.ReadFile(fixture)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return content
|
||||
}
|
||||
+101
-6
@@ -2,24 +2,119 @@ package licenses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
// Search scans the contents of a license file to attempt to determine the type of license it is
|
||||
func Search(ctx context.Context, scanner Scanner, reader file.LocationReadCloser) (licenses []pkg.License, err error) {
|
||||
const (
|
||||
unknownLicenseType = "UNKNOWN"
|
||||
UnknownLicensePrefix = unknownLicenseType + "_"
|
||||
)
|
||||
|
||||
func getCustomLicenseContentHash(contents []byte) string {
|
||||
hash := sha256.Sum256(contents)
|
||||
return fmt.Sprintf("%x", hash[:])
|
||||
}
|
||||
|
||||
func (s *scanner) IdentifyLicenseIDs(_ context.Context, reader io.Reader) ([]string, []byte, error) {
|
||||
if s.scanner == nil {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cov := s.scanner(content)
|
||||
if cov.Percent < s.coverageThreshold {
|
||||
// unknown or no licenses here
|
||||
// => check return content to Search to process
|
||||
return nil, content, nil
|
||||
}
|
||||
|
||||
var ids []string
|
||||
for _, m := range cov.Match {
|
||||
ids = append(ids, m.ID)
|
||||
}
|
||||
return ids, nil, nil
|
||||
}
|
||||
|
||||
// PkgSearch scans the contents of a license file to attempt to determine the type of license it is
|
||||
func (s *scanner) PkgSearch(ctx context.Context, reader file.LocationReadCloser) (licenses []pkg.License, err error) {
|
||||
licenses = make([]pkg.License, 0)
|
||||
|
||||
ids, err := scanner.IdentifyLicenseIDs(ctx, reader)
|
||||
ids, content, err := s.IdentifyLicenseIDs(ctx, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
lic := pkg.NewLicenseFromLocations(id, reader.Location)
|
||||
lic.Type = license.Concluded
|
||||
// IdentifyLicenseIDs can only return a list of ID or content
|
||||
// These return values are mutually exclusive.
|
||||
// If the scanner threshold for matching scores < 75% then we return the license full content
|
||||
if len(ids) > 0 {
|
||||
for _, id := range ids {
|
||||
lic := pkg.NewLicenseFromLocations(id, reader.Location)
|
||||
lic.Type = license.Concluded
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
} else if len(content) > 0 {
|
||||
// harmonize line endings to unix compatible first:
|
||||
// 1. \r\n => \n (Windows => UNIX)
|
||||
// 2. \r => \n (Macintosh => UNIX)
|
||||
content = []byte(strings.ReplaceAll(strings.ReplaceAll(string(content), "\r\n", "\n"), "\r", "\n"))
|
||||
|
||||
lic := pkg.NewLicenseFromLocations(unknownLicenseType, reader.Location)
|
||||
lic.SPDXExpression = UnknownLicensePrefix + getCustomLicenseContentHash(content)
|
||||
if s.includeLicenseContent {
|
||||
lic.Contents = string(content)
|
||||
}
|
||||
lic.Type = license.Declared
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
|
||||
return licenses, nil
|
||||
}
|
||||
|
||||
// FileSearch scans the contents of a license file to attempt to determine the type of license it is
|
||||
func (s *scanner) FileSearch(ctx context.Context, reader file.LocationReadCloser) (licenses []file.License, err error) {
|
||||
licenses = make([]file.License, 0)
|
||||
|
||||
ids, content, err := s.IdentifyLicenseIDs(ctx, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// IdentifyLicenseIDs can only return a list of ID or content
|
||||
// These return values are mutually exclusive.
|
||||
// If the scanner threshold for matching scores < 75% then we return the license full content
|
||||
if len(ids) > 0 {
|
||||
for _, id := range ids {
|
||||
lic := file.NewLicense(id)
|
||||
lic.Type = license.Concluded
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
} else if len(content) > 0 {
|
||||
// harmonize line endings to unix compatible first:
|
||||
// 1. \r\n => \n (Windows => UNIX)
|
||||
// 2. \r => \n (Macintosh => UNIX)
|
||||
content = []byte(strings.ReplaceAll(strings.ReplaceAll(string(content), "\r\n", "\n"), "\r", "\n"))
|
||||
|
||||
lic := file.NewLicense(unknownLicenseType)
|
||||
lic.SPDXExpression = UnknownLicensePrefix + getCustomLicenseContentHash(content)
|
||||
if s.includeLicenseContent {
|
||||
lic.Contents = string(content)
|
||||
}
|
||||
lic.Type = license.Declared
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,166 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
type bytesReadCloser struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (brc *bytesReadCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newBytesReadCloser(data []byte) *bytesReadCloser {
|
||||
return &bytesReadCloser{
|
||||
Buffer: *bytes.NewBuffer(data),
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchFileLicenses(t *testing.T) {
|
||||
type expectation struct {
|
||||
yieldError bool
|
||||
licenses []file.License
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
includeUnkownLicenseContent bool
|
||||
expected expectation
|
||||
}{
|
||||
{
|
||||
name: "apache license 2.0",
|
||||
in: "test-fixtures/apache-license-2.0",
|
||||
expected: expectation{
|
||||
yieldError: false,
|
||||
licenses: []file.License{
|
||||
{
|
||||
Value: "Apache-2.0",
|
||||
SPDXExpression: "Apache-2.0",
|
||||
Type: "concluded",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
content, err := os.ReadFile(test.in)
|
||||
require.NoError(t, err)
|
||||
s := testScanner(false)
|
||||
result, err := s.FileSearch(ctx, file.NewLocationReadCloser(file.NewLocation("LICENSE"), io.NopCloser(bytes.NewReader(content))))
|
||||
if test.expected.yieldError {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, result, len(test.expected.licenses))
|
||||
|
||||
if len(test.expected.licenses) > 0 {
|
||||
require.Equal(t, test.expected.licenses, result)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSearchPkgLicenses(t *testing.T) {
|
||||
type expectation struct {
|
||||
wantErr require.ErrorAssertionFunc
|
||||
licenses []pkg.License
|
||||
}
|
||||
|
||||
testLocation := file.NewLocation("LICENSE")
|
||||
tests := []struct {
|
||||
name string
|
||||
in string
|
||||
includeUnkownLicenseContent bool
|
||||
expected expectation
|
||||
}{
|
||||
{
|
||||
name: "apache license 2.0",
|
||||
in: "test-fixtures/apache-license-2.0",
|
||||
expected: expectation{
|
||||
licenses: []pkg.License{
|
||||
{
|
||||
Value: "Apache-2.0",
|
||||
SPDXExpression: "Apache-2.0",
|
||||
Type: "concluded",
|
||||
URLs: nil,
|
||||
Locations: file.NewLocationSet(testLocation),
|
||||
Contents: "",
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom license no content by default",
|
||||
in: "test-fixtures/nvidia-software-and-cuda-supplement",
|
||||
expected: expectation{
|
||||
licenses: []pkg.License{
|
||||
{
|
||||
Value: "UNKNOWN",
|
||||
SPDXExpression: "UNKNOWN_eebcea3ab1d1a28e671de90119ffcfb35fe86951e4af1b17af52b7a82fcf7d0a",
|
||||
Type: "declared",
|
||||
URLs: nil,
|
||||
Locations: file.NewLocationSet(testLocation),
|
||||
Contents: "",
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom license with content when scanner has content config",
|
||||
in: "test-fixtures/nvidia-software-and-cuda-supplement",
|
||||
includeUnkownLicenseContent: true,
|
||||
expected: expectation{
|
||||
licenses: []pkg.License{
|
||||
{
|
||||
Value: "UNKNOWN",
|
||||
SPDXExpression: "UNKNOWN_eebcea3ab1d1a28e671de90119ffcfb35fe86951e4af1b17af52b7a82fcf7d0a",
|
||||
Type: "declared",
|
||||
URLs: nil,
|
||||
Locations: file.NewLocationSet(testLocation),
|
||||
Contents: string(mustOpen("test-fixtures/nvidia-software-and-cuda-supplement")),
|
||||
},
|
||||
},
|
||||
wantErr: nil,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
ctx := context.TODO()
|
||||
content, err := os.ReadFile(test.in)
|
||||
require.NoError(t, err)
|
||||
s := testScanner(test.includeUnkownLicenseContent)
|
||||
result, err := s.PkgSearch(ctx, file.NewLocationReadCloser(file.NewLocation("LICENSE"), io.NopCloser(bytes.NewReader(content))))
|
||||
if test.expected.wantErr != nil {
|
||||
test.expected.wantErr(t, err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
require.Len(t, result, len(test.expected.licenses))
|
||||
|
||||
if len(test.expected.licenses) > 0 {
|
||||
require.Equal(t, test.expected.licenses, result)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
1. Definitions.
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
END OF TERMS AND CONDITIONS
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,5 @@
|
||||
End User License Agreement
|
||||
--------------------------
|
||||
|
||||
NVIDIA Software License Agreement and CUDA Supplement to
|
||||
Software License Agreement. Last updated: October 8, 2021
|
||||
@@ -1,6 +1,11 @@
|
||||
package internal
|
||||
|
||||
import "regexp"
|
||||
import (
|
||||
"io"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
const readerChunkSize = 1024 * 1024
|
||||
|
||||
// MatchNamedCaptureGroups takes a regular expression and string and returns all of the named capture group results in a map.
|
||||
// This is only for the first match in the regex. Callers shouldn't be providing regexes with multiple capture groups with the same name.
|
||||
@@ -32,6 +37,89 @@ func MatchNamedCaptureGroups(regEx *regexp.Regexp, content string) map[string]st
|
||||
return results
|
||||
}
|
||||
|
||||
// MatchNamedCaptureGroupsFromReader matches named capture groups from a reader, assuming the pattern fits within
|
||||
// 1.5x the reader chunk size (1MB * 1.5).
|
||||
func MatchNamedCaptureGroupsFromReader(re *regexp.Regexp, r io.Reader) (map[string]string, error) {
|
||||
results := make(map[string]string)
|
||||
_, err := processReaderInChunks(r, readerChunkSize, matchNamedCaptureGroupsHandler(re, results))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// MatchAnyFromReader matches any of the provided regular expressions from a reader, assuming the pattern fits within
|
||||
// 1.5x the reader chunk size (1MB * 1.5).
|
||||
func MatchAnyFromReader(r io.Reader, res ...*regexp.Regexp) (bool, error) {
|
||||
return processReaderInChunks(r, readerChunkSize, matchAnyHandler(res))
|
||||
}
|
||||
|
||||
func matchNamedCaptureGroupsHandler(re *regexp.Regexp, results map[string]string) func(data []byte) (bool, error) {
|
||||
return func(data []byte) (bool, error) {
|
||||
if match := re.FindSubmatch(data); match != nil {
|
||||
groupNames := re.SubexpNames()
|
||||
for i, name := range groupNames {
|
||||
if i > 0 && name != "" {
|
||||
results[name] = string(match[i])
|
||||
}
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
func matchAnyHandler(res []*regexp.Regexp) func(data []byte) (bool, error) {
|
||||
return func(data []byte) (bool, error) {
|
||||
for _, re := range res {
|
||||
if re.Match(data) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
// processReaderInChunks reads from the provided reader in chunks and calls the provided handler with each chunk + portion of the previous neighboring chunk.
|
||||
// Note that we only overlap the last half of the previous chunk with the current chunk to avoid missing matches that span chunk boundaries.
|
||||
func processReaderInChunks(rdr io.Reader, chunkSize int, handler func(data []byte) (bool, error)) (bool, error) {
|
||||
half := chunkSize / 2
|
||||
bufSize := chunkSize + half
|
||||
buf := make([]byte, bufSize)
|
||||
lastRead := 0
|
||||
|
||||
for {
|
||||
offset := half
|
||||
if lastRead < half {
|
||||
offset = lastRead
|
||||
}
|
||||
start := half - offset
|
||||
if lastRead > 0 {
|
||||
copy(buf[start:], buf[half+offset:half+lastRead])
|
||||
}
|
||||
n, err := rdr.Read(buf[half:])
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// process the combined data with the handler
|
||||
matched, handlerErr := handler(buf[start : half+n])
|
||||
if handlerErr != nil {
|
||||
return false, handlerErr
|
||||
}
|
||||
if matched {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
lastRead = n
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func isEmptyMap(m map[string]string) bool {
|
||||
if len(m) == 0 {
|
||||
return true
|
||||
|
||||
@@ -2,9 +2,11 @@ package internal
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMatchCaptureGroups(t *testing.T) {
|
||||
@@ -68,3 +70,149 @@ func TestMatchCaptureGroups(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchNamedCaptureGroupsFromReader(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pattern string
|
||||
input string
|
||||
want map[string]string
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "match single group",
|
||||
pattern: `(?P<key>[^1-9]+)`,
|
||||
input: "key",
|
||||
want: map[string]string{"key": "key"},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "match multiple groups",
|
||||
pattern: `(?P<key>[^1-9]+):(?P<value>\w+)`,
|
||||
input: "key:value",
|
||||
want: map[string]string{"key": "key", "value": "value"},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "no match",
|
||||
pattern: `(?P<key>[^1-9]+)`,
|
||||
input: "2345",
|
||||
want: nil,
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "error empty reader",
|
||||
pattern: `(?P<key>\w+)`,
|
||||
input: "",
|
||||
want: nil,
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
re := regexp.MustCompile(tt.pattern)
|
||||
r := strings.NewReader(tt.input)
|
||||
got, err := MatchNamedCaptureGroupsFromReader(re, r)
|
||||
tt.wantErr(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMatchAnyFromReader(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
patterns []*regexp.Regexp
|
||||
want bool
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "match single pattern",
|
||||
input: "hello world",
|
||||
patterns: []*regexp.Regexp{regexp.MustCompile(`hello`)},
|
||||
want: true,
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "match multiple patterns",
|
||||
input: "test case",
|
||||
patterns: []*regexp.Regexp{regexp.MustCompile(`case`), regexp.MustCompile(`test`)},
|
||||
want: true,
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "no match",
|
||||
input: "nothing here",
|
||||
patterns: []*regexp.Regexp{regexp.MustCompile(`absent`)},
|
||||
want: false,
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "error empty reader",
|
||||
input: "",
|
||||
patterns: []*regexp.Regexp{regexp.MustCompile(`match`)},
|
||||
want: false,
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := strings.NewReader(tt.input)
|
||||
got, err := MatchAnyFromReader(r, tt.patterns...)
|
||||
tt.wantErr(t, err)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessReaderInChunks_ChunkBoundaries(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
chunkSize int
|
||||
expectedCalls []string
|
||||
returnOnChunk int
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "go case",
|
||||
input: "123456789012345",
|
||||
chunkSize: 4,
|
||||
returnOnChunk: 2,
|
||||
expectedCalls: []string{"1234", "345678", "789012"},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "no match",
|
||||
input: "123456789012345",
|
||||
chunkSize: 4,
|
||||
returnOnChunk: -1,
|
||||
expectedCalls: []string{"1234", "345678", "789012", "12345"},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var actualCalls []string
|
||||
var current int
|
||||
handler := func(data []byte) (bool, error) {
|
||||
actualCalls = append(actualCalls, string(data))
|
||||
if current == tt.returnOnChunk {
|
||||
return true, nil
|
||||
}
|
||||
current++
|
||||
return false, nil
|
||||
}
|
||||
r := strings.NewReader(tt.input)
|
||||
got, err := processReaderInChunks(r, tt.chunkSize, handler)
|
||||
tt.wantErr(t, err)
|
||||
if tt.returnOnChunk == -1 {
|
||||
assert.False(t, got)
|
||||
} else {
|
||||
assert.True(t, got)
|
||||
}
|
||||
assert.Equal(t, tt.expectedCalls, actualCalls)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,11 +58,10 @@ func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, i
|
||||
|
||||
// PackagesToRemove returns a list of binary packages (resolved by the ELF cataloger) that should be removed from the SBOM
|
||||
// These packages are removed because they are already represented by a higher order packages in the SBOM.
|
||||
func PackagesToRemove(resolver file.Resolver, accessor sbomsync.Accessor) []artifact.ID {
|
||||
func PackagesToRemove(accessor sbomsync.Accessor) []artifact.ID {
|
||||
pkgsToDelete := make([]artifact.ID, 0)
|
||||
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
|
||||
// OTHER package type > ELF package type > Binary package type
|
||||
pkgsToDelete = append(pkgsToDelete, getBinaryPackagesToDelete(resolver, s)...)
|
||||
// ELF package type > Binary package type
|
||||
pkgsToDelete = append(pkgsToDelete, compareElfBinaryPackages(s)...)
|
||||
})
|
||||
return pkgsToDelete
|
||||
@@ -114,33 +113,6 @@ func isElfPackage(p pkg.Package) bool {
|
||||
return ok
|
||||
}
|
||||
|
||||
func getBinaryPackagesToDelete(resolver file.Resolver, s *sbom.SBOM) []artifact.ID {
|
||||
pkgsToDelete := make([]artifact.ID, 0)
|
||||
for p := range s.Artifacts.Packages.Enumerate() {
|
||||
if p.Type == pkg.BinaryPkg {
|
||||
continue
|
||||
}
|
||||
fileOwner, ok := p.Metadata.(pkg.FileOwner)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
ownedFiles := fileOwner.OwnedFiles()
|
||||
locations, err := resolver.FilesByPath(ownedFiles...)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to find path for owned file")
|
||||
continue
|
||||
}
|
||||
for _, loc := range locations {
|
||||
for _, pathPkg := range s.Artifacts.Packages.PackagesByPath(loc.RealPath) {
|
||||
if pathPkg.Type == pkg.BinaryPkg {
|
||||
pkgsToDelete = append(pkgsToDelete, pathPkg.ID())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return pkgsToDelete
|
||||
}
|
||||
|
||||
func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver file.Resolver, addRelationship func(artifact.Relationship), index *sharedLibraryIndex) {
|
||||
for _, libReference := range exec.ImportedLibraries {
|
||||
// for each library reference, check s.Artifacts.Packages.Sorted(pkg.BinaryPkg) for a binary package that represents that library
|
||||
|
||||
@@ -21,7 +21,7 @@ func TestPackagesToRemove(t *testing.T) {
|
||||
Name: "glibc",
|
||||
Version: "2.28-236.el8_9.12",
|
||||
Locations: file.NewLocationSet(
|
||||
file.NewLocation(glibcCoordinate.RealPath),
|
||||
file.NewLocation("path/to/rpmdb"),
|
||||
),
|
||||
Type: pkg.RpmPkg,
|
||||
Metadata: pkg.RpmDBEntry{
|
||||
@@ -88,50 +88,45 @@ func TestPackagesToRemove(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
resolver file.Resolver
|
||||
accessor sbomsync.Accessor
|
||||
want []artifact.ID
|
||||
}{
|
||||
{
|
||||
name: "remove packages that are overlapping rpm --> binary",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccessor([]pkg.Package{glibCPackage, glibCBinaryELFPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{glibCBinaryELFPackage.ID()},
|
||||
// this is surprising, right? the calling function reasons about if any generic binary package (regardless of it being an ELF package or not)
|
||||
// should be deleted or kept based on the user configuration to do so.
|
||||
want: []artifact.ID{},
|
||||
},
|
||||
{
|
||||
name: "keep packages that are overlapping rpm --> binary when the binary self identifies as an RPM",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccessor([]pkg.Package{glibCPackage, glibCBinaryELFPackageAsRPM}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{},
|
||||
},
|
||||
{
|
||||
name: "remove no packages when there is a single binary package (or self identifying RPM)",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccessor([]pkg.Package{glibCBinaryELFPackage, glibCBinaryELFPackageAsRPM}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{},
|
||||
},
|
||||
{
|
||||
name: "remove packages when there is a single binary package and a classifier package",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccessor([]pkg.Package{glibCBinaryELFPackage, glibCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{glibCBinaryClassifierPackage.ID()},
|
||||
},
|
||||
{
|
||||
name: "ensure we're considering ELF packages, not just binary packages (supporting evidence)",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccessor([]pkg.Package{glibCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{},
|
||||
},
|
||||
{
|
||||
name: "ensure we're considering ELF packages, not just binary packages (primary evidence)",
|
||||
resolver: file.NewMockResolverForPaths(glibcCoordinate.RealPath),
|
||||
accessor: newAccessor([]pkg.Package{libCBinaryClassifierPackage}, map[file.Coordinates]file.Executable{}, nil),
|
||||
want: []artifact.ID{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
pkgsToDelete := PackagesToRemove(tt.resolver, tt.accessor)
|
||||
pkgsToDelete := PackagesToRemove(tt.accessor)
|
||||
if diff := cmp.Diff(tt.want, pkgsToDelete); diff != "" {
|
||||
t.Errorf("unexpected packages to delete (-want, +got): %s", diff)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,9 @@ var (
|
||||
binaryCatalogerTypes = []pkg.Type{
|
||||
pkg.BinaryPkg,
|
||||
}
|
||||
bitnamiCatalogerTypes = []pkg.Type{
|
||||
pkg.BitnamiPkg,
|
||||
}
|
||||
binaryMetadataTypes = []string{
|
||||
reflect.TypeOf(pkg.ELFBinaryPackageNoteJSONPayload{}).Name(),
|
||||
reflect.TypeOf(pkg.BinarySignature{}).Name(),
|
||||
@@ -65,6 +68,10 @@ func excludeByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) a
|
||||
return idToRemove
|
||||
}
|
||||
|
||||
if idToRemove := identifyOverlappingBitnamiRelationship(parent, child); idToRemove != "" {
|
||||
return idToRemove
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -124,3 +131,24 @@ func identifyOverlappingOSRelationship(parent *pkg.Package, child *pkg.Package)
|
||||
|
||||
return child.ID()
|
||||
}
|
||||
|
||||
// identifyOverlappingBitnamiRelationship indicates the package ID to remove if this is a Bitnami pkg -> bin pkg relationship.
|
||||
func identifyOverlappingBitnamiRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID {
|
||||
if !slices.Contains(bitnamiCatalogerTypes, parent.Type) {
|
||||
return ""
|
||||
}
|
||||
|
||||
if slices.Contains(binaryCatalogerTypes, child.Type) {
|
||||
return child.ID()
|
||||
}
|
||||
|
||||
if child.Metadata == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if !slices.Contains(binaryMetadataTypes, reflect.TypeOf(child.Metadata).Name()) {
|
||||
return ""
|
||||
}
|
||||
|
||||
return child.ID()
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ func TestExcludeByFileOwnershipOverlap(t *testing.T) {
|
||||
packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg}
|
||||
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg, Metadata: pkg.JavaVMInstallation{}}
|
||||
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{Type: "rpm"}}
|
||||
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC} {
|
||||
packageD := pkg.Package{Name: "package-d", Type: pkg.BitnamiPkg}
|
||||
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD} {
|
||||
p := p
|
||||
p.SetID()
|
||||
}
|
||||
@@ -46,6 +47,17 @@ func TestExcludeByFileOwnershipOverlap(t *testing.T) {
|
||||
packages: pkg.NewCollection(packageC, packageB),
|
||||
shouldExclude: true,
|
||||
},
|
||||
{
|
||||
// prove that Bitnami -> bin exclusions are wired
|
||||
name: "exclusions from bitnami -> binary",
|
||||
relationship: artifact.Relationship{
|
||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
||||
From: packageD, // Bitnami
|
||||
To: packageC, // ELF binary
|
||||
},
|
||||
packages: pkg.NewCollection(packageD, packageC),
|
||||
shouldExclude: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -117,6 +129,63 @@ func TestIdentifyOverlappingOSRelationship(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentifyOverlappingBitnamiRelationship(t *testing.T) {
|
||||
packageA := pkg.Package{Name: "package-a", Type: pkg.BitnamiPkg} // Bitnami package
|
||||
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg}
|
||||
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}}
|
||||
packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg} // Language package
|
||||
packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{}}
|
||||
|
||||
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} {
|
||||
p.SetID()
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
parent *pkg.Package
|
||||
child *pkg.Package
|
||||
expectedID artifact.ID
|
||||
}{
|
||||
{
|
||||
name: "Bitnami -> binary without metadata",
|
||||
parent: &packageA,
|
||||
child: &packageB,
|
||||
expectedID: packageB.ID(), // Bitnami package to binary package, should return child ID
|
||||
},
|
||||
{
|
||||
name: "Bitnami -> binary with binary metadata",
|
||||
parent: &packageA,
|
||||
child: &packageC,
|
||||
expectedID: packageC.ID(), // Bitnami package to binary package with binary metadata, should return child ID
|
||||
},
|
||||
{
|
||||
name: "Bitnami -> non-binary package",
|
||||
parent: &packageA,
|
||||
child: &packageD,
|
||||
expectedID: "", // Bitnami package to non-binary package, no exclusion
|
||||
},
|
||||
{
|
||||
name: "Bitnami -> binary with ELF metadata",
|
||||
parent: &packageA,
|
||||
child: &packageE,
|
||||
expectedID: packageE.ID(), // Bitnami package to binary package with ELF metadata, should return child ID
|
||||
},
|
||||
{
|
||||
name: "non-Bitnami parent",
|
||||
parent: &packageD, // non-Bitnami package
|
||||
child: &packageC,
|
||||
expectedID: "", // non-Bitnami parent, no exclusion
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resultID := identifyOverlappingBitnamiRelationship(tt.parent, tt.child)
|
||||
assert.Equal(t, tt.expectedID, resultID)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIdentifyOverlappingJVMRelationship(t *testing.T) {
|
||||
|
||||
packageA := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Code generated by go generate; DO NOT EDIT.
|
||||
// This file was generated by robots at 2024-08-20 11:33:49.349625 -0400 EDT m=+0.383911876
|
||||
// This file was generated by robots at 2024-11-29 09:22:05.594787 +0100 CET m=+2.414400043
|
||||
// using data from https://spdx.org/licenses/licenses.json
|
||||
package spdxlicense
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/cataloging/filecataloging"
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
)
|
||||
|
||||
type CatalogingFactoryConfig struct {
|
||||
ComplianceConfig cataloging.ComplianceConfig
|
||||
SearchConfig cataloging.SearchConfig
|
||||
RelationshipsConfig cataloging.RelationshipsConfig
|
||||
DataGenerationConfig cataloging.DataGenerationConfig
|
||||
LicenseConfig cataloging.LicenseConfig
|
||||
PackagesConfig pkgcataloging.Config
|
||||
FilesConfig filecataloging.Config
|
||||
}
|
||||
|
||||
func DefaultCatalogingFactoryConfig() CatalogingFactoryConfig {
|
||||
return CatalogingFactoryConfig{
|
||||
ComplianceConfig: cataloging.DefaultComplianceConfig(),
|
||||
SearchConfig: cataloging.DefaultSearchConfig(),
|
||||
RelationshipsConfig: cataloging.DefaultRelationshipsConfig(),
|
||||
DataGenerationConfig: cataloging.DefaultDataGenerationConfig(),
|
||||
LicenseConfig: cataloging.DefaultLicenseConfig(),
|
||||
PackagesConfig: pkgcataloging.DefaultConfig(),
|
||||
FilesConfig: filecataloging.DefaultConfig(),
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
)
|
||||
|
||||
var expressionNodePattern = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9-+]*)+$`)
|
||||
@@ -142,7 +142,7 @@ func (ec expressionContext) newExpression(exp string, operation Operation, token
|
||||
}
|
||||
}
|
||||
|
||||
func newExpressionsFromSelectionRequest(nc *expressionContext, selectionRequest pkgcataloging.SelectionRequest) Expressions {
|
||||
func newExpressionsFromSelectionRequest(nc *expressionContext, selectionRequest cataloging.SelectionRequest) Expressions {
|
||||
var all Expressions
|
||||
|
||||
for _, exp := range selectionRequest.DefaultNamesOrTags {
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
)
|
||||
|
||||
func Test_newExpressionsFromSelectionRequest(t *testing.T) {
|
||||
@@ -135,7 +135,7 @@ func Test_newExpressionsFromSelectionRequest(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
req := pkgcataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
|
||||
req := cataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
|
||||
|
||||
result := newExpressionsFromSelectionRequest(nc, req)
|
||||
if tt.expectedErrors != nil {
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
)
|
||||
|
||||
type factory func(cfg CatalogingFactoryConfig) Task
|
||||
|
||||
type Factories []factory
|
||||
|
||||
func (f Factories) Tasks(cfg CatalogingFactoryConfig) ([]Task, error) {
|
||||
var allTasks []Task
|
||||
taskNames := strset.New()
|
||||
duplicateTaskNames := strset.New()
|
||||
var err error
|
||||
for _, fact := range f {
|
||||
tsk := fact(cfg)
|
||||
if tsk == nil {
|
||||
continue
|
||||
}
|
||||
tskName := tsk.Name()
|
||||
if taskNames.Has(tskName) {
|
||||
duplicateTaskNames.Add(tskName)
|
||||
}
|
||||
|
||||
allTasks = append(allTasks, tsk)
|
||||
taskNames.Add(tskName)
|
||||
}
|
||||
if duplicateTaskNames.Size() > 0 {
|
||||
names := duplicateTaskNames.List()
|
||||
sort.Strings(names)
|
||||
err = fmt.Errorf("duplicate cataloger task names: %v", strings.Join(names, ", "))
|
||||
}
|
||||
|
||||
return allTasks, err
|
||||
}
|
||||
+58
-27
@@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/cataloging/filecataloging"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/file/cataloger/executable"
|
||||
"github.com/anchore/syft/syft/file/cataloger/filecontent"
|
||||
@@ -15,14 +16,27 @@ import (
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash) Task {
|
||||
if selection == file.NoFilesSelection || len(hashers) == 0 {
|
||||
return nil
|
||||
func DefaultFileTaskFactories() Factories {
|
||||
return Factories{
|
||||
newFileDigestCatalogerTaskFactory("digest"),
|
||||
newFileMetadataCatalogerTaskFactory("file-metadata"),
|
||||
newFileContentCatalogerTaskFactory("content"),
|
||||
newExecutableCatalogerTaskFactory("binary-metadata"),
|
||||
}
|
||||
}
|
||||
|
||||
digestsCataloger := filedigest.NewCataloger(hashers)
|
||||
func newFileDigestCatalogerTaskFactory(tags ...string) factory {
|
||||
return func(cfg CatalogingFactoryConfig) Task {
|
||||
return newFileDigestCatalogerTask(cfg.FilesConfig.Selection, cfg.FilesConfig.Hashers, tags...)
|
||||
}
|
||||
}
|
||||
|
||||
func newFileDigestCatalogerTask(selection file.Selection, hashers []crypto.Hash, tags ...string) Task {
|
||||
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
if selection == file.NoFilesSelection || len(hashers) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
accessor := builder.(sbomsync.Accessor)
|
||||
|
||||
coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor))
|
||||
@@ -30,7 +44,7 @@ func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash
|
||||
return nil
|
||||
}
|
||||
|
||||
result, err := digestsCataloger.Catalog(ctx, resolver, coordinates...)
|
||||
result, err := filedigest.NewCataloger(hashers).Catalog(ctx, resolver, coordinates...)
|
||||
|
||||
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
|
||||
sbom.Artifacts.FileDigests = result
|
||||
@@ -39,17 +53,21 @@ func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash
|
||||
return err
|
||||
}
|
||||
|
||||
return NewTask("file-digest-cataloger", fn)
|
||||
return NewTask("file-digest-cataloger", fn, commonFileTags(tags)...)
|
||||
}
|
||||
|
||||
func NewFileMetadataCatalogerTask(selection file.Selection) Task {
|
||||
if selection == file.NoFilesSelection {
|
||||
return nil
|
||||
func newFileMetadataCatalogerTaskFactory(tags ...string) factory {
|
||||
return func(cfg CatalogingFactoryConfig) Task {
|
||||
return newFileMetadataCatalogerTask(cfg.FilesConfig.Selection, tags...)
|
||||
}
|
||||
}
|
||||
|
||||
metadataCataloger := filemetadata.NewCataloger()
|
||||
|
||||
func newFileMetadataCatalogerTask(selection file.Selection, tags ...string) Task {
|
||||
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
if selection == file.NoFilesSelection {
|
||||
return nil
|
||||
}
|
||||
|
||||
accessor := builder.(sbomsync.Accessor)
|
||||
|
||||
coordinates, ok := coordinatesForSelection(selection, builder.(sbomsync.Accessor))
|
||||
@@ -57,7 +75,7 @@ func NewFileMetadataCatalogerTask(selection file.Selection) Task {
|
||||
return nil
|
||||
}
|
||||
|
||||
result, err := metadataCataloger.Catalog(ctx, resolver, coordinates...)
|
||||
result, err := filemetadata.NewCataloger().Catalog(ctx, resolver, coordinates...)
|
||||
|
||||
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
|
||||
sbom.Artifacts.FileMetadata = result
|
||||
@@ -66,20 +84,24 @@ func NewFileMetadataCatalogerTask(selection file.Selection) Task {
|
||||
return err
|
||||
}
|
||||
|
||||
return NewTask("file-metadata-cataloger", fn)
|
||||
return NewTask("file-metadata-cataloger", fn, commonFileTags(tags)...)
|
||||
}
|
||||
|
||||
func NewFileContentCatalogerTask(cfg filecontent.Config) Task {
|
||||
if len(cfg.Globs) == 0 {
|
||||
return nil
|
||||
func newFileContentCatalogerTaskFactory(tags ...string) factory {
|
||||
return func(cfg CatalogingFactoryConfig) Task {
|
||||
return newFileContentCatalogerTask(cfg.FilesConfig.Content, tags...)
|
||||
}
|
||||
}
|
||||
|
||||
cat := filecontent.NewCataloger(cfg)
|
||||
|
||||
func newFileContentCatalogerTask(cfg filecontent.Config, tags ...string) Task {
|
||||
fn := func(ctx context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
if len(cfg.Globs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
accessor := builder.(sbomsync.Accessor)
|
||||
|
||||
result, err := cat.Catalog(ctx, resolver)
|
||||
result, err := filecontent.NewCataloger(cfg).Catalog(ctx, resolver)
|
||||
|
||||
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
|
||||
sbom.Artifacts.FileContents = result
|
||||
@@ -88,20 +110,24 @@ func NewFileContentCatalogerTask(cfg filecontent.Config) Task {
|
||||
return err
|
||||
}
|
||||
|
||||
return NewTask("file-content-cataloger", fn)
|
||||
return NewTask("file-content-cataloger", fn, commonFileTags(tags)...)
|
||||
}
|
||||
|
||||
func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config) Task {
|
||||
if selection == file.NoFilesSelection {
|
||||
return nil
|
||||
func newExecutableCatalogerTaskFactory(tags ...string) factory {
|
||||
return func(cfg CatalogingFactoryConfig) Task {
|
||||
return newExecutableCatalogerTask(cfg.FilesConfig.Selection, cfg.FilesConfig.Executable, tags...)
|
||||
}
|
||||
}
|
||||
|
||||
cat := executable.NewCataloger(cfg)
|
||||
|
||||
func newExecutableCatalogerTask(selection file.Selection, cfg executable.Config, tags ...string) Task {
|
||||
fn := func(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
if selection == file.NoFilesSelection {
|
||||
return nil
|
||||
}
|
||||
|
||||
accessor := builder.(sbomsync.Accessor)
|
||||
|
||||
result, err := cat.Catalog(resolver)
|
||||
result, err := executable.NewCataloger(cfg).Catalog(resolver)
|
||||
|
||||
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
|
||||
sbom.Artifacts.Executables = result
|
||||
@@ -110,7 +136,7 @@ func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config)
|
||||
return err
|
||||
}
|
||||
|
||||
return NewTask("file-executable-cataloger", fn)
|
||||
return NewTask("file-executable-cataloger", fn, commonFileTags(tags)...)
|
||||
}
|
||||
|
||||
// TODO: this should be replaced with a fix that allows passing a coordinate or location iterator to the cataloger
|
||||
@@ -154,3 +180,8 @@ func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accesso
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func commonFileTags(tags []string) []string {
|
||||
tags = append(tags, filecataloging.FileTag)
|
||||
return tags
|
||||
}
|
||||
|
||||
@@ -3,12 +3,9 @@ package task
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/relationship"
|
||||
@@ -23,67 +20,18 @@ import (
|
||||
cpeutils "github.com/anchore/syft/syft/pkg/cataloger/common/cpe"
|
||||
)
|
||||
|
||||
type packageTaskFactory func(cfg CatalogingFactoryConfig) Task
|
||||
|
||||
type PackageTaskFactories []packageTaskFactory
|
||||
|
||||
type CatalogingFactoryConfig struct {
|
||||
ComplianceConfig cataloging.ComplianceConfig
|
||||
SearchConfig cataloging.SearchConfig
|
||||
RelationshipsConfig cataloging.RelationshipsConfig
|
||||
DataGenerationConfig cataloging.DataGenerationConfig
|
||||
PackagesConfig pkgcataloging.Config
|
||||
}
|
||||
|
||||
func DefaultCatalogingFactoryConfig() CatalogingFactoryConfig {
|
||||
return CatalogingFactoryConfig{
|
||||
ComplianceConfig: cataloging.DefaultComplianceConfig(),
|
||||
SearchConfig: cataloging.DefaultSearchConfig(),
|
||||
RelationshipsConfig: cataloging.DefaultRelationshipsConfig(),
|
||||
DataGenerationConfig: cataloging.DefaultDataGenerationConfig(),
|
||||
PackagesConfig: pkgcataloging.DefaultConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
func newPackageTaskFactory(catalogerFactory func(CatalogingFactoryConfig) pkg.Cataloger, tags ...string) packageTaskFactory {
|
||||
func newPackageTaskFactory(catalogerFactory func(CatalogingFactoryConfig) pkg.Cataloger, tags ...string) factory {
|
||||
return func(cfg CatalogingFactoryConfig) Task {
|
||||
return NewPackageTask(cfg, catalogerFactory(cfg), tags...)
|
||||
}
|
||||
}
|
||||
|
||||
func newSimplePackageTaskFactory(catalogerFactory func() pkg.Cataloger, tags ...string) packageTaskFactory {
|
||||
func newSimplePackageTaskFactory(catalogerFactory func() pkg.Cataloger, tags ...string) factory {
|
||||
return func(cfg CatalogingFactoryConfig) Task {
|
||||
return NewPackageTask(cfg, catalogerFactory(), tags...)
|
||||
}
|
||||
}
|
||||
|
||||
func (f PackageTaskFactories) Tasks(cfg CatalogingFactoryConfig) ([]Task, error) {
|
||||
var allTasks []Task
|
||||
taskNames := strset.New()
|
||||
duplicateTaskNames := strset.New()
|
||||
var err error
|
||||
for _, factory := range f {
|
||||
tsk := factory(cfg)
|
||||
if tsk == nil {
|
||||
continue
|
||||
}
|
||||
tskName := tsk.Name()
|
||||
if taskNames.Has(tskName) {
|
||||
duplicateTaskNames.Add(tskName)
|
||||
}
|
||||
|
||||
allTasks = append(allTasks, tsk)
|
||||
taskNames.Add(tskName)
|
||||
}
|
||||
if duplicateTaskNames.Size() > 0 {
|
||||
names := duplicateTaskNames.List()
|
||||
sort.Strings(names)
|
||||
err = fmt.Errorf("duplicate cataloger task names: %v", strings.Join(names, ", "))
|
||||
}
|
||||
|
||||
return allTasks, err
|
||||
}
|
||||
|
||||
// NewPackageTask creates a Task function for a generic pkg.Cataloger, honoring the common configuration options.
|
||||
func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string) Task {
|
||||
fn := func(ctx context.Context, resolver file.Resolver, sbom sbomsync.Builder) error {
|
||||
@@ -153,7 +101,7 @@ func finalizePkgCatalogerResults(cfg CatalogingFactoryConfig, resolver file.Path
|
||||
// create file-to-package relationships for files owned by the package
|
||||
owningRelationships, err := packageFileOwnershipRelationships(p, resolver)
|
||||
if err != nil {
|
||||
log.Warnf("unable to create any package-file relationships for package name=%q type=%q: %v", p.Name, p.Type, err)
|
||||
log.Debugf("unable to create any package-file relationships for package name=%q type=%q: %v", p.Name, p.Type, err)
|
||||
} else {
|
||||
relationships = append(relationships, owningRelationships...)
|
||||
}
|
||||
@@ -226,7 +174,7 @@ func applyComplianceRules(p *pkg.Package, cfg cataloging.ComplianceConfig) (bool
|
||||
return true
|
||||
|
||||
case cataloging.ComplianceActionKeep:
|
||||
log.WithFields("pkg", p.String(), "location", loc).Tracef("package with missing %s, taking no action", fieldName)
|
||||
log.WithFields("pkg", p.String(), "location", loc, "field", fieldName).Trace("package with missing field, taking no action")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/alpine"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/arch"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary"
|
||||
bitnamiSbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/bitnami"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/cpp"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/dart"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/debian"
|
||||
@@ -31,6 +32,7 @@ import (
|
||||
sbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/sbom"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/swift"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/swipl"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/terraform"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/wordpress"
|
||||
)
|
||||
|
||||
@@ -50,8 +52,8 @@ const (
|
||||
)
|
||||
|
||||
//nolint:funlen
|
||||
func DefaultPackageTaskFactories() PackageTaskFactories {
|
||||
return []packageTaskFactory{
|
||||
func DefaultPackageTaskFactories() Factories {
|
||||
return []factory{
|
||||
// OS package installed catalogers ///////////////////////////////////////////////////////////////////////////
|
||||
newSimplePackageTaskFactory(arch.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "alpm", "archlinux"),
|
||||
newSimplePackageTaskFactory(alpine.NewDBCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.OSTag, "linux", "apk", "alpine"),
|
||||
@@ -114,7 +116,11 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
|
||||
newSimplePackageTaskFactory(ocaml.NewOpamPackageManagerCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "ocaml", "opam"),
|
||||
|
||||
// language-specific package for both image and directory scans (but not necessarily declared) ////////////////////////////////////////
|
||||
newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "dotnet", "c#", "binary"),
|
||||
newSimplePackageTaskFactory(dotnet.NewDotnetPackagesLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.ImageTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "dotnet", "c#"),
|
||||
newPackageTaskFactory(
|
||||
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
||||
return dotnet.NewDotnetPortableExecutableCataloger(cfg.PackagesConfig.Dotnet)
|
||||
}, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "dotnet", "c#", "binary"),
|
||||
newSimplePackageTaskFactory(python.NewInstalledPackageCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "python"),
|
||||
newPackageTaskFactory(
|
||||
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
||||
@@ -150,6 +156,8 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
|
||||
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "linux", "kernel",
|
||||
),
|
||||
newSimplePackageTaskFactory(sbomCataloger.NewCataloger, "sbom"), // note: not evidence of installed packages
|
||||
newSimplePackageTaskFactory(bitnamiSbomCataloger.NewCataloger, "bitnami", pkgcataloging.InstalledTag, pkgcataloging.ImageTag),
|
||||
newSimplePackageTaskFactory(wordpress.NewWordpressPluginCataloger, pkgcataloging.DirectoryTag, pkgcataloging.ImageTag, "wordpress"),
|
||||
newSimplePackageTaskFactory(terraform.NewLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "terraform"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ func finalizeRelationships(resolver file.Resolver, builder sbomsync.Builder, cfg
|
||||
|
||||
// remove ELF packages and Binary packages that are already
|
||||
// represented by a source package (e.g. a package that is evident by some package manager)
|
||||
builder.DeletePackages(binary.PackagesToRemove(resolver, accessor)...)
|
||||
builder.DeletePackages(binary.PackagesToRemove(accessor)...)
|
||||
|
||||
// add relationships showing packages that are evident by a file which is owned by another package (package-to-package)
|
||||
if cfg.PackageFileOwnershipOverlap {
|
||||
|
||||
+152
-3
@@ -3,17 +3,19 @@ package task
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/cataloging/filecataloging"
|
||||
)
|
||||
|
||||
// Selection represents the users request for a subset of tasks to run and the resulting set of task names that were
|
||||
// selected. Additionally, all tokens that were matched on to reach the returned conclusion are also provided.
|
||||
type Selection struct {
|
||||
Request pkgcataloging.SelectionRequest
|
||||
Request cataloging.SelectionRequest
|
||||
Result *strset.Set
|
||||
TokensByTask map[string]TokenSelection
|
||||
}
|
||||
@@ -52,7 +54,18 @@ func newSelection() Selection {
|
||||
// Select parses the given expressions as two sets: expressions that represent a "set" operation, and expressions that
|
||||
// represent all other operations. The parsed expressions are then evaluated against the given tasks to return
|
||||
// a subset (or the same) set of tasks.
|
||||
func Select(allTasks []Task, selectionRequest pkgcataloging.SelectionRequest) ([]Task, Selection, error) {
|
||||
func Select(allTasks []Task, selectionRequest cataloging.SelectionRequest) ([]Task, Selection, error) {
|
||||
ensureDefaultSelectionHasFiles(&selectionRequest, allTasks)
|
||||
|
||||
return _select(allTasks, selectionRequest)
|
||||
}
|
||||
|
||||
func _select(allTasks []Task, selectionRequest cataloging.SelectionRequest) ([]Task, Selection, error) {
|
||||
if selectionRequest.IsEmpty() {
|
||||
selection := newSelection()
|
||||
selection.Request = selectionRequest
|
||||
return nil, selection, nil
|
||||
}
|
||||
nodes := newExpressionsFromSelectionRequest(newExpressionContext(allTasks), selectionRequest)
|
||||
|
||||
finalTasks, selection := selectByExpressions(allTasks, nodes)
|
||||
@@ -62,6 +75,142 @@ func Select(allTasks []Task, selectionRequest pkgcataloging.SelectionRequest) ([
|
||||
return finalTasks, selection, nodes.Validate()
|
||||
}
|
||||
|
||||
// ensureDefaultSelectionHasFiles ensures that the default selection request has the "file" tag, as this is a required
|
||||
// for backwards compatibility (when catalogers were only for packages and not for separate groups of tasks).
|
||||
func ensureDefaultSelectionHasFiles(selectionRequest *cataloging.SelectionRequest, allTasks ...[]Task) {
|
||||
for _, ts := range allTasks {
|
||||
_, leftOver := tagsOrNamesThatTaskGroupRespondsTo(ts, strset.New(filecataloging.FileTag))
|
||||
if leftOver.Has(filecataloging.FileTag) {
|
||||
// the given set of tasks do not respond to file, so don't include it in the default selection
|
||||
continue
|
||||
}
|
||||
|
||||
defaultNamesOrTags := strset.New(selectionRequest.DefaultNamesOrTags...)
|
||||
removals := strset.New(selectionRequest.RemoveNamesOrTags...)
|
||||
missingFileIshTag := !defaultNamesOrTags.Has(filecataloging.FileTag) && !defaultNamesOrTags.Has("all") && !defaultNamesOrTags.Has("default")
|
||||
if missingFileIshTag && !removals.Has(filecataloging.FileTag) {
|
||||
log.Warnf("adding '%s' tag to the default cataloger selection, to override add '-%s' to the cataloger selection request", filecataloging.FileTag, filecataloging.FileTag)
|
||||
selectionRequest.DefaultNamesOrTags = append(selectionRequest.DefaultNamesOrTags, filecataloging.FileTag)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SelectInGroups is a convenience function that allows for selecting tasks from multiple groups of tasks. The original
|
||||
// request is split into sub-requests, where only tokens that are relevant to the given group of tasks are considered.
|
||||
// If tokens are passed that are not relevant to any group of tasks, an error is returned.
|
||||
func SelectInGroups(taskGroups [][]Task, selectionRequest cataloging.SelectionRequest) ([][]Task, Selection, error) {
|
||||
ensureDefaultSelectionHasFiles(&selectionRequest, taskGroups...)
|
||||
|
||||
reqs, errs := splitCatalogerSelectionRequest(selectionRequest, taskGroups)
|
||||
if errs != nil {
|
||||
return nil, Selection{
|
||||
Request: selectionRequest,
|
||||
}, errs
|
||||
}
|
||||
|
||||
var finalTasks [][]Task
|
||||
var selections []Selection
|
||||
for idx, req := range reqs {
|
||||
tskGroup := taskGroups[idx]
|
||||
subFinalTasks, subSelection, err := _select(tskGroup, req)
|
||||
if err != nil {
|
||||
return nil, Selection{
|
||||
Request: selectionRequest,
|
||||
}, err
|
||||
}
|
||||
finalTasks = append(finalTasks, subFinalTasks)
|
||||
selections = append(selections, subSelection)
|
||||
}
|
||||
|
||||
return finalTasks, mergeSelections(selections, selectionRequest), nil
|
||||
}
|
||||
|
||||
func mergeSelections(selections []Selection, ogRequest cataloging.SelectionRequest) Selection {
|
||||
finalSelection := newSelection()
|
||||
for _, s := range selections {
|
||||
finalSelection.Result.Add(s.Result.List()...)
|
||||
for name, tokenSelection := range s.TokensByTask {
|
||||
if existing, exists := finalSelection.TokensByTask[name]; exists {
|
||||
existing.merge(tokenSelection)
|
||||
finalSelection.TokensByTask[name] = existing
|
||||
} else {
|
||||
finalSelection.TokensByTask[name] = tokenSelection
|
||||
}
|
||||
}
|
||||
}
|
||||
finalSelection.Request = ogRequest
|
||||
return finalSelection
|
||||
}
|
||||
|
||||
func splitCatalogerSelectionRequest(req cataloging.SelectionRequest, selectablePkgTaskGroups [][]Task) ([]cataloging.SelectionRequest, error) {
|
||||
requestTagsOrNames := allRequestReferences(req)
|
||||
leftoverTags := strset.New()
|
||||
usedTagsAndNames := strset.New()
|
||||
var usedTagGroups []*strset.Set
|
||||
for _, taskGroup := range selectablePkgTaskGroups {
|
||||
selectedTagOrNames, remainingTagsOrNames := tagsOrNamesThatTaskGroupRespondsTo(taskGroup, requestTagsOrNames)
|
||||
leftoverTags = strset.Union(leftoverTags, remainingTagsOrNames)
|
||||
usedTagGroups = append(usedTagGroups, selectedTagOrNames)
|
||||
usedTagsAndNames.Add(selectedTagOrNames.List()...)
|
||||
}
|
||||
|
||||
leftoverTags = strset.Difference(leftoverTags, usedTagsAndNames)
|
||||
leftoverTags.Remove("all")
|
||||
|
||||
if leftoverTags.Size() > 0 {
|
||||
l := leftoverTags.List()
|
||||
sort.Strings(l)
|
||||
return nil, fmt.Errorf("no cataloger tasks respond to the following selections: %v", strings.Join(l, ", "))
|
||||
}
|
||||
|
||||
var newSelections []cataloging.SelectionRequest
|
||||
for _, tags := range usedTagGroups {
|
||||
newSelections = append(newSelections, newSelectionWithTags(req, tags))
|
||||
}
|
||||
|
||||
return newSelections, nil
|
||||
}
|
||||
|
||||
func newSelectionWithTags(req cataloging.SelectionRequest, tags *strset.Set) cataloging.SelectionRequest {
|
||||
return cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: filterTags(req.DefaultNamesOrTags, tags),
|
||||
SubSelectTags: filterTags(req.SubSelectTags, tags),
|
||||
AddNames: filterTags(req.AddNames, tags),
|
||||
RemoveNamesOrTags: filterTags(req.RemoveNamesOrTags, tags),
|
||||
}
|
||||
}
|
||||
|
||||
func filterTags(reqTags []string, filterTags *strset.Set) []string {
|
||||
var filtered []string
|
||||
for _, tag := range reqTags {
|
||||
if filterTags.Has(tag) {
|
||||
filtered = append(filtered, tag)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
func tagsOrNamesThatTaskGroupRespondsTo(tasks []Task, requestTagsOrNames *strset.Set) (*strset.Set, *strset.Set) {
|
||||
positiveRefs := strset.New()
|
||||
for _, t := range tasks {
|
||||
if sel, ok := t.(Selector); ok {
|
||||
positiveRefs.Add("all") // everything responds to "all"
|
||||
positiveRefs.Add(strset.Intersection(requestTagsOrNames, strset.New(sel.Selectors()...)).List()...)
|
||||
}
|
||||
positiveRefs.Add(t.Name())
|
||||
}
|
||||
return positiveRefs, strset.Difference(requestTagsOrNames, positiveRefs)
|
||||
}
|
||||
|
||||
func allRequestReferences(s cataloging.SelectionRequest) *strset.Set {
|
||||
st := strset.New()
|
||||
st.Add(s.DefaultNamesOrTags...)
|
||||
st.Add(s.SubSelectTags...)
|
||||
st.Add(s.AddNames...)
|
||||
st.Add(s.RemoveNamesOrTags...)
|
||||
return st
|
||||
}
|
||||
|
||||
// selectByExpressions the set of tasks to run based on the given expression(s).
|
||||
func selectByExpressions(ts tasks, nodes Expressions) (tasks, Selection) {
|
||||
if len(nodes) == 0 {
|
||||
|
||||
+459
-41
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
@@ -21,45 +21,54 @@ func dummyTask(name string, tags ...string) Task {
|
||||
}
|
||||
|
||||
// note: this test fixture does not need to be kept up to date here, but makes a great test subject
|
||||
func createDummyTasks() tasks {
|
||||
func createDummyPackageTasks() tasks {
|
||||
return []Task{
|
||||
// OS package installed catalogers
|
||||
dummyTask("alpm-db-cataloger", "directory", "installed", "image", "os", "alpm", "archlinux"),
|
||||
dummyTask("apk-db-cataloger", "directory", "installed", "image", "os", "apk", "alpine"),
|
||||
dummyTask("dpkg-db-cataloger", "directory", "installed", "image", "os", "dpkg", "debian"),
|
||||
dummyTask("portage-cataloger", "directory", "installed", "image", "os", "portage", "gentoo"),
|
||||
dummyTask("rpm-db-cataloger", "directory", "installed", "image", "os", "rpm", "redhat"),
|
||||
dummyTask("alpm-db-cataloger", "package", "directory", "installed", "image", "os", "alpm", "archlinux"),
|
||||
dummyTask("apk-db-cataloger", "package", "directory", "installed", "image", "os", "apk", "alpine"),
|
||||
dummyTask("dpkg-db-cataloger", "package", "directory", "installed", "image", "os", "dpkg", "debian"),
|
||||
dummyTask("portage-cataloger", "package", "directory", "installed", "image", "os", "portage", "gentoo"),
|
||||
dummyTask("rpm-db-cataloger", "package", "directory", "installed", "image", "os", "rpm", "redhat"),
|
||||
|
||||
// OS package declared catalogers
|
||||
dummyTask("rpm-archive-cataloger", "declared", "directory", "os", "rpm", "redhat"),
|
||||
dummyTask("rpm-archive-cataloger", "package", "declared", "directory", "os", "rpm", "redhat"),
|
||||
|
||||
// language-specific package installed catalogers
|
||||
dummyTask("conan-info-cataloger", "installed", "image", "language", "cpp", "conan"),
|
||||
dummyTask("javascript-package-cataloger", "installed", "image", "language", "javascript", "node"),
|
||||
dummyTask("php-composer-installed-cataloger", "installed", "image", "language", "php", "composer"),
|
||||
dummyTask("ruby-installed-gemspec-cataloger", "installed", "image", "language", "ruby", "gem", "gemspec"),
|
||||
dummyTask("rust-cargo-lock-cataloger", "installed", "image", "language", "rust", "binary"),
|
||||
dummyTask("conan-info-cataloger", "package", "installed", "image", "language", "cpp", "conan"),
|
||||
dummyTask("javascript-package-cataloger", "package", "installed", "image", "language", "javascript", "node"),
|
||||
dummyTask("php-composer-installed-cataloger", "package", "installed", "image", "language", "php", "composer"),
|
||||
dummyTask("ruby-installed-gemspec-cataloger", "package", "installed", "image", "language", "ruby", "gem", "gemspec"),
|
||||
dummyTask("rust-cargo-lock-cataloger", "package", "installed", "image", "language", "rust", "binary"),
|
||||
|
||||
// language-specific package declared catalogers
|
||||
dummyTask("conan-cataloger", "declared", "directory", "language", "cpp", "conan"),
|
||||
dummyTask("dart-pubspec-lock-cataloger", "declared", "directory", "language", "dart"),
|
||||
dummyTask("dotnet-deps-cataloger", "declared", "directory", "language", "dotnet", "c#"),
|
||||
dummyTask("elixir-mix-lock-cataloger", "declared", "directory", "language", "elixir"),
|
||||
dummyTask("erlang-rebar-lock-cataloger", "declared", "directory", "language", "erlang"),
|
||||
dummyTask("javascript-lock-cataloger", "declared", "directory", "language", "javascript", "node", "npm"),
|
||||
dummyTask("conan-cataloger", "package", "declared", "directory", "language", "cpp", "conan"),
|
||||
dummyTask("dart-pubspec-lock-cataloger", "package", "declared", "directory", "language", "dart"),
|
||||
dummyTask("dotnet-deps-cataloger", "package", "declared", "directory", "language", "dotnet", "c#"),
|
||||
dummyTask("elixir-mix-lock-cataloger", "package", "declared", "directory", "language", "elixir"),
|
||||
dummyTask("erlang-rebar-lock-cataloger", "package", "declared", "directory", "language", "erlang"),
|
||||
dummyTask("javascript-lock-cataloger", "package", "declared", "directory", "language", "javascript", "node", "npm"),
|
||||
|
||||
// language-specific package for both image and directory scans (but not necessarily declared)
|
||||
dummyTask("dotnet-portable-executable-cataloger", "directory", "installed", "image", "language", "dotnet", "c#"),
|
||||
dummyTask("python-installed-package-cataloger", "directory", "installed", "image", "language", "python"),
|
||||
dummyTask("go-module-binary-cataloger", "directory", "installed", "image", "language", "go", "golang", "gomod", "binary"),
|
||||
dummyTask("java-archive-cataloger", "directory", "installed", "image", "language", "java", "maven"),
|
||||
dummyTask("graalvm-native-image-cataloger", "directory", "installed", "image", "language", "java"),
|
||||
dummyTask("dotnet-portable-executable-cataloger", "package", "directory", "installed", "image", "language", "dotnet", "c#"),
|
||||
dummyTask("python-installed-package-cataloger", "package", "directory", "installed", "image", "language", "python"),
|
||||
dummyTask("go-module-binary-cataloger", "package", "directory", "installed", "image", "language", "go", "golang", "gomod", "binary"),
|
||||
dummyTask("java-archive-cataloger", "package", "directory", "installed", "image", "language", "java", "maven"),
|
||||
dummyTask("graalvm-native-image-cataloger", "package", "directory", "installed", "image", "language", "java"),
|
||||
|
||||
// other package catalogers
|
||||
dummyTask("binary-cataloger", "declared", "directory", "image", "binary"),
|
||||
dummyTask("github-actions-usage-cataloger", "declared", "directory", "github", "github-actions"),
|
||||
dummyTask("github-action-workflow-usage-cataloger", "declared", "directory", "github", "github-actions"),
|
||||
dummyTask("sbom-cataloger", "declared", "directory", "image", "sbom"),
|
||||
dummyTask("binary-cataloger", "package", "declared", "directory", "image", "binary"),
|
||||
dummyTask("github-actions-usage-cataloger", "package", "declared", "directory", "github", "github-actions"),
|
||||
dummyTask("github-action-workflow-usage-cataloger", "package", "declared", "directory", "github", "github-actions"),
|
||||
dummyTask("sbom-cataloger", "package", "declared", "directory", "image", "sbom"),
|
||||
}
|
||||
}
|
||||
|
||||
func createDummyFileTasks() tasks {
|
||||
return []Task{
|
||||
dummyTask("file-content-cataloger", "file", "content"),
|
||||
dummyTask("file-metadata-cataloger", "file", "metadata"),
|
||||
dummyTask("file-digest-cataloger", "file", "digest"),
|
||||
dummyTask("file-executable-cataloger", "file", "binary-metadata"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +81,7 @@ func TestSelect(t *testing.T) {
|
||||
expressions []string
|
||||
wantNames []string
|
||||
wantTokens map[string]TokenSelection
|
||||
wantRequest pkgcataloging.SelectionRequest
|
||||
wantRequest cataloging.SelectionRequest
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
@@ -82,11 +91,11 @@ func TestSelect(t *testing.T) {
|
||||
expressions: []string{},
|
||||
wantNames: []string{},
|
||||
wantTokens: map[string]TokenSelection{},
|
||||
wantRequest: pkgcataloging.SelectionRequest{},
|
||||
wantRequest: cataloging.SelectionRequest{},
|
||||
},
|
||||
{
|
||||
name: "use default tasks",
|
||||
allTasks: createDummyTasks(),
|
||||
allTasks: createDummyPackageTasks(),
|
||||
basis: []string{
|
||||
"image",
|
||||
},
|
||||
@@ -129,13 +138,13 @@ func TestSelect(t *testing.T) {
|
||||
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
},
|
||||
wantRequest: pkgcataloging.SelectionRequest{
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "select, add, and remove tasks",
|
||||
allTasks: createDummyTasks(),
|
||||
allTasks: createDummyPackageTasks(),
|
||||
basis: []string{
|
||||
"image",
|
||||
},
|
||||
@@ -175,7 +184,7 @@ func TestSelect(t *testing.T) {
|
||||
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
},
|
||||
wantRequest: pkgcataloging.SelectionRequest{
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
SubSelectTags: []string{"os"},
|
||||
RemoveNamesOrTags: []string{"dpkg"},
|
||||
@@ -184,7 +193,7 @@ func TestSelect(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "allow for partial selections",
|
||||
allTasks: createDummyTasks(),
|
||||
allTasks: createDummyPackageTasks(),
|
||||
basis: []string{
|
||||
"image",
|
||||
},
|
||||
@@ -228,7 +237,7 @@ func TestSelect(t *testing.T) {
|
||||
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
},
|
||||
wantRequest: pkgcataloging.SelectionRequest{
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
SubSelectTags: []string{"os", "rust-cargo-lock-cataloger"},
|
||||
RemoveNamesOrTags: []string{"dpkg"},
|
||||
@@ -238,7 +247,7 @@ func TestSelect(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "select all tasks",
|
||||
allTasks: createDummyTasks(),
|
||||
allTasks: createDummyPackageTasks(),
|
||||
basis: []string{
|
||||
"all",
|
||||
},
|
||||
@@ -299,13 +308,13 @@ func TestSelect(t *testing.T) {
|
||||
"github-action-workflow-usage-cataloger": newTokenSelection([]string{"all"}, nil),
|
||||
"sbom-cataloger": newTokenSelection([]string{"all"}, nil),
|
||||
},
|
||||
wantRequest: pkgcataloging.SelectionRequest{
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"all"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "set default with multiple tags",
|
||||
allTasks: createDummyTasks(),
|
||||
allTasks: createDummyPackageTasks(),
|
||||
basis: []string{
|
||||
"gemspec",
|
||||
"python",
|
||||
@@ -319,10 +328,31 @@ func TestSelect(t *testing.T) {
|
||||
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"gemspec"}, nil),
|
||||
"python-installed-package-cataloger": newTokenSelection([]string{"python"}, nil),
|
||||
},
|
||||
wantRequest: pkgcataloging.SelectionRequest{
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"gemspec", "python"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "automatically add file to default tags",
|
||||
allTasks: createDummyFileTasks(),
|
||||
basis: []string{},
|
||||
expressions: []string{},
|
||||
wantNames: []string{
|
||||
"file-content-cataloger",
|
||||
"file-metadata-cataloger",
|
||||
"file-digest-cataloger",
|
||||
"file-executable-cataloger",
|
||||
},
|
||||
wantTokens: map[string]TokenSelection{
|
||||
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
},
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"file"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@@ -330,7 +360,7 @@ func TestSelect(t *testing.T) {
|
||||
tt.wantErr = assert.NoError
|
||||
}
|
||||
|
||||
req := pkgcataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
|
||||
req := cataloging.NewSelectionRequest().WithDefaults(tt.basis...).WithExpression(tt.expressions...)
|
||||
|
||||
got, gotEvidence, err := Select(tt.allTasks, req)
|
||||
tt.wantErr(t, err)
|
||||
@@ -367,3 +397,391 @@ func TestSelect(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSelectInGroups(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
taskGroups [][]Task
|
||||
selectionReq cataloging.SelectionRequest
|
||||
wantGroups [][]string
|
||||
wantTokens map[string]TokenSelection
|
||||
wantRequest cataloging.SelectionRequest
|
||||
wantErr assert.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "select only within the file tasks (leave package tasks alone)",
|
||||
taskGroups: [][]Task{
|
||||
createDummyPackageTasks(),
|
||||
createDummyFileTasks(),
|
||||
},
|
||||
selectionReq: cataloging.NewSelectionRequest().
|
||||
WithDefaults("image"). // note: file missing
|
||||
WithSubSelections("content", "digest"),
|
||||
wantGroups: [][]string{
|
||||
{
|
||||
// this is the original, untouched package task list
|
||||
"alpm-db-cataloger",
|
||||
"apk-db-cataloger",
|
||||
"dpkg-db-cataloger",
|
||||
"portage-cataloger",
|
||||
"rpm-db-cataloger",
|
||||
"conan-info-cataloger",
|
||||
"javascript-package-cataloger",
|
||||
"php-composer-installed-cataloger",
|
||||
"ruby-installed-gemspec-cataloger",
|
||||
"rust-cargo-lock-cataloger",
|
||||
"dotnet-portable-executable-cataloger",
|
||||
"python-installed-package-cataloger",
|
||||
"go-module-binary-cataloger",
|
||||
"java-archive-cataloger",
|
||||
"graalvm-native-image-cataloger",
|
||||
"binary-cataloger",
|
||||
"sbom-cataloger",
|
||||
},
|
||||
{
|
||||
// this has been filtered based on the request
|
||||
"file-content-cataloger",
|
||||
"file-digest-cataloger",
|
||||
},
|
||||
},
|
||||
wantTokens: map[string]TokenSelection{
|
||||
// packages
|
||||
"alpm-db-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"apk-db-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"conan-info-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"dpkg-db-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"java-archive-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"javascript-package-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"portage-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"rpm-db-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
// files
|
||||
"file-content-cataloger": newTokenSelection([]string{"content", "file"}, nil),
|
||||
"file-digest-cataloger": newTokenSelection([]string{"digest", "file"}, nil),
|
||||
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
},
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"}, // note: file automatically added
|
||||
SubSelectTags: []string{"content", "digest"},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "select package tasks (leave file tasks alone)",
|
||||
taskGroups: [][]Task{
|
||||
createDummyPackageTasks(),
|
||||
createDummyFileTasks(),
|
||||
},
|
||||
selectionReq: cataloging.NewSelectionRequest().WithDefaults("image").WithSubSelections("os"),
|
||||
wantGroups: [][]string{
|
||||
{
|
||||
// filtered based on the request
|
||||
"alpm-db-cataloger",
|
||||
"apk-db-cataloger",
|
||||
"dpkg-db-cataloger",
|
||||
"portage-cataloger",
|
||||
"rpm-db-cataloger",
|
||||
},
|
||||
{
|
||||
// this is the original, untouched file task list
|
||||
"file-content-cataloger",
|
||||
"file-metadata-cataloger",
|
||||
"file-digest-cataloger",
|
||||
"file-executable-cataloger",
|
||||
},
|
||||
},
|
||||
wantTokens: map[string]TokenSelection{
|
||||
// packages - os
|
||||
"alpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
"apk-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
"rpm-archive-cataloger": newTokenSelection([]string{"os"}, nil),
|
||||
"rpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
"portage-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
"dpkg-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
// packages - remaining
|
||||
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"conan-info-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"java-archive-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"javascript-package-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
// files
|
||||
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
},
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
SubSelectTags: []string{"os"},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "select only file tasks (default)",
|
||||
taskGroups: [][]Task{
|
||||
createDummyPackageTasks(),
|
||||
createDummyFileTasks(),
|
||||
},
|
||||
selectionReq: cataloging.NewSelectionRequest().WithDefaults("file"),
|
||||
wantGroups: [][]string{
|
||||
// filtered based on the request
|
||||
nil,
|
||||
{
|
||||
// this is the original, untouched file task list
|
||||
"file-content-cataloger",
|
||||
"file-metadata-cataloger",
|
||||
"file-digest-cataloger",
|
||||
"file-executable-cataloger",
|
||||
},
|
||||
},
|
||||
wantTokens: map[string]TokenSelection{
|
||||
// files
|
||||
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
},
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"file"},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "select only file tasks (via removal of package)",
|
||||
taskGroups: [][]Task{
|
||||
createDummyPackageTasks(),
|
||||
createDummyFileTasks(),
|
||||
},
|
||||
selectionReq: cataloging.NewSelectionRequest().WithDefaults("file", "image").WithRemovals("package"),
|
||||
wantGroups: [][]string{
|
||||
// filtered based on the request
|
||||
nil,
|
||||
{
|
||||
// this is the original, untouched file task list
|
||||
"file-content-cataloger",
|
||||
"file-metadata-cataloger",
|
||||
"file-digest-cataloger",
|
||||
"file-executable-cataloger",
|
||||
},
|
||||
},
|
||||
wantTokens: map[string]TokenSelection{
|
||||
// packages
|
||||
"alpm-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"apk-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"binary-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"conan-info-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"dpkg-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"java-archive-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"javascript-package-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"portage-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"rpm-db-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"sbom-cataloger": newTokenSelection([]string{"image"}, []string{"package"}),
|
||||
"rpm-archive-cataloger": newTokenSelection(nil, []string{"package"}),
|
||||
"conan-cataloger": newTokenSelection(nil, []string{"package"}),
|
||||
"dart-pubspec-lock-cataloger": newTokenSelection(nil, []string{"package"}),
|
||||
"dotnet-deps-cataloger": newTokenSelection(nil, []string{"package"}),
|
||||
"elixir-mix-lock-cataloger": newTokenSelection(nil, []string{"package"}),
|
||||
"erlang-rebar-lock-cataloger": newTokenSelection(nil, []string{"package"}),
|
||||
"javascript-lock-cataloger": newTokenSelection(nil, []string{"package"}),
|
||||
"github-actions-usage-cataloger": newTokenSelection(nil, []string{"package"}),
|
||||
"github-action-workflow-usage-cataloger": newTokenSelection(nil, []string{"package"}),
|
||||
// files
|
||||
"file-content-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-digest-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
},
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"file", "image"},
|
||||
RemoveNamesOrTags: []string{"package"},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "select file and package tasks",
|
||||
taskGroups: [][]Task{
|
||||
createDummyPackageTasks(),
|
||||
createDummyFileTasks(),
|
||||
},
|
||||
selectionReq: cataloging.NewSelectionRequest().
|
||||
WithDefaults("image").
|
||||
WithSubSelections("os", "content", "digest"),
|
||||
wantGroups: [][]string{
|
||||
{
|
||||
// filtered based on the request
|
||||
"alpm-db-cataloger",
|
||||
"apk-db-cataloger",
|
||||
"dpkg-db-cataloger",
|
||||
"portage-cataloger",
|
||||
"rpm-db-cataloger",
|
||||
},
|
||||
{
|
||||
// filtered based on the request
|
||||
"file-content-cataloger",
|
||||
"file-digest-cataloger",
|
||||
},
|
||||
},
|
||||
wantTokens: map[string]TokenSelection{
|
||||
// packages - os
|
||||
"alpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
"apk-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
"rpm-archive-cataloger": newTokenSelection([]string{"os"}, nil),
|
||||
"rpm-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
"portage-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
"dpkg-db-cataloger": newTokenSelection([]string{"os", "image"}, nil),
|
||||
// packages - remaining
|
||||
"binary-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"conan-info-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"dotnet-portable-executable-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"go-module-binary-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"graalvm-native-image-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"java-archive-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"javascript-package-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"php-composer-installed-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"python-installed-package-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"ruby-installed-gemspec-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"rust-cargo-lock-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
"sbom-cataloger": newTokenSelection([]string{"image"}, nil),
|
||||
// files
|
||||
"file-content-cataloger": newTokenSelection([]string{"file", "content"}, nil), // note extra tags
|
||||
"file-digest-cataloger": newTokenSelection([]string{"file", "digest"}, nil), // note extra tags
|
||||
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-metadata-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
},
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
SubSelectTags: []string{"os", "content", "digest"},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "complex selection with multiple operators across groups",
|
||||
taskGroups: [][]Task{
|
||||
createDummyPackageTasks(),
|
||||
createDummyFileTasks(),
|
||||
},
|
||||
selectionReq: cataloging.NewSelectionRequest().
|
||||
WithDefaults("os"). // note: no file tag present
|
||||
WithExpression("+github-actions-usage-cataloger", "-dpkg", "-digest", "content", "+file-metadata-cataloger", "-declared"),
|
||||
wantGroups: [][]string{
|
||||
{
|
||||
"alpm-db-cataloger",
|
||||
"apk-db-cataloger",
|
||||
"portage-cataloger",
|
||||
"rpm-db-cataloger",
|
||||
"github-actions-usage-cataloger",
|
||||
},
|
||||
{
|
||||
"file-content-cataloger",
|
||||
"file-metadata-cataloger",
|
||||
},
|
||||
},
|
||||
wantTokens: map[string]TokenSelection{
|
||||
// selected package tasks
|
||||
"alpm-db-cataloger": newTokenSelection([]string{"os"}, nil),
|
||||
"apk-db-cataloger": newTokenSelection([]string{"os"}, nil),
|
||||
"dpkg-db-cataloger": newTokenSelection([]string{"os"}, []string{"dpkg"}),
|
||||
"portage-cataloger": newTokenSelection([]string{"os"}, nil),
|
||||
"rpm-archive-cataloger": newTokenSelection([]string{"os"}, []string{"declared"}),
|
||||
"rpm-db-cataloger": newTokenSelection([]string{"os"}, nil),
|
||||
"github-actions-usage-cataloger": newTokenSelection([]string{"github-actions-usage-cataloger"}, []string{"declared"}),
|
||||
|
||||
// selected file tasks
|
||||
"file-content-cataloger": newTokenSelection([]string{"content", "file"}, nil),
|
||||
"file-metadata-cataloger": newTokenSelection([]string{"file-metadata-cataloger", "file"}, nil),
|
||||
|
||||
// removed package tasks
|
||||
"binary-cataloger": newTokenSelection(nil, []string{"declared"}),
|
||||
"conan-cataloger": newTokenSelection(nil, []string{"declared"}),
|
||||
"dart-pubspec-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
|
||||
"dotnet-deps-cataloger": newTokenSelection(nil, []string{"declared"}),
|
||||
"elixir-mix-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
|
||||
"erlang-rebar-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
|
||||
"github-action-workflow-usage-cataloger": newTokenSelection(nil, []string{"declared"}),
|
||||
"javascript-lock-cataloger": newTokenSelection(nil, []string{"declared"}),
|
||||
"sbom-cataloger": newTokenSelection(nil, []string{"declared"}),
|
||||
|
||||
// removed file tasks
|
||||
"file-executable-cataloger": newTokenSelection([]string{"file"}, nil),
|
||||
"file-digest-cataloger": newTokenSelection([]string{"file"}, []string{"digest"}),
|
||||
},
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"os", "file"}, // note: file added automatically
|
||||
SubSelectTags: []string{"content"},
|
||||
RemoveNamesOrTags: []string{"dpkg", "digest", "declared"},
|
||||
AddNames: []string{"github-actions-usage-cataloger", "file-metadata-cataloger"},
|
||||
},
|
||||
wantErr: assert.NoError,
|
||||
},
|
||||
{
|
||||
name: "invalid tag",
|
||||
taskGroups: [][]Task{
|
||||
createDummyPackageTasks(),
|
||||
createDummyFileTasks(),
|
||||
},
|
||||
selectionReq: cataloging.NewSelectionRequest().WithDefaults("invalid"),
|
||||
wantGroups: nil,
|
||||
wantTokens: nil,
|
||||
wantRequest: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"invalid", "file"},
|
||||
},
|
||||
wantErr: assert.Error,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.wantErr == nil {
|
||||
tt.wantErr = assert.NoError
|
||||
}
|
||||
|
||||
gotGroups, gotSelection, err := SelectInGroups(tt.taskGroups, tt.selectionReq)
|
||||
tt.wantErr(t, err)
|
||||
if err != nil {
|
||||
// dev note: this is useful for debugging when needed...
|
||||
//for _, e := range gotEvidence.Request.Expressions {
|
||||
// t.Logf("expression (errors %q): %#v", e.Errors, e)
|
||||
//}
|
||||
|
||||
// note: we DON'T bail early in validations... this is because we should always return the full set of
|
||||
// of selected tasks and surrounding evidence.
|
||||
}
|
||||
|
||||
var gotGroupNames [][]string
|
||||
for _, group := range gotGroups {
|
||||
var names []string
|
||||
for _, task := range group {
|
||||
names = append(names, task.Name())
|
||||
}
|
||||
gotGroupNames = append(gotGroupNames, names)
|
||||
}
|
||||
|
||||
assert.Equal(t, tt.wantGroups, gotGroupNames)
|
||||
assert.Equal(t, tt.wantTokens, gotSelection.TokensByTask)
|
||||
assert.Equal(t, tt.wantRequest, gotSelection.Request)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/archiver/v3"
|
||||
|
||||
"github.com/anchore/archiver/v3"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
|
||||
@@ -23,7 +23,7 @@ func ProcessPathErrors(err error) error {
|
||||
}
|
||||
}
|
||||
unknowns, remainingErrors := ExtractCoordinateErrors(err)
|
||||
log.Warn(remainingErrors)
|
||||
log.Debug(remainingErrors)
|
||||
|
||||
var out []error
|
||||
for _, u := range unknowns {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
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.0.18/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.22/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@@ -218,6 +218,44 @@
|
||||
"matches"
|
||||
]
|
||||
},
|
||||
"BitnamiSbomEntry": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"arch": {
|
||||
"type": "string"
|
||||
},
|
||||
"distro": {
|
||||
"type": "string"
|
||||
},
|
||||
"revision": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"path": {
|
||||
"type": "string"
|
||||
},
|
||||
"files": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"arch",
|
||||
"distro",
|
||||
"revision",
|
||||
"version",
|
||||
"path",
|
||||
"files"
|
||||
]
|
||||
},
|
||||
"CConanFileEntry": {
|
||||
"properties": {
|
||||
"ref": {
|
||||
@@ -492,6 +530,29 @@
|
||||
"hashPath"
|
||||
]
|
||||
},
|
||||
"DotnetPackagesLockEntry": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"contentHash": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"version",
|
||||
"contentHash",
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"DotnetPortableExecutableEntry": {
|
||||
"properties": {
|
||||
"assemblyVersion": {
|
||||
@@ -1265,6 +1326,9 @@
|
||||
"$ref": "#/$defs/Location"
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"contents": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -1627,6 +1691,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/BinarySignature"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/BitnamiSbomEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/CConanFileEntry"
|
||||
},
|
||||
@@ -1648,6 +1715,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/DotnetDepsEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DotnetPackagesLockEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/DotnetPortableExecutableEntry"
|
||||
},
|
||||
@@ -1756,6 +1826,9 @@
|
||||
{
|
||||
"$ref": "#/$defs/SwiplpackPackage"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/TerraformLockProviderEntry"
|
||||
},
|
||||
{
|
||||
"$ref": "#/$defs/WordpressPluginEntry"
|
||||
}
|
||||
@@ -2694,6 +2767,32 @@
|
||||
"dependencies"
|
||||
]
|
||||
},
|
||||
"TerraformLockProviderEntry": {
|
||||
"properties": {
|
||||
"url": {
|
||||
"type": "string"
|
||||
},
|
||||
"constraints": {
|
||||
"type": "string"
|
||||
},
|
||||
"version": {
|
||||
"type": "string"
|
||||
},
|
||||
"hashes": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
"required": [
|
||||
"url",
|
||||
"constraints",
|
||||
"version",
|
||||
"hashes"
|
||||
]
|
||||
},
|
||||
"WordpressPluginEntry": {
|
||||
"properties": {
|
||||
"pluginInstallDirectory": {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
package filecataloging
|
||||
|
||||
const (
|
||||
// FileTag should be used to identify catalogers that primarily discover information about files (as opposed to packages).
|
||||
FileTag = "file"
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
package cataloging
|
||||
|
||||
import "github.com/anchore/syft/internal/licenses"
|
||||
|
||||
type LicenseConfig struct {
|
||||
IncludeUnkownLicenseContent bool `json:"include-unknown-license-content" yaml:"include-unknown-license-content" mapstructure:"include-unknown-license-content"`
|
||||
Coverage float64 `json:"coverage" yaml:"coverage" mapstructure:"coverage"`
|
||||
}
|
||||
|
||||
func DefaultLicenseConfig() LicenseConfig {
|
||||
return LicenseConfig{
|
||||
IncludeUnkownLicenseContent: licenses.DefaultIncludeLicenseContent,
|
||||
Coverage: licenses.DefaultCoverageThreshold,
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package pkgcataloging
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/binary"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/dotnet"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/golang"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/java"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
|
||||
type Config struct {
|
||||
Binary binary.ClassifierCatalogerConfig `yaml:"binary" json:"binary" mapstructure:"binary"`
|
||||
Dotnet dotnet.CatalogerConfig `yaml:"dotnet" json:"dotnet" mapstructure:"dotnet"`
|
||||
Golang golang.CatalogerConfig `yaml:"golang" json:"golang" mapstructure:"golang"`
|
||||
JavaArchive java.ArchiveCatalogerConfig `yaml:"java-archive" json:"java-archive" mapstructure:"java-archive"`
|
||||
JavaScript javascript.CatalogerConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"`
|
||||
@@ -21,6 +23,7 @@ type Config struct {
|
||||
func DefaultConfig() Config {
|
||||
return Config{
|
||||
Binary: binary.DefaultClassifierCatalogerConfig(),
|
||||
Dotnet: dotnet.DefaultCatalogerConfig(),
|
||||
Golang: golang.DefaultCatalogerConfig(),
|
||||
LinuxKernel: kernel.DefaultLinuxKernelCatalogerConfig(),
|
||||
Python: python.DefaultCatalogerConfig(),
|
||||
@@ -33,6 +36,11 @@ func (c Config) WithBinaryConfig(cfg binary.ClassifierCatalogerConfig) Config {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c Config) WithDotnetConfig(cfg dotnet.CatalogerConfig) Config {
|
||||
c.Dotnet = cfg
|
||||
return c
|
||||
}
|
||||
|
||||
func (c Config) WithGolangConfig(cfg golang.CatalogerConfig) Config {
|
||||
c.Golang = cfg
|
||||
return c
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package pkgcataloging
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
)
|
||||
|
||||
// SelectionRequest is deprecated: use cataloging.SelectionRequest instead
|
||||
type SelectionRequest = cataloging.SelectionRequest
|
||||
|
||||
// NewSelectionRequest is deprecated: use cataloging.NewSelectionRequest instead
|
||||
var NewSelectionRequest = cataloging.NewSelectionRequest
|
||||
@@ -1,8 +1,6 @@
|
||||
package pkgcataloging
|
||||
package cataloging
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
import "strings"
|
||||
|
||||
type SelectionRequest struct {
|
||||
DefaultNamesOrTags []string `json:"default,omitempty"`
|
||||
@@ -50,6 +48,10 @@ func (s SelectionRequest) WithRemovals(nameOrTags ...string) SelectionRequest {
|
||||
return s
|
||||
}
|
||||
|
||||
func (s SelectionRequest) IsEmpty() bool {
|
||||
return len(s.AddNames) == 0 && len(s.RemoveNamesOrTags) == 0 && len(s.SubSelectTags) == 0 && len(s.DefaultNamesOrTags) == 0
|
||||
}
|
||||
|
||||
func cleanSelection(tags []string) []string {
|
||||
var cleaned []string
|
||||
for _, tag := range tags {
|
||||
@@ -16,13 +16,14 @@ type configurationAuditTrail struct {
|
||||
DataGeneration cataloging.DataGenerationConfig `json:"data-generation" yaml:"data-generation" mapstructure:"data-generation"`
|
||||
Packages pkgcataloging.Config `json:"packages" yaml:"packages" mapstructure:"packages"`
|
||||
Files filecataloging.Config `json:"files" yaml:"files" mapstructure:"files"`
|
||||
Licenses cataloging.LicenseConfig `json:"licenses" yaml:"licenses" mapstructure:"licenses"`
|
||||
Catalogers catalogerManifest `json:"catalogers" yaml:"catalogers" mapstructure:"catalogers"`
|
||||
ExtraConfigs any `json:"extra,omitempty" yaml:"extra" mapstructure:"extra"`
|
||||
}
|
||||
|
||||
type catalogerManifest struct {
|
||||
Requested pkgcataloging.SelectionRequest `json:"requested" yaml:"requested" mapstructure:"requested"`
|
||||
Used []string `json:"used" yaml:"used" mapstructure:"used"`
|
||||
Requested cataloging.SelectionRequest `json:"requested" yaml:"requested" mapstructure:"requested"`
|
||||
Used []string `json:"used" yaml:"used" mapstructure:"used"`
|
||||
}
|
||||
|
||||
type marshalAPIConfiguration configurationAuditTrail
|
||||
|
||||
+13
-2
@@ -52,6 +52,7 @@ func CreateSBOM(ctx context.Context, src source.Source, cfg *CreateSBOMConfig) (
|
||||
DataGeneration: cfg.DataGeneration,
|
||||
Packages: cfg.Packages,
|
||||
Files: cfg.Files,
|
||||
Licenses: cfg.Licenses,
|
||||
Catalogers: *audit,
|
||||
ExtraConfigs: cfg.ToolConfiguration,
|
||||
},
|
||||
@@ -61,8 +62,15 @@ func CreateSBOM(ctx context.Context, src source.Source, cfg *CreateSBOMConfig) (
|
||||
},
|
||||
}
|
||||
|
||||
// inject a single license scanner for all package cataloging tasks into context
|
||||
ctx = licenses.SetContextLicenseScanner(ctx, licenses.NewDefaultScanner())
|
||||
// inject a single license scanner and content config for all package cataloging tasks into context
|
||||
licenseScanner, err := licenses.NewDefaultScanner(
|
||||
licenses.WithIncludeLicenseContent(cfg.Licenses.IncludeUnkownLicenseContent),
|
||||
licenses.WithCoverage(cfg.Licenses.Coverage),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not build licenseScanner for cataloging: %w", err)
|
||||
}
|
||||
ctx = licenses.SetContextLicenseScanner(ctx, licenseScanner)
|
||||
|
||||
catalogingProgress := monitorCatalogingTask(src.ID(), taskGroups)
|
||||
packageCatalogingProgress := monitorPackageCatalogingTask()
|
||||
@@ -125,6 +133,9 @@ func monitorCatalogingTask(srcID artifact.ID, tasks [][]task.Task) *monitor.Cata
|
||||
func formatTaskNames(tasks []task.Task) []string {
|
||||
set := strset.New()
|
||||
for _, td := range tasks {
|
||||
if td == nil {
|
||||
continue
|
||||
}
|
||||
set.Add(td.Name())
|
||||
}
|
||||
list := set.List()
|
||||
|
||||
+106
-47
@@ -7,6 +7,7 @@ import (
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/task"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/cataloging/filecataloging"
|
||||
@@ -25,16 +26,17 @@ type CreateSBOMConfig struct {
|
||||
Unknowns cataloging.UnknownsConfig
|
||||
DataGeneration cataloging.DataGenerationConfig
|
||||
Packages pkgcataloging.Config
|
||||
Licenses cataloging.LicenseConfig
|
||||
Files filecataloging.Config
|
||||
Parallelism int
|
||||
CatalogerSelection pkgcataloging.SelectionRequest
|
||||
CatalogerSelection cataloging.SelectionRequest
|
||||
|
||||
// audit what tool is being used to generate the SBOM
|
||||
ToolName string
|
||||
ToolVersion string
|
||||
ToolConfiguration interface{}
|
||||
|
||||
packageTaskFactories task.PackageTaskFactories
|
||||
packageTaskFactories task.Factories
|
||||
packageCatalogerReferences []pkgcataloging.CatalogerReference
|
||||
}
|
||||
|
||||
@@ -45,6 +47,7 @@ func DefaultCreateSBOMConfig() *CreateSBOMConfig {
|
||||
Relationships: cataloging.DefaultRelationshipsConfig(),
|
||||
DataGeneration: cataloging.DefaultDataGenerationConfig(),
|
||||
Packages: pkgcataloging.DefaultConfig(),
|
||||
Licenses: cataloging.DefaultLicenseConfig(),
|
||||
Files: filecataloging.DefaultConfig(),
|
||||
Parallelism: 1,
|
||||
packageTaskFactories: task.DefaultPackageTaskFactories(),
|
||||
@@ -133,6 +136,12 @@ func (c *CreateSBOMConfig) WithPackagesConfig(cfg pkgcataloging.Config) *CreateS
|
||||
return c
|
||||
}
|
||||
|
||||
// WithPackagesConfig allows for defining any specific behavior for syft-implemented catalogers.
|
||||
func (c *CreateSBOMConfig) WithLicenseConfig(cfg cataloging.LicenseConfig) *CreateSBOMConfig {
|
||||
c.Licenses = cfg
|
||||
return c
|
||||
}
|
||||
|
||||
// WithFilesConfig allows for defining file-based cataloging parameters.
|
||||
func (c *CreateSBOMConfig) WithFilesConfig(cfg filecataloging.Config) *CreateSBOMConfig {
|
||||
c.Files = cfg
|
||||
@@ -149,7 +158,7 @@ func (c *CreateSBOMConfig) WithoutFiles() *CreateSBOMConfig {
|
||||
}
|
||||
|
||||
// WithCatalogerSelection allows for adding to, removing from, or sub-selecting the final set of catalogers by name or tag.
|
||||
func (c *CreateSBOMConfig) WithCatalogerSelection(selection pkgcataloging.SelectionRequest) *CreateSBOMConfig {
|
||||
func (c *CreateSBOMConfig) WithCatalogerSelection(selection cataloging.SelectionRequest) *CreateSBOMConfig {
|
||||
c.CatalogerSelection = selection
|
||||
return c
|
||||
}
|
||||
@@ -165,6 +174,10 @@ func (c *CreateSBOMConfig) WithoutCatalogers() *CreateSBOMConfig {
|
||||
// WithCatalogers allows for adding user-provided catalogers to the final set of catalogers that will always be run
|
||||
// regardless of the source type or any cataloger selections provided.
|
||||
func (c *CreateSBOMConfig) WithCatalogers(catalogerRefs ...pkgcataloging.CatalogerReference) *CreateSBOMConfig {
|
||||
for i := range catalogerRefs {
|
||||
// ensure that all package catalogers have the package tag
|
||||
catalogerRefs[i].Tags = append(catalogerRefs[i].Tags, pkgcataloging.PackageTag)
|
||||
}
|
||||
c.packageCatalogerReferences = append(c.packageCatalogerReferences, catalogerRefs...)
|
||||
|
||||
return c
|
||||
@@ -181,8 +194,8 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
|
||||
environmentTasks := c.environmentTasks()
|
||||
relationshipsTasks := c.relationshipTasks(src)
|
||||
unknownTasks := c.unknownsTasks()
|
||||
fileTasks := c.fileTasks()
|
||||
pkgTasks, selectionEvidence, err := c.packageTasks(src)
|
||||
|
||||
pkgTasks, fileTasks, selectionEvidence, err := c.selectTasks(src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -213,79 +226,117 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
|
||||
taskGroups...,
|
||||
)
|
||||
|
||||
var allTasks []task.Task
|
||||
allTasks = append(allTasks, pkgTasks...)
|
||||
allTasks = append(allTasks, fileTasks...)
|
||||
|
||||
return taskGroups, &catalogerManifest{
|
||||
Requested: selectionEvidence.Request,
|
||||
Used: formatTaskNames(pkgTasks),
|
||||
Used: formatTaskNames(allTasks),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// fileTasks returns the set of tasks that should be run to catalog files.
|
||||
func (c *CreateSBOMConfig) fileTasks() []task.Task {
|
||||
var tsks []task.Task
|
||||
|
||||
if t := task.NewFileDigestCatalogerTask(c.Files.Selection, c.Files.Hashers...); t != nil {
|
||||
tsks = append(tsks, t)
|
||||
}
|
||||
if t := task.NewFileMetadataCatalogerTask(c.Files.Selection); t != nil {
|
||||
tsks = append(tsks, t)
|
||||
}
|
||||
if t := task.NewFileContentCatalogerTask(c.Files.Content); t != nil {
|
||||
tsks = append(tsks, t)
|
||||
}
|
||||
if t := task.NewExecutableCatalogerTask(c.Files.Selection, c.Files.Executable); t != nil {
|
||||
tsks = append(tsks, t)
|
||||
func (c *CreateSBOMConfig) fileTasks(cfg task.CatalogingFactoryConfig) ([]task.Task, error) {
|
||||
tsks, err := task.DefaultFileTaskFactories().Tasks(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create file cataloger tasks: %w", err)
|
||||
}
|
||||
|
||||
return tsks
|
||||
return tsks, nil
|
||||
}
|
||||
|
||||
// packageTasks returns the set of tasks that should be run to catalog packages.
|
||||
func (c *CreateSBOMConfig) packageTasks(src source.Description) ([]task.Task, *task.Selection, error) {
|
||||
// selectTasks returns the set of tasks that should be run to catalog packages and files.
|
||||
func (c *CreateSBOMConfig) selectTasks(src source.Description) ([]task.Task, []task.Task, *task.Selection, error) {
|
||||
cfg := task.CatalogingFactoryConfig{
|
||||
SearchConfig: c.Search,
|
||||
RelationshipsConfig: c.Relationships,
|
||||
DataGenerationConfig: c.DataGeneration,
|
||||
PackagesConfig: c.Packages,
|
||||
ComplianceConfig: c.Compliance,
|
||||
FilesConfig: c.Files,
|
||||
}
|
||||
|
||||
persistentTasks, selectableTasks, err := c.allPackageTasks(cfg)
|
||||
persistentPkgTasks, selectablePkgTasks, err := c.allPackageTasks(cfg)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to create package cataloger tasks: %w", err)
|
||||
return nil, nil, nil, fmt.Errorf("unable to create package cataloger tasks: %w", err)
|
||||
}
|
||||
|
||||
req, err := finalSelectionRequest(c.CatalogerSelection, src)
|
||||
req, err := finalTaskSelectionRequest(c.CatalogerSelection, src)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
finalTasks, selection, err := task.Select(selectableTasks, *req)
|
||||
selectableFileTasks, err := c.fileTasks(cfg)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
finalTasks = append(finalTasks, persistentTasks...)
|
||||
|
||||
if len(finalTasks) == 0 {
|
||||
return nil, nil, fmt.Errorf("no catalogers selected")
|
||||
taskGroups := [][]task.Task{
|
||||
selectablePkgTasks,
|
||||
selectableFileTasks,
|
||||
}
|
||||
|
||||
return finalTasks, &selection, nil
|
||||
finalTaskGroups, selection, err := task.SelectInGroups(taskGroups, *req)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
finalPkgTasks := finalTaskGroups[0]
|
||||
finalFileTasks := finalTaskGroups[1]
|
||||
|
||||
finalPkgTasks = append(finalPkgTasks, persistentPkgTasks...)
|
||||
|
||||
if len(finalPkgTasks) == 0 && len(finalFileTasks) == 0 {
|
||||
return nil, nil, nil, fmt.Errorf("no catalogers selected")
|
||||
}
|
||||
|
||||
logTaskNames(finalPkgTasks, "package cataloger")
|
||||
logTaskNames(finalFileTasks, "file cataloger")
|
||||
|
||||
if len(finalPkgTasks) == 0 && len(finalFileTasks) == 0 {
|
||||
return nil, nil, nil, fmt.Errorf("no catalogers selected")
|
||||
}
|
||||
|
||||
if len(finalPkgTasks) == 0 {
|
||||
log.Debug("no package catalogers selected")
|
||||
}
|
||||
|
||||
if len(finalFileTasks) == 0 {
|
||||
if c.Files.Selection != file.NoFilesSelection {
|
||||
log.Warnf("no file catalogers selected but file selection is configured as %q (this may be unintentional)", c.Files.Selection)
|
||||
} else {
|
||||
log.Debug("no file catalogers selected")
|
||||
}
|
||||
}
|
||||
|
||||
return finalPkgTasks, finalFileTasks, &selection, nil
|
||||
}
|
||||
|
||||
func finalSelectionRequest(req pkgcataloging.SelectionRequest, src source.Description) (*pkgcataloging.SelectionRequest, error) {
|
||||
func logTaskNames(tasks []task.Task, kind string) {
|
||||
// log as tree output (like tree command)
|
||||
log.Debugf("selected %d %s tasks", len(tasks), kind)
|
||||
names := formatTaskNames(tasks)
|
||||
for idx, t := range names {
|
||||
if idx == len(tasks)-1 {
|
||||
log.Tracef("└── %s", t)
|
||||
} else {
|
||||
log.Tracef("├── %s", t)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func finalTaskSelectionRequest(req cataloging.SelectionRequest, src source.Description) (*cataloging.SelectionRequest, error) {
|
||||
if len(req.DefaultNamesOrTags) == 0 {
|
||||
defaultTag, err := findDefaultTag(src)
|
||||
defaultTags, err := findDefaultTags(src)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to determine default cataloger tag: %w", err)
|
||||
}
|
||||
|
||||
if defaultTag != "" {
|
||||
req.DefaultNamesOrTags = append(req.DefaultNamesOrTags, defaultTag)
|
||||
}
|
||||
req.DefaultNamesOrTags = append(req.DefaultNamesOrTags, defaultTags...)
|
||||
|
||||
req.RemoveNamesOrTags = replaceDefaultTagReferences(defaultTag, req.RemoveNamesOrTags)
|
||||
req.SubSelectTags = replaceDefaultTagReferences(defaultTag, req.SubSelectTags)
|
||||
req.RemoveNamesOrTags = replaceDefaultTagReferences(defaultTags, req.RemoveNamesOrTags)
|
||||
req.SubSelectTags = replaceDefaultTagReferences(defaultTags, req.SubSelectTags)
|
||||
}
|
||||
|
||||
return &req, nil
|
||||
@@ -377,21 +428,29 @@ func (c *CreateSBOMConfig) Create(ctx context.Context, src source.Source) (*sbom
|
||||
return CreateSBOM(ctx, src, c)
|
||||
}
|
||||
|
||||
func findDefaultTag(src source.Description) (string, error) {
|
||||
func findDefaultTags(src source.Description) ([]string, error) {
|
||||
switch m := src.Metadata.(type) {
|
||||
case source.ImageMetadata:
|
||||
return pkgcataloging.ImageTag, nil
|
||||
return []string{pkgcataloging.ImageTag, filecataloging.FileTag}, nil
|
||||
case source.FileMetadata, source.DirectoryMetadata:
|
||||
return pkgcataloging.DirectoryTag, nil
|
||||
return []string{pkgcataloging.DirectoryTag, filecataloging.FileTag}, nil
|
||||
default:
|
||||
return "", fmt.Errorf("unable to determine default cataloger tag for source type=%T", m)
|
||||
return nil, fmt.Errorf("unable to determine default cataloger tag for source type=%T", m)
|
||||
}
|
||||
}
|
||||
|
||||
func replaceDefaultTagReferences(defaultTag string, lst []string) []string {
|
||||
func replaceDefaultTagReferences(defaultTags []string, lst []string) []string {
|
||||
for i, tag := range lst {
|
||||
if strings.ToLower(tag) == "default" {
|
||||
lst[i] = defaultTag
|
||||
switch len(defaultTags) {
|
||||
case 0:
|
||||
lst[i] = ""
|
||||
case 1:
|
||||
lst[i] = defaultTags[0]
|
||||
default:
|
||||
// remove the default tag and add the individual tags
|
||||
lst = append(lst[:i], append(defaultTags, lst[i+1:]...)...)
|
||||
}
|
||||
}
|
||||
}
|
||||
return lst
|
||||
|
||||
@@ -88,15 +88,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
fileCatalogerNames(),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
},
|
||||
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "image"), fileCatalogerNames()),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
@@ -107,15 +107,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
pkgCatalogerNamesWithTagOrName(t, "directory"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
fileCatalogerNames(),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"directory"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"directory", "file"},
|
||||
},
|
||||
Used: pkgCatalogerNamesWithTagOrName(t, "directory"),
|
||||
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "directory"), fileCatalogerNames()),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
@@ -127,51 +127,53 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
pkgCatalogerNamesWithTagOrName(t, "directory"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
fileCatalogerNames(),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"directory"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"directory", "file"},
|
||||
},
|
||||
Used: pkgCatalogerNamesWithTagOrName(t, "directory"),
|
||||
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "directory"), fileCatalogerNames()),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "no file digest cataloger",
|
||||
src: imgSrc,
|
||||
cfg: DefaultCreateSBOMConfig().WithFilesConfig(filecataloging.DefaultConfig().WithHashers()),
|
||||
cfg: DefaultCreateSBOMConfig().WithCatalogerSelection(cataloging.NewSelectionRequest().WithRemovals("digest")),
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
fileCatalogerNames(false, true, true), // note: the digest cataloger is not included
|
||||
fileCatalogerNames("file-metadata", "content", "binary-metadata"),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
RemoveNamesOrTags: []string{"digest"},
|
||||
},
|
||||
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "image"), fileCatalogerNames("file-metadata", "content", "binary-metadata")),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
{
|
||||
name: "select no file catalogers",
|
||||
src: imgSrc,
|
||||
cfg: DefaultCreateSBOMConfig().WithFilesConfig(filecataloging.DefaultConfig().WithSelection(file.NoFilesSelection)),
|
||||
cfg: DefaultCreateSBOMConfig().WithCatalogerSelection(cataloging.NewSelectionRequest().WithRemovals("file")),
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
// note: there are no file catalogers in their own group
|
||||
nil, // note: there is a file cataloging group, with no items in it
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
RemoveNamesOrTags: []string{"file"},
|
||||
},
|
||||
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
},
|
||||
@@ -186,16 +188,16 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
// note: there is a single group of catalogers for pkgs and files
|
||||
append(
|
||||
pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
fileCatalogerNames(true, true, true)...,
|
||||
fileCatalogerNames()...,
|
||||
),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
},
|
||||
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "image"), fileCatalogerNames()),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
@@ -208,15 +210,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
fileCatalogerNames(),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
},
|
||||
Used: addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"),
|
||||
Used: flatten(addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"), fileCatalogerNames()),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
@@ -229,15 +231,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
fileCatalogerNames(),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"directory"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"directory", "file"},
|
||||
},
|
||||
Used: addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"),
|
||||
Used: flatten(addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"), fileCatalogerNames()),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
@@ -246,20 +248,20 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
src: imgSrc,
|
||||
cfg: DefaultCreateSBOMConfig().WithCatalogers(
|
||||
pkgcataloging.NewAlwaysEnabledCatalogerReference(newDummyCataloger("persistent")),
|
||||
).WithCatalogerSelection(pkgcataloging.NewSelectionRequest().WithSubSelections("javascript")),
|
||||
).WithCatalogerSelection(cataloging.NewSelectionRequest().WithSubSelections("javascript")),
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
addTo(pkgIntersect("image", "javascript"), "persistent"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
fileCatalogerNames(),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
SubSelectTags: []string{"javascript"},
|
||||
},
|
||||
Used: addTo(pkgIntersect("image", "javascript"), "persistent"),
|
||||
Used: flatten(addTo(pkgIntersect("image", "javascript"), "persistent"), fileCatalogerNames()),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
@@ -272,15 +274,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
fileCatalogerNames(),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
},
|
||||
Used: addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"),
|
||||
Used: flatten(addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"), fileCatalogerNames()),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
@@ -293,15 +295,15 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
wantTaskNames: [][]string{
|
||||
environmentCatalogerNames(),
|
||||
pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
fileCatalogerNames(),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image"},
|
||||
Requested: cataloging.SelectionRequest{
|
||||
DefaultNamesOrTags: []string{"image", "file"},
|
||||
},
|
||||
Used: pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
Used: flatten(pkgCatalogerNamesWithTagOrName(t, "image"), fileCatalogerNames()),
|
||||
},
|
||||
wantErr: require.NoError,
|
||||
},
|
||||
@@ -314,9 +316,6 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
|
||||
// sanity check
|
||||
require.NotEmpty(t, tt.wantTaskNames)
|
||||
for _, group := range tt.wantTaskNames {
|
||||
require.NotEmpty(t, group)
|
||||
}
|
||||
|
||||
// test the subject
|
||||
gotTasks, gotManifest, err := tt.cfg.makeTaskGroups(tt.src)
|
||||
@@ -378,20 +377,51 @@ func pkgCatalogerNamesWithTagOrName(t *testing.T, token string) []string {
|
||||
return names
|
||||
}
|
||||
|
||||
func fileCatalogerNames(digest, metadata, executable bool) []string {
|
||||
func fileCatalogerNames(tokens ...string) []string {
|
||||
var names []string
|
||||
if digest {
|
||||
names = append(names, "file-digest-cataloger")
|
||||
}
|
||||
if executable {
|
||||
names = append(names, "file-executable-cataloger")
|
||||
}
|
||||
if metadata {
|
||||
names = append(names, "file-metadata-cataloger")
|
||||
cfg := task.DefaultCatalogingFactoryConfig()
|
||||
topLoop:
|
||||
for _, factory := range task.DefaultFileTaskFactories() {
|
||||
cat := factory(cfg)
|
||||
|
||||
if cat == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
name := cat.Name()
|
||||
|
||||
if len(tokens) == 0 {
|
||||
names = append(names, name)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, token := range tokens {
|
||||
if selector, ok := cat.(task.Selector); ok {
|
||||
if selector.HasAllSelectors(token) {
|
||||
names = append(names, name)
|
||||
continue topLoop
|
||||
}
|
||||
|
||||
}
|
||||
if name == token {
|
||||
names = append(names, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sort.Strings(names)
|
||||
return names
|
||||
}
|
||||
|
||||
func flatten(lists ...[]string) []string {
|
||||
var final []string
|
||||
for _, lst := range lists {
|
||||
final = append(final, lst...)
|
||||
}
|
||||
sort.Strings(final)
|
||||
return final
|
||||
}
|
||||
|
||||
func relationshipCatalogerNames() []string {
|
||||
return []string{"relationships-cataloger"}
|
||||
}
|
||||
@@ -436,7 +466,7 @@ func Test_replaceDefaultTagReferences(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.want, replaceDefaultTagReferences("replacement", tt.lst))
|
||||
assert.Equal(t, tt.want, replaceDefaultTagReferences([]string{"replacement"}, tt.lst))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -446,7 +476,7 @@ func Test_findDefaultTag(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
src source.Description
|
||||
want string
|
||||
want []string
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
@@ -454,21 +484,21 @@ func Test_findDefaultTag(t *testing.T) {
|
||||
src: source.Description{
|
||||
Metadata: source.ImageMetadata{},
|
||||
},
|
||||
want: pkgcataloging.ImageTag,
|
||||
want: []string{pkgcataloging.ImageTag, filecataloging.FileTag},
|
||||
},
|
||||
{
|
||||
name: "directory",
|
||||
src: source.Description{
|
||||
Metadata: source.DirectoryMetadata{},
|
||||
},
|
||||
want: pkgcataloging.DirectoryTag,
|
||||
want: []string{pkgcataloging.DirectoryTag, filecataloging.FileTag},
|
||||
},
|
||||
{
|
||||
name: "file",
|
||||
src: source.Description{
|
||||
Metadata: source.FileMetadata{},
|
||||
},
|
||||
want: pkgcataloging.DirectoryTag, // not a mistake...
|
||||
want: []string{pkgcataloging.DirectoryTag, filecataloging.FileTag}, // not a mistake...
|
||||
},
|
||||
{
|
||||
name: "unknown",
|
||||
@@ -483,7 +513,7 @@ func Test_findDefaultTag(t *testing.T) {
|
||||
if tt.wantErr == nil {
|
||||
tt.wantErr = require.NoError
|
||||
}
|
||||
got, err := findDefaultTag(tt.src)
|
||||
got, err := findDefaultTags(tt.src)
|
||||
tt.wantErr(t, err)
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -86,14 +86,14 @@ func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.E
|
||||
func processExecutableLocation(loc file.Location, resolver file.Resolver) (*file.Executable, error) {
|
||||
reader, err := resolver.FileContentsByLocation(loc)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warnf("unable to get file contents for %q", loc.RealPath)
|
||||
log.WithFields("error", err, "path", loc.RealPath).Debug("unable to get file contents")
|
||||
return nil, fmt.Errorf("unable to get file contents: %w", err)
|
||||
}
|
||||
defer internal.CloseAndLogError(reader, loc.RealPath)
|
||||
|
||||
uReader, err := unionreader.GetUnionReader(reader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warnf("unable to get union reader for %q", loc.RealPath)
|
||||
log.WithFields("error", err, "path", loc.RealPath).Debug("unable to get union reader")
|
||||
return nil, fmt.Errorf("unable to get union reader: %w", err)
|
||||
}
|
||||
|
||||
@@ -168,17 +168,17 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
|
||||
switch format {
|
||||
case file.ELF:
|
||||
if err = findELFFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine ELF features for %q", loc.RealPath)
|
||||
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine ELF features")
|
||||
err = fmt.Errorf("unable to determine ELF features: %w", err)
|
||||
}
|
||||
case file.PE:
|
||||
if err = findPEFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine PE features for %q", loc.RealPath)
|
||||
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine PE features")
|
||||
err = fmt.Errorf("unable to determine PE features: %w", err)
|
||||
}
|
||||
case file.MachO:
|
||||
if err = findMachoFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine Macho features for %q", loc.RealPath)
|
||||
log.WithFields("error", err, "path", loc.RealPath).Trace("unable to determine Macho features")
|
||||
err = fmt.Errorf("unable to determine Macho features: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,14 +14,14 @@ func AllRegularFiles(ctx context.Context, resolver file.Resolver) (locations []f
|
||||
for location := range resolver.AllLocations(ctx) {
|
||||
resolvedLocations, err := resolver.FilesByPath(location.RealPath)
|
||||
if err != nil {
|
||||
log.Warnf("unable to resolve %+v: %+v", location, err)
|
||||
log.Debugf("unable to resolve %+v: %+v", location, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, resolvedLocation := range resolvedLocations {
|
||||
metadata, err := resolver.FileMetadataByLocation(resolvedLocation)
|
||||
if err != nil {
|
||||
log.Warnf("unable to get metadata for %+v: %+v", location, err)
|
||||
log.Debugf("unable to get metadata for %+v: %+v", location, err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ func (c Coordinates) ID() artifact.ID {
|
||||
f, err := artifact.IDByHash(c)
|
||||
if err != nil {
|
||||
// TODO: what to do in this case?
|
||||
log.Warnf("unable to get fingerprint of location coordinate=%+v: %+v", c, err)
|
||||
log.Debugf("unable to get fingerprint of location coordinate=%+v: %+v", c, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ type License struct {
|
||||
SPDXExpression string
|
||||
Type license.Type
|
||||
LicenseEvidence *LicenseEvidence // evidence from license classifier
|
||||
Contents string `hash:"ignore"`
|
||||
}
|
||||
|
||||
type LicenseEvidence struct {
|
||||
|
||||
@@ -6,12 +6,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/CycloneDX/cyclonedx-go"
|
||||
cyclonedx "github.com/CycloneDX/cyclonedx-go"
|
||||
"github.com/google/uuid"
|
||||
|
||||
stfile "github.com/anchore/stereoscope/pkg/file"
|
||||
"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/cyclonedxutil/helpers"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -19,6 +21,18 @@ import (
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
var cycloneDXValidHash = map[string]cyclonedx.HashAlgorithm{
|
||||
"sha1": cyclonedx.HashAlgoSHA1,
|
||||
"md5": cyclonedx.HashAlgoMD5,
|
||||
"sha256": cyclonedx.HashAlgoSHA256,
|
||||
"sha384": cyclonedx.HashAlgoSHA384,
|
||||
"sha512": cyclonedx.HashAlgoSHA512,
|
||||
"blake2b256": cyclonedx.HashAlgoBlake2b_256,
|
||||
"blake2b384": cyclonedx.HashAlgoBlake2b_384,
|
||||
"blake2b512": cyclonedx.HashAlgoBlake2b_512,
|
||||
"blake3": cyclonedx.HashAlgoBlake3,
|
||||
}
|
||||
|
||||
func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
|
||||
cdxBOM := cyclonedx.NewBOM()
|
||||
|
||||
@@ -28,12 +42,49 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
|
||||
cdxBOM.SerialNumber = uuid.New().URN()
|
||||
cdxBOM.Metadata = toBomDescriptor(s.Descriptor.Name, s.Descriptor.Version, s.Source)
|
||||
|
||||
// Packages
|
||||
packages := s.Artifacts.Packages.Sorted()
|
||||
components := make([]cyclonedx.Component, len(packages))
|
||||
for i, p := range packages {
|
||||
components[i] = helpers.EncodeComponent(p)
|
||||
}
|
||||
components = append(components, toOSComponent(s.Artifacts.LinuxDistribution)...)
|
||||
|
||||
// Files
|
||||
artifacts := s.Artifacts
|
||||
coordinates := s.AllCoordinates()
|
||||
for _, coordinate := range coordinates {
|
||||
var metadata *file.Metadata
|
||||
// File Info
|
||||
fileMetadata, exists := artifacts.FileMetadata[coordinate]
|
||||
// no file metadata then don't include in SBOM
|
||||
// the syft config allows for sometimes only capturing files owned by packages
|
||||
// so there can be a map miss here where we have less metadata than all coordinates
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
if fileMetadata.Type == stfile.TypeDirectory ||
|
||||
fileMetadata.Type == stfile.TypeSocket ||
|
||||
fileMetadata.Type == stfile.TypeSymLink {
|
||||
// skip dir, symlinks and sockets for the final bom
|
||||
continue
|
||||
}
|
||||
metadata = &fileMetadata
|
||||
|
||||
// Digests
|
||||
var digests []file.Digest
|
||||
if digestsForLocation, exists := artifacts.FileDigests[coordinate]; exists {
|
||||
digests = digestsForLocation
|
||||
}
|
||||
|
||||
cdxHashes := digestsToHashes(digests)
|
||||
components = append(components, cyclonedx.Component{
|
||||
BOMRef: string(coordinate.ID()),
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Name: metadata.Path,
|
||||
Hashes: &cdxHashes,
|
||||
})
|
||||
}
|
||||
cdxBOM.Components = &components
|
||||
|
||||
dependencies := toDependencies(s.Relationships)
|
||||
@@ -44,6 +95,22 @@ func ToFormatModel(s sbom.SBOM) *cyclonedx.BOM {
|
||||
return cdxBOM
|
||||
}
|
||||
|
||||
func digestsToHashes(digests []file.Digest) []cyclonedx.Hash {
|
||||
var hashes []cyclonedx.Hash
|
||||
for _, digest := range digests {
|
||||
lookup := strings.ToLower(digest.Algorithm)
|
||||
cdxAlgo, exists := cycloneDXValidHash[lookup]
|
||||
if !exists {
|
||||
continue
|
||||
}
|
||||
hashes = append(hashes, cyclonedx.Hash{
|
||||
Algorithm: cdxAlgo,
|
||||
Value: digest.Value,
|
||||
})
|
||||
}
|
||||
return hashes
|
||||
}
|
||||
|
||||
func toOSComponent(distro *linux.Release) []cyclonedx.Component {
|
||||
if distro == nil {
|
||||
return []cyclonedx.Component{}
|
||||
@@ -235,7 +302,7 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
}
|
||||
bomRef, err := artifact.IDByHash(metadata.ID)
|
||||
if err != nil {
|
||||
log.Warnf("unable to get fingerprint of source image metadata=%s: %+v", metadata.ID, err)
|
||||
log.Debugf("unable to get fingerprint of source image metadata=%s: %+v", metadata.ID, err)
|
||||
}
|
||||
return &cyclonedx.Component{
|
||||
BOMRef: string(bomRef),
|
||||
@@ -249,7 +316,7 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
}
|
||||
bomRef, err := artifact.IDByHash(metadata.Path)
|
||||
if err != nil {
|
||||
log.Warnf("unable to get fingerprint of source directory metadata path=%s: %+v", metadata.Path, err)
|
||||
log.Debugf("unable to get fingerprint of source directory metadata path=%s: %+v", metadata.Path, err)
|
||||
}
|
||||
return &cyclonedx.Component{
|
||||
BOMRef: string(bomRef),
|
||||
@@ -264,7 +331,7 @@ func toBomDescriptorComponent(srcMetadata source.Description) *cyclonedx.Compone
|
||||
}
|
||||
bomRef, err := artifact.IDByHash(metadata.Path)
|
||||
if err != nil {
|
||||
log.Warnf("unable to get fingerprint of source file metadata path=%s: %+v", metadata.Path, err)
|
||||
log.Debugf("unable to get fingerprint of source file metadata path=%s: %+v", metadata.Path, err)
|
||||
}
|
||||
return &cyclonedx.Component{
|
||||
BOMRef: string(bomRef),
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
stfile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/format/internal/cyclonedxutil/helpers"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -143,6 +145,178 @@ func Test_relationships(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_FileComponents(t *testing.T) {
|
||||
p1 := pkg.Package{
|
||||
Name: "p1",
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
sbom sbom.SBOM
|
||||
want []cyclonedx.Component
|
||||
}{
|
||||
{
|
||||
name: "sbom coordinates with file metadata are serialized to cdx along with packages",
|
||||
sbom: sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(p1),
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{
|
||||
{RealPath: "/test"}: {Path: "/test", Type: stfile.TypeRegular},
|
||||
},
|
||||
FileDigests: map[file.Coordinates][]file.Digest{
|
||||
{RealPath: "/test"}: {
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "xyz12345",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []cyclonedx.Component{
|
||||
{
|
||||
BOMRef: "2a1fc74ade23e357",
|
||||
Type: cyclonedx.ComponentTypeLibrary,
|
||||
Name: "p1",
|
||||
},
|
||||
{
|
||||
BOMRef: "3f31cb2d98be6c1e",
|
||||
Name: "/test",
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Hashes: &[]cyclonedx.Hash{
|
||||
{Algorithm: "SHA-256", Value: "xyz12345"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sbom coordinates that don't contain metadata are not added to the final output",
|
||||
sbom: sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{
|
||||
{RealPath: "/test"}: {Path: "/test", Type: stfile.TypeRegular},
|
||||
},
|
||||
FileDigests: map[file.Coordinates][]file.Digest{
|
||||
{RealPath: "/test"}: {
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "xyz12345",
|
||||
},
|
||||
},
|
||||
{RealPath: "/test-2"}: {
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "xyz678910",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []cyclonedx.Component{
|
||||
{
|
||||
BOMRef: "3f31cb2d98be6c1e",
|
||||
Name: "/test",
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Hashes: &[]cyclonedx.Hash{
|
||||
{Algorithm: "SHA-256", Value: "xyz12345"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sbom coordinates that return hashes not covered by cdx only include valid digests",
|
||||
sbom: sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{
|
||||
{RealPath: "/test"}: {Path: "/test", Type: stfile.TypeRegular},
|
||||
},
|
||||
FileDigests: map[file.Coordinates][]file.Digest{
|
||||
{RealPath: "/test"}: {
|
||||
{
|
||||
Algorithm: "xxh64",
|
||||
Value: "xyz12345",
|
||||
},
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "xyz678910",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []cyclonedx.Component{
|
||||
{
|
||||
BOMRef: "3f31cb2d98be6c1e",
|
||||
Name: "/test",
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Hashes: &[]cyclonedx.Hash{
|
||||
{Algorithm: "SHA-256", Value: "xyz678910"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sbom coordinates who's metadata is directory or symlink are skipped",
|
||||
sbom: sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{
|
||||
{RealPath: "/testdir"}: {
|
||||
Path: "/testdir",
|
||||
Type: stfile.TypeDirectory,
|
||||
},
|
||||
{RealPath: "/testsym"}: {
|
||||
Path: "/testsym",
|
||||
Type: stfile.TypeSymLink,
|
||||
},
|
||||
{RealPath: "/test"}: {Path: "/test", Type: stfile.TypeRegular},
|
||||
},
|
||||
FileDigests: map[file.Coordinates][]file.Digest{
|
||||
{RealPath: "/test"}: {
|
||||
{
|
||||
Algorithm: "sha256",
|
||||
Value: "xyz12345",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: []cyclonedx.Component{
|
||||
{
|
||||
BOMRef: "3f31cb2d98be6c1e",
|
||||
Name: "/test",
|
||||
Type: cyclonedx.ComponentTypeFile,
|
||||
Hashes: &[]cyclonedx.Hash{
|
||||
{Algorithm: "SHA-256", Value: "xyz12345"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sbom with no files serialized correctly",
|
||||
sbom: sbom.SBOM{
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(p1),
|
||||
},
|
||||
},
|
||||
want: []cyclonedx.Component{
|
||||
{
|
||||
BOMRef: "2a1fc74ade23e357",
|
||||
Type: cyclonedx.ComponentTypeLibrary,
|
||||
Name: "p1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
cdx := ToFormatModel(test.sbom)
|
||||
got := *cdx.Components
|
||||
if diff := cmp.Diff(test.want, got); diff != "" {
|
||||
t.Errorf("cdx file components mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toBomDescriptor(t *testing.T) {
|
||||
type args struct {
|
||||
name string
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha1"
|
||||
"fmt"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/spdx/tools-golang/spdx"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
internallicenses "github.com/anchore/syft/internal/licenses"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/mimetype"
|
||||
"github.com/anchore/syft/internal/relationship"
|
||||
@@ -246,6 +248,7 @@ func toRootPackage(s source.Description) *spdx.Package {
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
Supplier: helpers.NOASSERTION,
|
||||
},
|
||||
PackageCopyrightText: helpers.NOASSERTION,
|
||||
PackageDownloadLocation: helpers.NOASSERTION,
|
||||
PackageLicenseConcluded: helpers.NOASSERTION,
|
||||
PackageLicenseDeclared: helpers.NOASSERTION,
|
||||
@@ -626,14 +629,21 @@ func toFiles(s sbom.SBOM) (results []*spdx.File) {
|
||||
comment = fmt.Sprintf("layerID: %s", coordinates.FileSystemID)
|
||||
}
|
||||
|
||||
relativePath, err := convertAbsoluteToRelative(coordinates.RealPath)
|
||||
if err != nil {
|
||||
log.Debugf("unable to convert relative path '%s' to absolute path: %s", coordinates.RealPath, err)
|
||||
relativePath = coordinates.RealPath
|
||||
}
|
||||
|
||||
results = append(results, &spdx.File{
|
||||
FileSPDXIdentifier: toSPDXID(coordinates),
|
||||
FileComment: comment,
|
||||
// required, no attempt made to determine license information
|
||||
LicenseConcluded: noAssertion,
|
||||
Checksums: toFileChecksums(digests),
|
||||
FileName: coordinates.RealPath,
|
||||
FileTypes: toFileTypes(metadata),
|
||||
LicenseConcluded: noAssertion,
|
||||
FileCopyrightText: noAssertion,
|
||||
Checksums: toFileChecksums(digests),
|
||||
FileName: relativePath,
|
||||
FileTypes: toFileTypes(metadata),
|
||||
LicenseInfoInFiles: []string{ // required in SPDX 2.2
|
||||
helpers.NOASSERTION,
|
||||
},
|
||||
@@ -713,8 +723,8 @@ func toFileTypes(metadata *file.Metadata) (ty []string) {
|
||||
return ty
|
||||
}
|
||||
|
||||
// other licenses are for licenses from the pkg.Package that do not have an SPDXExpression
|
||||
// field. The spdxexpression field is only filled given a validated Value field.
|
||||
// other licenses are for licenses from the pkg.Package that do not have a valid SPDX Expression
|
||||
// OR are an expression that is a single `License-Ref-*`
|
||||
func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense {
|
||||
licenses := map[string]helpers.SPDXLicense{}
|
||||
|
||||
@@ -724,11 +734,17 @@ func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense {
|
||||
if l.Value != "" {
|
||||
licenses[l.ID] = l
|
||||
}
|
||||
if l.ID != "" && isLicenseRef(l.ID) {
|
||||
licenses[l.ID] = l
|
||||
}
|
||||
}
|
||||
for _, l := range concludedLicenses {
|
||||
if l.Value != "" {
|
||||
licenses[l.ID] = l
|
||||
}
|
||||
if l.ID != "" && isLicenseRef(l.ID) {
|
||||
licenses[l.ID] = l
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,14 +758,33 @@ func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense {
|
||||
slices.Sort(ids)
|
||||
for _, id := range ids {
|
||||
license := licenses[id]
|
||||
result = append(result, &spdx.OtherLicense{
|
||||
value := license.Value
|
||||
// handle cases where LicenseRef needs to be included in hasExtractedLicensingInfos
|
||||
if license.Value == "" {
|
||||
value, _ = strings.CutPrefix(license.ID, "LicenseRef-")
|
||||
}
|
||||
other := &spdx.OtherLicense{
|
||||
LicenseIdentifier: license.ID,
|
||||
ExtractedText: license.Value,
|
||||
})
|
||||
ExtractedText: value,
|
||||
}
|
||||
customPrefix := spdxlicense.LicenseRefPrefix + helpers.SanitizeElementID(internallicenses.UnknownLicensePrefix)
|
||||
if strings.HasPrefix(license.ID, customPrefix) {
|
||||
other.LicenseName = strings.TrimPrefix(license.ID, customPrefix)
|
||||
other.LicenseComment = strings.Trim(internallicenses.UnknownLicensePrefix, "-_")
|
||||
}
|
||||
result = append(result, other)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
var licenseRefRegEx = regexp.MustCompile(`^LicenseRef-[A-Za-z0-9_-]+$`)
|
||||
|
||||
// isSingularLicenseRef checks if the string is a singular LicenseRef-* identifier
|
||||
func isLicenseRef(s string) bool {
|
||||
// Match the input string against the regex
|
||||
return licenseRefRegEx.MatchString(s)
|
||||
}
|
||||
|
||||
// TODO: handle SPDX excludes file case
|
||||
// f file is an "excludes" file, skip it /* exclude SPDX analysis file(s) */
|
||||
// see: https://spdx.github.io/spdx-spec/v2.3/package-information/#79-package-verification-code-field
|
||||
@@ -811,3 +846,22 @@ func trimPatchVersion(semver string) string {
|
||||
}
|
||||
return semver
|
||||
}
|
||||
|
||||
// spdx requires that the file name field is a relative filename
|
||||
// with the root of the package archive or directory
|
||||
func convertAbsoluteToRelative(absPath string) (string, error) {
|
||||
// Ensure the absolute path is absolute (although it should already be)
|
||||
if !path.IsAbs(absPath) {
|
||||
// already relative
|
||||
log.Debugf("%s is already relative", absPath)
|
||||
return absPath, nil
|
||||
}
|
||||
|
||||
// we use "/" here given that we're converting absolute paths from root to relative
|
||||
relPath, found := strings.CutPrefix(absPath, "/")
|
||||
if !found {
|
||||
return "", fmt.Errorf("error calculating relative path: %s", absPath)
|
||||
}
|
||||
|
||||
return relPath, nil
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ func Test_toFormatModel(t *testing.T) {
|
||||
{
|
||||
Category: "PACKAGE-MANAGER",
|
||||
RefType: "purl",
|
||||
Locator: "pkg:oci/alpine@sha256:d34db33f?arch=&tag=latest",
|
||||
Locator: "pkg:oci/alpine@sha256%3Ad34db33f?arch=&tag=latest",
|
||||
},
|
||||
},
|
||||
PackageSupplier: &spdx.Supplier{
|
||||
@@ -382,6 +382,51 @@ func Test_toPackageChecksums(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toFiles(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in sbom.SBOM
|
||||
want spdx.File
|
||||
}{
|
||||
{
|
||||
name: "File paths are converted to relative in final SPDX collection",
|
||||
in: sbom.SBOM{
|
||||
Source: source.Description{
|
||||
Name: "alpine",
|
||||
Version: "sha256:d34db33f",
|
||||
Metadata: source.ImageMetadata{
|
||||
UserInput: "alpine:latest",
|
||||
ManifestDigest: "sha256:d34db33f",
|
||||
},
|
||||
},
|
||||
Artifacts: sbom.Artifacts{
|
||||
Packages: pkg.NewCollection(pkg.Package{
|
||||
Name: "pkg-1",
|
||||
Version: "version-1",
|
||||
}),
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{
|
||||
{
|
||||
RealPath: "/some/path",
|
||||
FileSystemID: "",
|
||||
}: {
|
||||
Path: "/some/path",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: spdx.File{
|
||||
FileName: "some/path",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
files := toFiles(test.in)
|
||||
got := files[0]
|
||||
assert.Equal(t, test.want.FileName, got.FileName)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_toFileTypes(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
@@ -753,6 +798,29 @@ func Test_OtherLicenses(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LicenseRef as a valid spdx expression",
|
||||
pkg: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("LicenseRef-Fedora-Public-Domain"),
|
||||
),
|
||||
},
|
||||
expected: []*spdx.OtherLicense{
|
||||
{
|
||||
LicenseIdentifier: "LicenseRef-Fedora-Public-Domain",
|
||||
ExtractedText: "Fedora-Public-Domain",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "LicenseRef as a valid spdx expression does not otherize compound spdx expressions",
|
||||
pkg: pkg.Package{
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("(MIT AND LicenseRef-Fedora-Public-Domain)"),
|
||||
),
|
||||
},
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -5,8 +5,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/mholt/archiver/v3"
|
||||
|
||||
"github.com/anchore/archiver/v3"
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -165,7 +164,7 @@ func toDependencies(s *sbom.SBOM, p pkg.Package) (out []string) {
|
||||
func dependencyName(p pkg.Package) string {
|
||||
purl, err := packageurl.FromString(p.PURL)
|
||||
if err != nil {
|
||||
log.Warnf("Invalid PURL for package: '%s' PURL: '%s' (%w)", p.Name, p.PURL, err)
|
||||
log.Debugf("Invalid PURL for package: '%s' PURL: '%s' (%w)", p.Name, p.PURL, err)
|
||||
return ""
|
||||
}
|
||||
// don't use qualifiers for this
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"version": "v0.42.0-bogus"
|
||||
},
|
||||
"metadata": {
|
||||
"syft:distro": "pkg:generic/debian@1.2.3?like=like!"
|
||||
"syft:distro": "pkg:generic/debian@1.2.3?like=like%21"
|
||||
},
|
||||
"manifests": {
|
||||
"redacted/some/path/some/path/pkg1": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"version": "v0.42.0-bogus"
|
||||
},
|
||||
"metadata": {
|
||||
"syft:distro": "pkg:generic/debian@1.2.3?like=like!"
|
||||
"syft:distro": "pkg:generic/debian@1.2.3?like=like%21"
|
||||
},
|
||||
"manifests": {
|
||||
"user-image-input:/somefile-1.txt": {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user