mirror of
https://github.com/anchore/syft.git
synced 2026-04-05 14:20:34 +02:00
Merge remote-tracking branch 'origin/main' into chore/build-syft-for-cli-tests
This commit is contained in:
commit
bd9b39d370
@ -1 +1,2 @@
|
|||||||
enforce-v0: true # don't make breaking-change label bump major version before 1.0.
|
enforce-v0: true # don't make breaking-change label bump major version before 1.0.
|
||||||
|
title: ""
|
||||||
39
.github/actions/bootstrap/action.yaml
vendored
39
.github/actions/bootstrap/action.yaml
vendored
@ -1,14 +1,11 @@
|
|||||||
name: "Bootstrap"
|
name: "Bootstrap"
|
||||||
|
|
||||||
description: "Bootstrap all tools and dependencies"
|
description: "Bootstrap all tools and dependencies"
|
||||||
inputs:
|
inputs:
|
||||||
go-version:
|
go-version:
|
||||||
description: "Go version to install"
|
description: "Go version to install"
|
||||||
required: true
|
required: true
|
||||||
default: "1.20.x"
|
default: "1.21.x"
|
||||||
use-go-cache:
|
|
||||||
description: "Restore go cache"
|
|
||||||
required: true
|
|
||||||
default: "true"
|
|
||||||
cache-key-prefix:
|
cache-key-prefix:
|
||||||
description: "Prefix all cache keys with this value"
|
description: "Prefix all cache keys with this value"
|
||||||
required: true
|
required: true
|
||||||
@ -24,49 +21,25 @@ inputs:
|
|||||||
runs:
|
runs:
|
||||||
using: "composite"
|
using: "composite"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/setup-go@v3
|
# note: go mod and build is automatically cached on default with v4+
|
||||||
|
- uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ inputs.go-version }}
|
go-version: ${{ inputs.go-version }}
|
||||||
|
|
||||||
- name: Restore tool cache
|
- name: Restore tool cache
|
||||||
id: tool-cache
|
id: tool-cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/.tmp
|
path: ${{ github.workspace }}/.tmp
|
||||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('Makefile') }}
|
||||||
|
|
||||||
# note: we need to keep restoring the go mod cache before bootstrapping tools since `go install` is used in
|
|
||||||
# some installations of project tools.
|
|
||||||
- name: Restore go module cache
|
|
||||||
id: go-mod-cache
|
|
||||||
if: inputs.use-go-cache == 'true'
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/go/pkg/mod
|
|
||||||
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ inputs.cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-
|
|
||||||
|
|
||||||
- name: (cache-miss) Bootstrap project tools
|
- name: (cache-miss) Bootstrap project tools
|
||||||
shell: bash
|
shell: bash
|
||||||
if: steps.tool-cache.outputs.cache-hit != 'true'
|
if: steps.tool-cache.outputs.cache-hit != 'true'
|
||||||
run: make bootstrap-tools
|
run: make bootstrap-tools
|
||||||
|
|
||||||
- name: Restore go build cache
|
- name: Bootstrap go dependencies
|
||||||
id: go-cache
|
|
||||||
if: inputs.use-go-cache == 'true'
|
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cache/go-build
|
|
||||||
key: ${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-${{ hashFiles('**/go.sum') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ inputs.cache-key-prefix }}-${{ inputs.build-cache-key-prefix }}-${{ runner.os }}-go-${{ inputs.go-version }}-
|
|
||||||
|
|
||||||
- name: (cache-miss) Bootstrap go dependencies
|
|
||||||
shell: bash
|
shell: bash
|
||||||
if: steps.go-mod-cache.outputs.cache-hit != 'true' && inputs.use-go-cache == 'true'
|
|
||||||
run: make bootstrap-go
|
run: make bootstrap-go
|
||||||
|
|
||||||
- name: Install apt packages
|
- name: Install apt packages
|
||||||
|
|||||||
12
.github/dependabot.yml
vendored
12
.github/dependabot.yml
vendored
@ -1,10 +1,12 @@
|
|||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
|
- package-ecosystem: gomod
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
open-pull-requests-limit: 10
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: "github-actions"
|
||||||
directory: "/"
|
directory: "/"
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
interval: "daily"
|
||||||
- package-ecosystem: "gomod"
|
open-pull-requests-limit: 10
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: daily
|
|
||||||
12
.github/workflows/benchmark-testing.yaml
vendored
12
.github/workflows/benchmark-testing.yaml
vendored
@ -4,6 +4,9 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
Benchmark-Test:
|
Benchmark-Test:
|
||||||
@ -13,13 +16,14 @@ jobs:
|
|||||||
# we also want to run on push such that merges to main are recorded to the cache. For this reason we don't filter
|
# 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.
|
# the job by event.
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.0
|
||||||
|
|
||||||
- name: Bootstrap environment
|
- name: Bootstrap environment
|
||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
||||||
- name: Restore base benchmark result
|
- name: Restore base benchmark result
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: test/results/benchmark-main.txt
|
path: test/results/benchmark-main.txt
|
||||||
# use base sha for PR or new commit hash for main push in benchmark result key
|
# use base sha for PR or new commit hash for main push in benchmark result key
|
||||||
@ -35,13 +39,13 @@ jobs:
|
|||||||
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
|
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
|
||||||
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
|
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
- uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # v3.1.3
|
||||||
with:
|
with:
|
||||||
name: benchmark-test-results
|
name: benchmark-test-results
|
||||||
path: test/results/**/*
|
path: test/results/**/*
|
||||||
|
|
||||||
- name: Update PR benchmark results comment
|
- name: Update PR benchmark results comment
|
||||||
uses: marocchino/sticky-pull-request-comment@v2
|
uses: marocchino/sticky-pull-request-comment@efaaab3fd41a9c3de579aba759d2552635e590fd #v2.8.0
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
header: benchmark
|
header: benchmark
|
||||||
|
|||||||
19
.github/workflows/codeql-analysis.yml
vendored
19
.github/workflows/codeql-analysis.yml
vendored
@ -14,11 +14,17 @@ on:
|
|||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 * * 3'
|
- cron: '0 0 * * 3'
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
analyze:
|
analyze:
|
||||||
name: Analyze
|
name: Analyze
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
security-events: write
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@ -30,11 +36,16 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
|
- name: Install Go
|
||||||
|
uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@d90b8d79de6dc1f58e83a1499aa58d6c93dc28de #v2.22.2
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@ -45,7 +56,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# 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)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v2
|
uses: github/codeql-action/autobuild@d90b8d79de6dc1f58e83a1499aa58d6c93dc28de #v2.22.2
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@ -59,4 +70,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v2
|
uses: github/codeql-action/analyze@d90b8d79de6dc1f58e83a1499aa58d6c93dc28de #v2.22.2
|
||||||
|
|||||||
4
.github/workflows/oss-project-board-add.yaml
vendored
4
.github/workflows/oss-project-board-add.yaml
vendored
@ -1,5 +1,8 @@
|
|||||||
name: Add to OSS board
|
name: Add to OSS board
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
on:
|
on:
|
||||||
issues:
|
issues:
|
||||||
types:
|
types:
|
||||||
@ -9,7 +12,6 @@ on:
|
|||||||
- labeled
|
- labeled
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
run:
|
run:
|
||||||
uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main"
|
uses: "anchore/workflows/.github/workflows/oss-project-board-add.yaml@main"
|
||||||
secrets:
|
secrets:
|
||||||
|
|||||||
25
.github/workflows/release-homebrew.yaml
vendored
Normal file
25
.github/workflows/release-homebrew.yaml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: "Release Homebrew"
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
homebrew:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- name: Update Homebrew formula
|
||||||
|
uses: dawidd6/action-homebrew-bump-formula@d3667e5ae14df19579e4414897498e3e88f2f458 # v3.10.0
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.HOMEBREW_TOKEN }}
|
||||||
|
org: anchore
|
||||||
|
formula: syft
|
||||||
|
|
||||||
|
- uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #v3.15.1
|
||||||
|
if: ${{ failure() }}
|
||||||
|
with:
|
||||||
|
status: ${{ job.status }}
|
||||||
|
fields: repo,workflow,action,eventName
|
||||||
|
text: "Post-release homebrew publish workflow has failed: https://github.com/anchore/syft/actions/workflows/release-homebrew.yaml"
|
||||||
|
env:
|
||||||
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
|
||||||
41
.github/workflows/release.yaml
vendored
41
.github/workflows/release.yaml
vendored
@ -1,4 +1,8 @@
|
|||||||
name: "Release"
|
name: "Release"
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
@ -6,15 +10,12 @@ on:
|
|||||||
description: tag the latest commit on main with the given version (prefixed with v)
|
description: tag the latest commit on main with the given version (prefixed with v)
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
env:
|
|
||||||
GO_VERSION: "1.19.x"
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
quality-gate:
|
quality-gate:
|
||||||
environment: release
|
environment: release
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
- name: Check if tag already exists
|
- name: Check if tag already exists
|
||||||
# note: this will fail if the tag already exists
|
# note: this will fail if the tag already exists
|
||||||
@ -23,7 +24,7 @@ jobs:
|
|||||||
git tag ${{ github.event.inputs.version }}
|
git tag ${{ github.event.inputs.version }}
|
||||||
|
|
||||||
- name: Check static analysis results
|
- name: Check static analysis results
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0
|
||||||
id: static-analysis
|
id: static-analysis
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -32,7 +33,7 @@ jobs:
|
|||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check unit test results
|
- name: Check unit test results
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0
|
||||||
id: unit
|
id: unit
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -41,7 +42,7 @@ jobs:
|
|||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check integration test results
|
- name: Check integration test results
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0
|
||||||
id: integration
|
id: integration
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -50,7 +51,7 @@ jobs:
|
|||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check acceptance test results (linux)
|
- name: Check acceptance test results (linux)
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0
|
||||||
id: acceptance-linux
|
id: acceptance-linux
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -59,7 +60,7 @@ jobs:
|
|||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check acceptance test results (mac)
|
- name: Check acceptance test results (mac)
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0
|
||||||
id: acceptance-mac
|
id: acceptance-mac
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -68,7 +69,7 @@ jobs:
|
|||||||
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
ref: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||||
|
|
||||||
- name: Check cli test results (linux)
|
- name: Check cli test results (linux)
|
||||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
uses: fountainhead/action-wait-for-check@297be350cf8393728ea4d4b39435c7d7ae167c93 # v1.1.0
|
||||||
id: cli-linux
|
id: cli-linux
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -94,7 +95,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
packages: write
|
packages: write
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
@ -105,13 +106,13 @@ jobs:
|
|||||||
build-cache-key-prefix: "snapshot"
|
build-cache-key-prefix: "snapshot"
|
||||||
|
|
||||||
- name: Login to Docker Hub
|
- name: Login to Docker Hub
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d #v3.0.0
|
||||||
with:
|
with:
|
||||||
username: ${{ secrets.TOOLBOX_DOCKER_USER }}
|
username: ${{ secrets.TOOLBOX_DOCKER_USER }}
|
||||||
password: ${{ secrets.TOOLBOX_DOCKER_PASS }}
|
password: ${{ secrets.TOOLBOX_DOCKER_PASS }}
|
||||||
|
|
||||||
- name: Login to GitHub Container Registry
|
- name: Login to GitHub Container Registry
|
||||||
uses: docker/login-action@v2
|
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d #v3.0.0
|
||||||
with:
|
with:
|
||||||
registry: ghcr.io
|
registry: ghcr.io
|
||||||
username: ${{ github.actor }}
|
username: ${{ github.actor }}
|
||||||
@ -119,7 +120,9 @@ jobs:
|
|||||||
|
|
||||||
- name: Tag release
|
- name: Tag release
|
||||||
run: |
|
run: |
|
||||||
git tag ${{ github.event.inputs.version }}
|
git config --global user.name "anchoreci"
|
||||||
|
git config --global user.email "anchoreci@users.noreply.github.com"
|
||||||
|
git tag -a ${{ github.event.inputs.version }} -m "Release ${{ github.event.inputs.version }}"
|
||||||
git push origin --tags
|
git push origin --tags
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@ -141,12 +144,12 @@ jobs:
|
|||||||
AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.TOOLBOX_AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }}
|
||||||
|
|
||||||
- uses: anchore/sbom-action@v0
|
- uses: anchore/sbom-action@78fc58e266e87a38d4194b2137a3d4e9bcaf7ca1 #v0.14.3
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
artifact-name: sbom.spdx.json
|
artifact-name: sbom.spdx.json
|
||||||
|
|
||||||
- uses: 8398a7/action-slack@v3
|
- uses: 8398a7/action-slack@fbd6aa58ba854a740e11a35d0df80cb5d12101d8 #v3.15.1
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
with:
|
with:
|
||||||
status: ${{ job.status }}
|
status: ${{ job.status }}
|
||||||
@ -155,9 +158,3 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
|
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_TOOLBOX_WEBHOOK_URL }}
|
||||||
if: ${{ success() }}
|
if: ${{ success() }}
|
||||||
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: artifacts
|
|
||||||
path: dist/**/*
|
|
||||||
|
|
||||||
|
|||||||
17
.github/workflows/update-bootstrap-tools.yml
vendored
17
.github/workflows/update-bootstrap-tools.yml
vendored
@ -6,17 +6,20 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: "1.20.x"
|
GO_VERSION: "1.21.x"
|
||||||
GO_STABLE_VERSION: true
|
GO_STABLE_VERSION: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-bootstrap-tools:
|
update-bootstrap-tools:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'anchore/syft' # only run for main repo
|
if: github.repository == 'anchore/syft' # only run for main repo
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
stable: ${{ env.GO_STABLE_VERSION }}
|
stable: ${{ env.GO_STABLE_VERSION }}
|
||||||
@ -29,6 +32,7 @@ jobs:
|
|||||||
GOSIMPORTS_LATEST_VERSION=$(go list -m -json github.com/rinchsan/gosimports@latest 2>/dev/null | jq -r '.Version')
|
GOSIMPORTS_LATEST_VERSION=$(go list -m -json github.com/rinchsan/gosimports@latest 2>/dev/null | jq -r '.Version')
|
||||||
YAJSV_LATEST_VERSION=$(go list -m -json github.com/neilpa/yajsv@latest 2>/dev/null | jq -r '.Version')
|
YAJSV_LATEST_VERSION=$(go list -m -json github.com/neilpa/yajsv@latest 2>/dev/null | jq -r '.Version')
|
||||||
COSIGN_LATEST_VERSION=$(go list -m -json github.com/sigstore/cosign/v2@latest 2>/dev/null | jq -r '.Version')
|
COSIGN_LATEST_VERSION=$(go list -m -json github.com/sigstore/cosign/v2@latest 2>/dev/null | jq -r '.Version')
|
||||||
|
QUILL_LATEST_VERSION=$(go list -m -json github.com/anchore/quill@latest 2>/dev/null | jq -r '.Version')
|
||||||
GLOW_LATEST_VERSION=$(go list -m -json github.com/charmbracelet/glow@latest 2>/dev/null | jq -r '.Version')
|
GLOW_LATEST_VERSION=$(go list -m -json github.com/charmbracelet/glow@latest 2>/dev/null | jq -r '.Version')
|
||||||
|
|
||||||
# update version variables in the Makefile
|
# update version variables in the Makefile
|
||||||
@ -39,6 +43,7 @@ jobs:
|
|||||||
sed -r -i -e 's/^(GOSIMPORTS_VERSION := ).*/\1'${GOSIMPORTS_LATEST_VERSION}'/' Makefile
|
sed -r -i -e 's/^(GOSIMPORTS_VERSION := ).*/\1'${GOSIMPORTS_LATEST_VERSION}'/' Makefile
|
||||||
sed -r -i -e 's/^(YAJSV_VERSION := ).*/\1'${YAJSV_LATEST_VERSION}'/' Makefile
|
sed -r -i -e 's/^(YAJSV_VERSION := ).*/\1'${YAJSV_LATEST_VERSION}'/' Makefile
|
||||||
sed -r -i -e 's/^(COSIGN_VERSION := ).*/\1'${COSIGN_LATEST_VERSION}'/' Makefile
|
sed -r -i -e 's/^(COSIGN_VERSION := ).*/\1'${COSIGN_LATEST_VERSION}'/' Makefile
|
||||||
|
sed -r -i -e 's/^(QUILL_VERSION := ).*/\1'${QUILL_LATEST_VERSION}'/' Makefile
|
||||||
sed -r -i -e 's/^(GLOW_VERSION := ).*/\1'${GLOW_LATEST_VERSION}'/' Makefile
|
sed -r -i -e 's/^(GLOW_VERSION := ).*/\1'${GLOW_LATEST_VERSION}'/' Makefile
|
||||||
|
|
||||||
# export the versions for use with create-pull-request
|
# export the versions for use with create-pull-request
|
||||||
@ -49,16 +54,17 @@ jobs:
|
|||||||
echo "GOSIMPORTS=$GOSIMPORTS_LATEST_VERSION" >> $GITHUB_OUTPUT
|
echo "GOSIMPORTS=$GOSIMPORTS_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "YAJSV=$YAJSV_LATEST_VERSION" >> $GITHUB_OUTPUT
|
echo "YAJSV=$YAJSV_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "COSIGN=$COSIGN_LATEST_VERSION" >> $GITHUB_OUTPUT
|
echo "COSIGN=$COSIGN_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
echo "QUILL=$QUILL_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||||
echo "GLOW=GLOW_LATEST_VERSION" >> $GITHUB_OUTPUT
|
echo "GLOW=GLOW_LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||||
id: latest-versions
|
id: latest-versions
|
||||||
|
|
||||||
- uses: tibdex/github-app-token@v1
|
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
|
||||||
id: generate-token
|
id: generate-token
|
||||||
with:
|
with:
|
||||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@v5
|
- uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 #v5.0.2
|
||||||
with:
|
with:
|
||||||
signoff: true
|
signoff: true
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
@ -74,6 +80,7 @@ jobs:
|
|||||||
- [gosimports ${{ steps.latest-versions.outputs.GOSIMPORTS }}](https://github.com/rinchsan/gosimports/releases/tag/${{ steps.latest-versions.outputs.GOSIMPORTS }})
|
- [gosimports ${{ steps.latest-versions.outputs.GOSIMPORTS }}](https://github.com/rinchsan/gosimports/releases/tag/${{ steps.latest-versions.outputs.GOSIMPORTS }})
|
||||||
- [yajsv ${{ steps.latest-versions.outputs.YAJSV }}](https://github.com/neilpa/yajsv/releases/tag/${{ steps.latest-versions.outputs.YAJSV }})
|
- [yajsv ${{ steps.latest-versions.outputs.YAJSV }}](https://github.com/neilpa/yajsv/releases/tag/${{ steps.latest-versions.outputs.YAJSV }})
|
||||||
- [cosign ${{ steps.latest-versions.outputs.COSIGN }}](https://github.com/sigstore/cosign/releases/tag/${{ steps.latest-versions.outputs.COSIGN }})
|
- [cosign ${{ steps.latest-versions.outputs.COSIGN }}](https://github.com/sigstore/cosign/releases/tag/${{ steps.latest-versions.outputs.COSIGN }})
|
||||||
|
- [quill ${{ steps.latest-versions.outputs.QUILL }}](https://github.com/anchore/quill/releases/tag/${{ steps.latest-versions.outputs.QUILL }})
|
||||||
- [glow ${{ steps.latest-versions.outputs.GLOW }}](https://github.com/charmbracelet/glow/releases/tag/${{ steps.latest-versions.outputs.GLOW }})
|
- [glow ${{ steps.latest-versions.outputs.GLOW }}](https://github.com/charmbracelet/glow/releases/tag/${{ steps.latest-versions.outputs.GLOW }})
|
||||||
This is an auto-generated pull request to update all of the bootstrap tools to the latest versions.
|
This is an auto-generated pull request to update all of the bootstrap tools to the latest versions.
|
||||||
token: ${{ steps.generate-token.outputs.token }}
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
|||||||
@ -5,8 +5,11 @@ on:
|
|||||||
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: "1.20.x"
|
GO_VERSION: "1.21.x"
|
||||||
GO_STABLE_VERSION: true
|
GO_STABLE_VERSION: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@ -14,9 +17,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'anchore/syft' # only run for main repo
|
if: github.repository == 'anchore/syft' # only run for main repo
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@3df4ab11eba7bda6032a0b82a6bb43b11571feac #v4.0.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
stable: ${{ env.GO_STABLE_VERSION }}
|
stable: ${{ env.GO_STABLE_VERSION }}
|
||||||
@ -24,13 +27,13 @@ jobs:
|
|||||||
- run: |
|
- run: |
|
||||||
make generate-cpe-dictionary-index
|
make generate-cpe-dictionary-index
|
||||||
|
|
||||||
- uses: tibdex/github-app-token@v1
|
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
|
||||||
id: generate-token
|
id: generate-token
|
||||||
with:
|
with:
|
||||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@v5
|
- uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 #v5.0.2
|
||||||
with:
|
with:
|
||||||
signoff: true
|
signoff: true
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
|
|||||||
13
.github/workflows/update-stereoscope-release.yml
vendored
13
.github/workflows/update-stereoscope-release.yml
vendored
@ -6,17 +6,20 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
GO_VERSION: "1.20.x"
|
GO_VERSION: "1.21.x"
|
||||||
GO_STABLE_VERSION: true
|
GO_STABLE_VERSION: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
upgrade-stereoscope:
|
upgrade-stereoscope:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.repository == 'anchore/syft' # only run for main repo
|
if: github.repository == 'anchore/syft' # only run for main repo
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
- uses: actions/setup-go@v4
|
- uses: actions/setup-go@93397bea11091df50f3d7e59dc26a7711a8bcfbe #v4.1.0
|
||||||
with:
|
with:
|
||||||
go-version: ${{ env.GO_VERSION }}
|
go-version: ${{ env.GO_VERSION }}
|
||||||
stable: ${{ env.GO_STABLE_VERSION }}
|
stable: ${{ env.GO_STABLE_VERSION }}
|
||||||
@ -32,13 +35,13 @@ jobs:
|
|||||||
echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_OUTPUT
|
echo "LATEST_VERSION=$LATEST_VERSION" >> $GITHUB_OUTPUT
|
||||||
id: latest-version
|
id: latest-version
|
||||||
|
|
||||||
- uses: tibdex/github-app-token@v1
|
- uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a #v2.1.0
|
||||||
id: generate-token
|
id: generate-token
|
||||||
with:
|
with:
|
||||||
app_id: ${{ secrets.TOKEN_APP_ID }}
|
app_id: ${{ secrets.TOKEN_APP_ID }}
|
||||||
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
- uses: peter-evans/create-pull-request@v5
|
- uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 #v5.0.2
|
||||||
with:
|
with:
|
||||||
signoff: true
|
signoff: true
|
||||||
delete-branch: true
|
delete-branch: true
|
||||||
|
|||||||
67
.github/workflows/validations.yaml
vendored
67
.github/workflows/validations.yaml
vendored
@ -7,14 +7,16 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
|
||||||
jobs:
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
Static-Analysis:
|
Static-Analysis:
|
||||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||||
name: "Static analysis"
|
name: "Static analysis"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
- name: Bootstrap environment
|
- name: Bootstrap environment
|
||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
@ -28,37 +30,37 @@ jobs:
|
|||||||
name: "Unit tests"
|
name: "Unit tests"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
- name: Bootstrap environment
|
- name: Bootstrap environment
|
||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
||||||
- name: Restore Java test-fixture cache
|
- name: Restore Java test-fixture cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages
|
path: syft/pkg/cataloger/java/test-fixtures/java-builds/packages
|
||||||
key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-java-cache-${{ hashFiles( 'syft/pkg/cataloger/java/test-fixtures/java-builds/cache.fingerprint' ) }}
|
||||||
|
|
||||||
- name: Restore RPM test-fixture cache
|
- name: Restore RPM test-fixture cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/rpm/test-fixtures/rpms
|
path: syft/pkg/cataloger/rpm/test-fixtures/rpms
|
||||||
key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/rpm/test-fixtures/rpms.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-rpm-cache-${{ hashFiles( 'syft/pkg/cataloger/rpm/test-fixtures/rpms.fingerprint' ) }}
|
||||||
|
|
||||||
- name: Restore go binary test-fixture cache
|
- name: Restore go binary test-fixture cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries
|
path: syft/pkg/cataloger/golang/test-fixtures/archs/binaries
|
||||||
key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-go-binaries-cache-${{ hashFiles( 'syft/pkg/cataloger/golang/test-fixtures/archs/binaries.fingerprint' ) }}
|
||||||
|
|
||||||
- name: Restore binary cataloger test-fixture cache
|
- name: Restore binary cataloger test-fixture cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/binary/test-fixtures/classifiers/dynamic
|
path: syft/pkg/cataloger/binary/test-fixtures/classifiers/dynamic
|
||||||
key: ${{ runner.os }}-unit-binary-cataloger-cache-${{ hashFiles( 'syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-binary-cataloger-cache-${{ hashFiles( 'syft/pkg/cataloger/binary/test-fixtures/cache.fingerprint' ) }}
|
||||||
|
|
||||||
- name: Restore Kernel test-fixture cache
|
- name: Restore Kernel test-fixture cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: syft/pkg/cataloger/kernel/test-fixtures/cache
|
path: syft/pkg/cataloger/kernel/test-fixtures/cache
|
||||||
key: ${{ runner.os }}-unit-kernel-cache-${{ hashFiles( 'syft/pkg/cataloger/kernel/test-fixtures/cache.fingerprint' ) }}
|
key: ${{ runner.os }}-unit-kernel-cache-${{ hashFiles( 'syft/pkg/cataloger/kernel/test-fixtures/cache.fingerprint' ) }}
|
||||||
@ -72,7 +74,7 @@ jobs:
|
|||||||
name: "Integration tests"
|
name: "Integration tests"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
- name: Bootstrap environment
|
- name: Bootstrap environment
|
||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
@ -81,7 +83,7 @@ jobs:
|
|||||||
run: make validate-cyclonedx-schema
|
run: make validate-cyclonedx-schema
|
||||||
|
|
||||||
- name: Restore integration test cache
|
- name: Restore integration test cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/test/integration/test-fixtures/cache
|
path: ${{ github.workspace }}/test/integration/test-fixtures/cache
|
||||||
key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }}
|
key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }}
|
||||||
@ -95,13 +97,13 @@ jobs:
|
|||||||
name: "CLI tests (Linux)"
|
name: "CLI tests (Linux)"
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
- name: Bootstrap environment
|
- name: Bootstrap environment
|
||||||
uses: ./.github/actions/bootstrap
|
uses: ./.github/actions/bootstrap
|
||||||
|
|
||||||
- name: Restore CLI test-fixture cache
|
- name: Restore CLI test-fixture cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/test/cli/test-fixtures/cache
|
path: ${{ github.workspace }}/test/cli/test-fixtures/cache
|
||||||
key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }}
|
key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }}
|
||||||
@ -110,45 +112,16 @@ jobs:
|
|||||||
run: make cli
|
run: make cli
|
||||||
|
|
||||||
|
|
||||||
# Build-Snapshot-Artifacts:
|
|
||||||
# name: "Build snapshot artifacts"
|
|
||||||
# runs-on: ubuntu-20.04
|
|
||||||
# steps:
|
|
||||||
# - uses: actions/checkout@v3
|
|
||||||
#
|
|
||||||
# - name: Bootstrap environment
|
|
||||||
# uses: ./.github/actions/bootstrap
|
|
||||||
# with:
|
|
||||||
# # why have another build cache key? We don't want unit/integration/etc test build caches to replace
|
|
||||||
# # the snapshot build cache, which includes builds for all OSs and architectures. As long as this key is
|
|
||||||
# # unique from the build-cache-key-prefix in other CI jobs, we should be fine.
|
|
||||||
# #
|
|
||||||
# # note: ideally this value should match what is used in release (just to help with build times).
|
|
||||||
# build-cache-key-prefix: "snapshot"
|
|
||||||
# bootstrap-apt-packages: ""
|
|
||||||
#
|
|
||||||
# - name: Build snapshot artifacts
|
|
||||||
# run: make snapshot
|
|
||||||
#
|
|
||||||
# # 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@v3
|
|
||||||
# with:
|
|
||||||
# path: snapshot
|
|
||||||
# key: snapshot-build-${{ github.run_id }}
|
|
||||||
|
|
||||||
|
|
||||||
Acceptance-Linux:
|
Acceptance-Linux:
|
||||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||||
name: "Acceptance tests (Linux)"
|
name: "Acceptance tests (Linux)"
|
||||||
# needs: [Build-Snapshot-Artifacts]
|
# needs: [Build-Snapshot-Artifacts]
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
# - name: Download snapshot build
|
# - name: Download snapshot build
|
||||||
# uses: actions/cache/restore@v3
|
# uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
# with:
|
# with:
|
||||||
# path: snapshot
|
# path: snapshot
|
||||||
# key: snapshot-build-${{ github.run_id }}
|
# key: snapshot-build-${{ github.run_id }}
|
||||||
@ -158,7 +131,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Restore install.sh test image cache
|
- name: Restore install.sh test image cache
|
||||||
id: install-test-image-cache
|
id: install-test-image-cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: ${{ github.workspace }}/test/install/cache
|
path: ${{ github.workspace }}/test/install/cache
|
||||||
key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }}
|
key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }}
|
||||||
@ -181,17 +154,17 @@ jobs:
|
|||||||
# needs: [Build-Snapshot-Artifacts]
|
# needs: [Build-Snapshot-Artifacts]
|
||||||
runs-on: macos-latest
|
runs-on: macos-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 #v4.1.0
|
||||||
|
|
||||||
# - name: Download snapshot build
|
# - name: Download snapshot build
|
||||||
# uses: actions/cache/restore@v3
|
# uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
# with:
|
# with:
|
||||||
# path: snapshot
|
# path: snapshot
|
||||||
# key: snapshot-build-${{ github.run_id }}
|
# key: snapshot-build-${{ github.run_id }}
|
||||||
|
|
||||||
- name: Restore docker image cache for compare testing
|
- name: Restore docker image cache for compare testing
|
||||||
id: mac-compare-testing-cache
|
id: mac-compare-testing-cache
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
|
||||||
with:
|
with:
|
||||||
path: image.tar
|
path: image.tar
|
||||||
key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }}
|
key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }}
|
||||||
|
|||||||
36
.gitignore
vendored
36
.gitignore
vendored
@ -1,17 +1,39 @@
|
|||||||
|
# local development tailoring
|
||||||
go.work
|
go.work
|
||||||
go.work.sum
|
go.work.sum
|
||||||
|
.tool-versions
|
||||||
|
|
||||||
|
# app configuration
|
||||||
|
/.syft.yaml
|
||||||
|
|
||||||
|
# tool and bin directories
|
||||||
|
.tmp/
|
||||||
|
bin/
|
||||||
/bin
|
/bin
|
||||||
/.bin
|
/.bin
|
||||||
/build
|
/build
|
||||||
CHANGELOG.md
|
|
||||||
VERSION
|
|
||||||
/test/results
|
|
||||||
/dist
|
/dist
|
||||||
/snapshot
|
/snapshot
|
||||||
.server/
|
|
||||||
|
# changelog generation
|
||||||
|
CHANGELOG.md
|
||||||
|
VERSION
|
||||||
|
|
||||||
|
# IDE configuration
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
.server/
|
||||||
.history/
|
.history/
|
||||||
|
|
||||||
|
# test related
|
||||||
*.fingerprint
|
*.fingerprint
|
||||||
|
/test/results
|
||||||
|
coverage.txt
|
||||||
|
*.log
|
||||||
|
test/integration/test-fixtures/**/go.sum
|
||||||
|
|
||||||
|
# probable archives
|
||||||
|
.images
|
||||||
*.tar
|
*.tar
|
||||||
*.jar
|
*.jar
|
||||||
*.war
|
*.war
|
||||||
@ -19,13 +41,7 @@ VERSION
|
|||||||
*.jpi
|
*.jpi
|
||||||
*.hpi
|
*.hpi
|
||||||
*.zip
|
*.zip
|
||||||
.idea/
|
|
||||||
*.iml
|
*.iml
|
||||||
*.log
|
|
||||||
.images
|
|
||||||
.tmp/
|
|
||||||
coverage.txt
|
|
||||||
bin/
|
|
||||||
|
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins
|
||||||
*.exe
|
*.exe
|
||||||
|
|||||||
@ -24,10 +24,10 @@ builds:
|
|||||||
-w
|
-w
|
||||||
-s
|
-s
|
||||||
-extldflags '-static'
|
-extldflags '-static'
|
||||||
-X github.com/anchore/syft/internal/version.version={{.Version}}
|
-X main.version={{.Version}}
|
||||||
-X github.com/anchore/syft/internal/version.gitCommit={{.Commit}}
|
-X main.gitCommit={{.Commit}}
|
||||||
-X github.com/anchore/syft/internal/version.buildDate={{.Date}}
|
-X main.buildDate={{.Date}}
|
||||||
-X github.com/anchore/syft/internal/version.gitDescription={{.Summary}}
|
-X main.gitDescription={{.Summary}}
|
||||||
|
|
||||||
- id: darwin-build
|
- id: darwin-build
|
||||||
dir: ./cmd/syft
|
dir: ./cmd/syft
|
||||||
|
|||||||
@ -153,56 +153,47 @@ sequenceDiagram
|
|||||||
|
|
||||||
### Syft Catalogers
|
### Syft Catalogers
|
||||||
|
|
||||||
##### Summary
|
Catalogers are the way in which syft is able to identify and construct packages given a set a targeted list of files.
|
||||||
|
For example, a cataloger can ask syft for all `package-lock.json` files in order to parse and raise up javascript packages
|
||||||
Catalogers are the way in which syft is able to identify and construct packages given some amount of source metadata.
|
(see [how file globs](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) and
|
||||||
For example, Syft can locate and process `package-lock.json` files when performing filesystem scans.
|
[file parser functions](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) are used
|
||||||
See: [how to specify file globs](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21)
|
for a quick example).
|
||||||
and an implementation of the [package-lock.json parser](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/javascript/cataloger.go#L16-L21) for a quick review.
|
|
||||||
|
|
||||||
From a high level catalogers have the following properties:
|
From a high level catalogers have the following properties:
|
||||||
|
|
||||||
- They are independent from one another. The java cataloger has no idea of the processes, assumptions, or results of the python cataloger, for example.
|
- _They are independent from one another_. The java cataloger has no idea of the processes, assumptions, or results of the python cataloger, for example.
|
||||||
|
|
||||||
- They do not know what source is being analyzed. Are we analyzing a local directory? an image? if so, the squashed representation or all layers? The catalogers do not know the answers to these questions. Only that there is an interface to query for file paths and contents from an underlying "source" being scanned.
|
- _They do not know what source is being analyzed_. Are we analyzing a local directory? an image? if so, the squashed representation or all layers? The catalogers do not know the answers to these questions. Only that there is an interface to query for file paths and contents from an underlying "source" being scanned.
|
||||||
|
|
||||||
|
- _Packages created by the cataloger should not be mutated after they are created_. There is one exception made for adding CPEs to a package after the cataloging phase, but that will most likely be moved back into the cataloger in the future.
|
||||||
|
|
||||||
- Packages created by the cataloger should not be mutated after they are created. There is one exception made for adding CPEs to a package after the cataloging phase, but that will most likely be moved back into the cataloger in the future.
|
|
||||||
|
|
||||||
#### Building a new Cataloger
|
#### Building a new Cataloger
|
||||||
|
|
||||||
Catalogers must fulfill the interface [found here](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger.go).
|
Catalogers must fulfill the [`pkg.Cataloger` interface](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger.go) in order to add packages to the SBOM.
|
||||||
This means that when building a new cataloger, the new struct must implement both method signatures of `Catalog` and `Name`.
|
All catalogers should be added to:
|
||||||
|
- the [global list of catalogers](https://github.com/anchore/syft/blob/9995950c70e849f9921919faffbfcf46401f71f3/syft/pkg/cataloger/cataloger.go#L92-L125)
|
||||||
|
- at least one source-specific list, today the two lists are [directory catalogers and image catalogers](https://github.com/anchore/syft/blob/9995950c70e849f9921919faffbfcf46401f71f3/syft/pkg/cataloger/cataloger.go#L39-L89)
|
||||||
|
|
||||||
A top level view of the functions that construct all the catalogers can be found [here](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/cataloger.go).
|
For reference, catalogers are [invoked within syft](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/catalog.go#L41-L100) one after the other, and can be invoked in parallel.
|
||||||
When an author has finished writing a new cataloger this is the spot to plug in the new catalog constructor.
|
|
||||||
|
|
||||||
For a top level view of how the catalogers are used see [this function](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/catalog.go#L41-L100) as a reference. It ranges over all catalogers passed as an argument and invokes the `Catalog` method:
|
`generic.NewCataloger` is an abstraction syft used to make writing common components easier (see the [apkdb cataloger](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/cataloger.go) for example usage).
|
||||||
|
It takes the following information as input:
|
||||||
|
- A `catalogerName` to identify the cataloger uniquely among all other catalogers.
|
||||||
|
- Pairs of file globs as well as parser functions to parse those files. These parser functions return a slice of [`pkg.Package`](https://github.com/anchore/syft/blob/9995950c70e849f9921919faffbfcf46401f71f3/syft/pkg/package.go#L19) as well as a slice of [`artifact.Relationship`](https://github.com/anchore/syft/blob/9995950c70e849f9921919faffbfcf46401f71f3/syft/artifact/relationship.go#L31) to describe how the returned packages are related. See this [the apkdb cataloger parser function](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L22-L102) as an example.
|
||||||
|
|
||||||
Each cataloger has its own `Catalog` method, but this does not mean that they are all vastly different.
|
Identified packages share a common `pkg.Package` struct so be sure that when the new cataloger is constructing a new package it is using the [`Package` struct](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/package.go#L16-L31).
|
||||||
Take a look at the `apkdb` cataloger for alpine to see how it [constructs a generic.NewCataloger](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/cataloger.go).
|
If you want to return more information than what is available on the `pkg.Package` struct then you can do so in the `pkg.Package.Metadata` section of the struct, which is unique for each [`pkg.Type`](https://github.com/anchore/syft/blob/v0.70.0/syft/pkg/type.go).
|
||||||
|
See [the `pkg` package](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg) for examples of the different metadata types that are supported today.
|
||||||
`generic.NewCataloger` is an abstraction syft uses to make writing common components easier. First, it takes the `catalogerName` to identify the cataloger.
|
|
||||||
On the other side of the call it uses two key pieces which inform the cataloger how to identify and return packages, the `globPatterns` and the `parseFunction`:
|
|
||||||
- The first piece is a `parseByGlob` matching pattern used to identify the files that contain the package metadata.
|
|
||||||
See [here for the APK example](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/apk_metadata.go#L16-L41).
|
|
||||||
- The other is a `parseFunction` which informs the cataloger what to do when it has found one of the above matches files.
|
|
||||||
See this [link for an example](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L22-L102).
|
|
||||||
|
|
||||||
If you're unsure about using the `Generic Cataloger` and think the use case being filled requires something more custom
|
|
||||||
just file an issue or ask in our slack, and we'd be more than happy to help on the design.
|
|
||||||
|
|
||||||
Identified packages share a common struct so be sure that when the new cataloger is constructing a new package it is using the [`Package` struct](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/package.go#L16-L31).
|
|
||||||
|
|
||||||
Metadata Note: Identified packages are also assigned specific metadata that can be unique to their environment.
|
|
||||||
See [this folder](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg) for examples of the different metadata types.
|
|
||||||
These are plugged into the `MetadataType` and `Metadata` fields in the above struct. `MetadataType` informs which type is being used. `Metadata` is an interface converted to that type.
|
These are plugged into the `MetadataType` and `Metadata` fields in the above struct. `MetadataType` informs which type is being used. `Metadata` is an interface converted to that type.
|
||||||
|
|
||||||
Finally, here is an example of where the package construction is done in the apk cataloger. The first link is where `newPackage` is called in the `parseFunction`. The second link shows the package construction:
|
Finally, here is an example of where the package construction is done within the apk cataloger:
|
||||||
- [Call for new package](https://github.com/anchore/syft/blob/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L106)
|
- [Calling the APK package constructor from the parser function](https://github.com/anchore/syft/blob/v0.70.0/syft/pkg/cataloger/apkdb/parse_apk_db.go#L106)
|
||||||
- [APK Package Constructor](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/package.go#L12-L27)
|
- [The APK package constructor itself](https://github.com/anchore/syft/tree/v0.70.0/syft/pkg/cataloger/apkdb/package.go#L12-L27)
|
||||||
|
|
||||||
|
Interested in building a new cataloger? Checkout the [list of issues with the `new-cataloger` label](https://github.com/anchore/syft/issues?q=is%3Aopen+is%3Aissue+label%3Anew-cataloger+no%3Aassignee)!
|
||||||
|
If you have questions about implementing a cataloger feel free to file an issue or reach out to us [on slack](https://anchore.com/slack)!
|
||||||
|
|
||||||
If you have more questions about implementing a cataloger or questions about one you might be currently working
|
|
||||||
always feel free to file an issue or reach out to us [on slack](https://anchore.com/slack).
|
|
||||||
|
|
||||||
#### Searching for files
|
#### Searching for files
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
FROM gcr.io/distroless/static-debian11:debug AS build
|
FROM gcr.io/distroless/static-debian11:debug@sha256:a0a404776dec98be120089ae42bbdfbe48c177921d856937d124d48eb8c0b951 AS build
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
# needed for version check HTTPS request
|
# needed for version check HTTPS request
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
FROM gcr.io/distroless/static-debian11:debug
|
FROM gcr.io/distroless/static-debian11:debug@sha256:a0a404776dec98be120089ae42bbdfbe48c177921d856937d124d48eb8c0b951
|
||||||
|
|
||||||
# create the /tmp dir, which is needed for image content cache
|
# create the /tmp dir, which is needed for image content cache
|
||||||
WORKDIR /tmp
|
WORKDIR /tmp
|
||||||
|
|||||||
13
Makefile
13
Makefile
@ -10,14 +10,14 @@ CHRONICLE_CMD = $(TEMP_DIR)/chronicle
|
|||||||
GLOW_CMD = $(TEMP_DIR)/glow
|
GLOW_CMD = $(TEMP_DIR)/glow
|
||||||
|
|
||||||
# Tool versions #################################
|
# Tool versions #################################
|
||||||
GOLANGCILINT_VERSION := v1.54.0
|
GOLANGCILINT_VERSION := v1.54.2
|
||||||
GOSIMPORTS_VERSION := v0.3.8
|
GOSIMPORTS_VERSION := v0.3.8
|
||||||
BOUNCER_VERSION := v0.4.0
|
BOUNCER_VERSION := v0.4.0
|
||||||
CHRONICLE_VERSION := v0.7.0
|
CHRONICLE_VERSION := v0.8.0
|
||||||
GORELEASER_VERSION := v1.20.0
|
GORELEASER_VERSION := v1.21.2
|
||||||
YAJSV_VERSION := v1.4.1
|
YAJSV_VERSION := v1.4.1
|
||||||
COSIGN_VERSION := v2.1.1
|
COSIGN_VERSION := v2.2.0
|
||||||
QUILL_VERSION := v0.2.0
|
QUILL_VERSION := v0.4.1
|
||||||
GLOW_VERSION := v1.5.1
|
GLOW_VERSION := v1.5.1
|
||||||
|
|
||||||
# Formatting variables #################################
|
# Formatting variables #################################
|
||||||
@ -146,13 +146,14 @@ check-json-schema-drift:
|
|||||||
.PHONY: unit
|
.PHONY: unit
|
||||||
unit: $(TEMP_DIR) fixtures ## Run unit tests (with coverage)
|
unit: $(TEMP_DIR) fixtures ## Run unit tests (with coverage)
|
||||||
$(call title,Running unit tests)
|
$(call title,Running unit tests)
|
||||||
go test -coverprofile $(TEMP_DIR)/unit-coverage-details.txt $(shell go list ./... | grep -v anchore/syft/test)
|
go test -race -coverprofile $(TEMP_DIR)/unit-coverage-details.txt $(shell go list ./... | grep -v anchore/syft/test)
|
||||||
@.github/scripts/coverage.py $(COVERAGE_THRESHOLD) $(TEMP_DIR)/unit-coverage-details.txt
|
@.github/scripts/coverage.py $(COVERAGE_THRESHOLD) $(TEMP_DIR)/unit-coverage-details.txt
|
||||||
|
|
||||||
.PHONY: integration
|
.PHONY: integration
|
||||||
integration: ## Run integration tests
|
integration: ## Run integration tests
|
||||||
$(call title,Running integration tests)
|
$(call title,Running integration tests)
|
||||||
go test -v ./test/integration
|
go test -v ./test/integration
|
||||||
|
go run -race cmd/syft/main.go alpine:latest
|
||||||
|
|
||||||
.PHONY: validate-cyclonedx-schema
|
.PHONY: validate-cyclonedx-schema
|
||||||
validate-cyclonedx-schema:
|
validate-cyclonedx-schema:
|
||||||
|
|||||||
31
README.md
31
README.md
@ -139,6 +139,7 @@ Sources can be explicitly provided with a scheme:
|
|||||||
```
|
```
|
||||||
docker:yourrepo/yourimage:tag use images from the Docker daemon
|
docker:yourrepo/yourimage:tag use images from the Docker daemon
|
||||||
podman:yourrepo/yourimage:tag use images from the Podman daemon
|
podman:yourrepo/yourimage:tag use images from the Podman daemon
|
||||||
|
containerd:yourrepo/yourimage:tag use images from the Containerd daemon
|
||||||
docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save"
|
docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save"
|
||||||
oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise)
|
oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise)
|
||||||
oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
|
oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
|
||||||
@ -389,11 +390,11 @@ syft convert <ORIGINAL-SBOM-FILE> -o <NEW-SBOM-FORMAT>[=<NEW-SBOM-FILE>]
|
|||||||
This feature is experimental and data might be lost when converting formats. Packages are the main SBOM component easily transferable across formats, whereas files and relationships, as well as other information Syft doesn't support, are more likely to be lost.
|
This feature is experimental and data might be lost when converting formats. Packages are the main SBOM component easily transferable across formats, whereas files and relationships, as well as other information Syft doesn't support, are more likely to be lost.
|
||||||
|
|
||||||
We support formats with wide community usage AND good encode/decode support by Syft. The supported formats are:
|
We support formats with wide community usage AND good encode/decode support by Syft. The supported formats are:
|
||||||
- Syft JSON
|
- Syft JSON (```-o syft-json```)
|
||||||
- SPDX 2.2 JSON
|
- SPDX 2.2 JSON (```-o spdx-json```)
|
||||||
- SPDX 2.2 tag-value
|
- SPDX 2.2 tag-value (```-o spdx-tag-value```)
|
||||||
- CycloneDX 1.4 JSON
|
- CycloneDX 1.4 JSON (```-o cyclonedx-json```)
|
||||||
- CycloneDX 1.4 XML
|
- CycloneDX 1.4 XML (```-o cyclonedx-xml```)
|
||||||
|
|
||||||
Conversion example:
|
Conversion example:
|
||||||
```sh
|
```sh
|
||||||
@ -671,7 +672,7 @@ source:
|
|||||||
# the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")
|
# the file digest algorithms to use on the scanned file (options: "md5", "sha1", "sha224", "sha256", "sha384", "sha512")
|
||||||
digests: ["sha256"]
|
digests: ["sha256"]
|
||||||
|
|
||||||
# options when pulling directly from a registry via the "registry:" scheme
|
# options when pulling directly from a registry via the "registry:" or "containerd:" scheme
|
||||||
registry:
|
registry:
|
||||||
# skip TLS verification when communicating with the registry
|
# skip TLS verification when communicating with the registry
|
||||||
# SYFT_REGISTRY_INSECURE_SKIP_TLS_VERIFY env var
|
# SYFT_REGISTRY_INSECURE_SKIP_TLS_VERIFY env var
|
||||||
@ -681,19 +682,35 @@ registry:
|
|||||||
# SYFT_REGISTRY_INSECURE_USE_HTTP env var
|
# SYFT_REGISTRY_INSECURE_USE_HTTP env var
|
||||||
insecure-use-http: false
|
insecure-use-http: false
|
||||||
|
|
||||||
|
# filepath to a CA certificate (or directory containing *.crt, *.cert, *.pem) used to generate the client certificate
|
||||||
|
# SYFT_REGISTRY_CA_CERT env var
|
||||||
|
ca-cert: ""
|
||||||
|
|
||||||
# credentials for specific registries
|
# credentials for specific registries
|
||||||
auth:
|
auth:
|
||||||
# the URL to the registry (e.g. "docker.io", "localhost:5000", etc.)
|
# the URL to the registry (e.g. "docker.io", "localhost:5000", etc.)
|
||||||
# SYFT_REGISTRY_AUTH_AUTHORITY env var
|
# SYFT_REGISTRY_AUTH_AUTHORITY env var
|
||||||
- authority: ""
|
- authority: ""
|
||||||
|
|
||||||
# SYFT_REGISTRY_AUTH_USERNAME env var
|
# SYFT_REGISTRY_AUTH_USERNAME env var
|
||||||
username: ""
|
username: ""
|
||||||
|
|
||||||
# SYFT_REGISTRY_AUTH_PASSWORD env var
|
# SYFT_REGISTRY_AUTH_PASSWORD env var
|
||||||
password: ""
|
password: ""
|
||||||
|
|
||||||
# note: token and username/password are mutually exclusive
|
# note: token and username/password are mutually exclusive
|
||||||
# SYFT_REGISTRY_AUTH_TOKEN env var
|
# SYFT_REGISTRY_AUTH_TOKEN env var
|
||||||
token: ""
|
token: ""
|
||||||
# - ... # note, more credentials can be provided via config file only
|
|
||||||
|
# filepath to the client certificate used for TLS authentication to the registry
|
||||||
|
# SYFT_REGISTRY_AUTH_TLS_CERT env var
|
||||||
|
tls-cert: ""
|
||||||
|
|
||||||
|
# filepath to the client key used for TLS authentication to the registry
|
||||||
|
# SYFT_REGISTRY_AUTH_TLS_KEY env var
|
||||||
|
tls-key: ""
|
||||||
|
|
||||||
|
# - ... # note, more credentials can be provided via config file only (not env vars)
|
||||||
|
|
||||||
# generate an attested SBOM
|
# generate an attested SBOM
|
||||||
attest:
|
attest:
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/attest"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
attestExample = ` {{.appName}} {{.command}} --output [FORMAT] alpine:latest defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry
|
|
||||||
`
|
|
||||||
attestSchemeHelp = "\n" + indent + schemeHelpHeader + "\n" + imageSchemeHelp
|
|
||||||
attestHelp = attestExample + attestSchemeHelp
|
|
||||||
)
|
|
||||||
|
|
||||||
func Attest(v *viper.Viper, app *config.Application, ro *options.RootOptions, po *options.PackagesOptions, ao *options.AttestOptions) *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "attest --output [FORMAT] <IMAGE>",
|
|
||||||
Short: "Generate an SBOM as an attestation for the given [SOURCE] container image",
|
|
||||||
Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from a container image as the predicate of an in-toto attestation that will be uploaded to the image registry",
|
|
||||||
Example: internal.Tprintf(attestHelp, map[string]interface{}{
|
|
||||||
"appName": internal.ApplicationName,
|
|
||||||
"command": "attest",
|
|
||||||
}),
|
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if err := app.LoadAllValues(v, ro.Config); err != nil {
|
|
||||||
return fmt.Errorf("unable to load configuration: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
newLogWrapper(app)
|
|
||||||
logApplicationConfig(app)
|
|
||||||
return validateArgs(cmd, args)
|
|
||||||
},
|
|
||||||
SilenceUsage: true,
|
|
||||||
SilenceErrors: true,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if app.CheckForAppUpdate {
|
|
||||||
checkForApplicationUpdate()
|
|
||||||
// TODO: this is broke, the bus isn't available yet
|
|
||||||
}
|
|
||||||
|
|
||||||
return attest.Run(cmd.Context(), app, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// syft attest is an enhancement of the packages command, so it should have the same flags
|
|
||||||
err := po.AddFlags(cmd, v)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// syft attest has its own options not included as part of the packages command
|
|
||||||
err = ao.AddFlags(cmd, v)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
@ -1,261 +0,0 @@
|
|||||||
package attest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/wagoodman/go-partybus"
|
|
||||||
"github.com/wagoodman/go-progress"
|
|
||||||
"golang.org/x/exp/slices"
|
|
||||||
|
|
||||||
"github.com/anchore/stereoscope"
|
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/eventloop"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/packages"
|
|
||||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
|
||||||
"github.com/anchore/syft/internal/bus"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
"github.com/anchore/syft/internal/file"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/anchore/syft/syft"
|
|
||||||
"github.com/anchore/syft/syft/event"
|
|
||||||
"github.com/anchore/syft/syft/event/monitor"
|
|
||||||
"github.com/anchore/syft/syft/formats/syftjson"
|
|
||||||
"github.com/anchore/syft/syft/formats/table"
|
|
||||||
"github.com/anchore/syft/syft/sbom"
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Run(_ context.Context, app *config.Application, args []string) error {
|
|
||||||
err := ValidateOutputOptions(app)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: must be a container image
|
|
||||||
userInput := args[0]
|
|
||||||
|
|
||||||
_, err = exec.LookPath("cosign")
|
|
||||||
if err != nil {
|
|
||||||
// when cosign is not installed the error will be rendered like so:
|
|
||||||
// 2023/06/30 08:31:52 error during command execution: 'syft attest' requires cosign to be installed: exec: "cosign": executable file not found in $PATH
|
|
||||||
return fmt.Errorf("'syft attest' requires cosign to be installed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
eventBus := partybus.NewBus()
|
|
||||||
stereoscope.SetBus(eventBus)
|
|
||||||
syft.SetBus(eventBus)
|
|
||||||
subscription := eventBus.Subscribe()
|
|
||||||
|
|
||||||
return eventloop.EventLoop(
|
|
||||||
execWorker(app, userInput),
|
|
||||||
eventloop.SetupSignals(),
|
|
||||||
subscription,
|
|
||||||
stereoscope.Cleanup,
|
|
||||||
ui.Select(options.IsVerbose(app), app.Quiet)...,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildSBOM(app *config.Application, userInput string, errs chan error) (*sbom.SBOM, error) {
|
|
||||||
cfg := source.DetectConfig{
|
|
||||||
DefaultImageSource: app.DefaultImagePullSource,
|
|
||||||
}
|
|
||||||
detection, err := source.Detect(userInput, cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("could not deteremine source: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if detection.IsContainerImage() {
|
|
||||||
return nil, fmt.Errorf("attestations are only supported for oci images at this time")
|
|
||||||
}
|
|
||||||
|
|
||||||
var platform *image.Platform
|
|
||||||
|
|
||||||
if app.Platform != "" {
|
|
||||||
platform, err = image.NewPlatform(app.Platform)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid platform: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hashers, err := file.Hashers(app.Source.File.Digests...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid hash: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
src, err := detection.NewSource(
|
|
||||||
source.DetectionSourceConfig{
|
|
||||||
Alias: source.Alias{
|
|
||||||
Name: app.Source.Name,
|
|
||||||
Version: app.Source.Version,
|
|
||||||
},
|
|
||||||
RegistryOptions: app.Registry.ToOptions(),
|
|
||||||
Platform: platform,
|
|
||||||
Exclude: source.ExcludeConfig{
|
|
||||||
Paths: app.Exclusions,
|
|
||||||
},
|
|
||||||
DigestAlgorithms: hashers,
|
|
||||||
BasePath: app.BasePath,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if src != nil {
|
|
||||||
defer src.Close()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
s, err := packages.GenerateSBOM(src, errs, app)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if s == nil {
|
|
||||||
return nil, fmt.Errorf("no SBOM produced for %q", userInput)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:funlen
|
|
||||||
func execWorker(app *config.Application, userInput string) <-chan error {
|
|
||||||
errs := make(chan error)
|
|
||||||
go func() {
|
|
||||||
defer close(errs)
|
|
||||||
defer bus.Exit()
|
|
||||||
|
|
||||||
s, err := buildSBOM(app, userInput, errs)
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("unable to build SBOM: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// note: ValidateOutputOptions ensures that there is no more than one output type
|
|
||||||
o := app.Outputs[0]
|
|
||||||
|
|
||||||
f, err := os.CreateTemp("", o)
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("unable to create temp file: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer os.Remove(f.Name())
|
|
||||||
|
|
||||||
writer, err := options.MakeSBOMWriter(app.Outputs, f.Name(), app.OutputTemplatePath)
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("unable to create SBOM writer: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writer.Write(*s); err != nil {
|
|
||||||
errs <- fmt.Errorf("unable to write SBOM to temp file: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: what other validation here besides binary name?
|
|
||||||
cmd := "cosign"
|
|
||||||
if !commandExists(cmd) {
|
|
||||||
errs <- fmt.Errorf("unable to find cosign in PATH; make sure you have it installed")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select Cosign predicate type based on defined output type
|
|
||||||
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
|
|
||||||
var predicateType string
|
|
||||||
switch strings.ToLower(o) {
|
|
||||||
case "cyclonedx-json":
|
|
||||||
predicateType = "cyclonedx"
|
|
||||||
case "spdx-tag-value", "spdx-tv":
|
|
||||||
predicateType = "spdx"
|
|
||||||
case "spdx-json", "json":
|
|
||||||
predicateType = "spdxjson"
|
|
||||||
default:
|
|
||||||
predicateType = "custom"
|
|
||||||
}
|
|
||||||
|
|
||||||
args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType}
|
|
||||||
if app.Attest.Key != "" {
|
|
||||||
args = append(args, "--key", app.Attest.Key)
|
|
||||||
}
|
|
||||||
|
|
||||||
execCmd := exec.Command(cmd, args...)
|
|
||||||
execCmd.Env = os.Environ()
|
|
||||||
if app.Attest.Key != "" {
|
|
||||||
execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", app.Attest.Password))
|
|
||||||
} else {
|
|
||||||
// no key provided, use cosign's keyless mode
|
|
||||||
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
|
|
||||||
}
|
|
||||||
|
|
||||||
log.WithFields("cmd", strings.Join(execCmd.Args, " ")).Trace("creating attestation")
|
|
||||||
|
|
||||||
// bus adapter for ui to hook into stdout via an os pipe
|
|
||||||
r, w, err := os.Pipe()
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("unable to create os pipe: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer w.Close()
|
|
||||||
|
|
||||||
mon := progress.NewManual(-1)
|
|
||||||
|
|
||||||
bus.Publish(
|
|
||||||
partybus.Event{
|
|
||||||
Type: event.AttestationStarted,
|
|
||||||
Source: monitor.GenericTask{
|
|
||||||
Title: monitor.Title{
|
|
||||||
Default: "Create attestation",
|
|
||||||
WhileRunning: "Creating attestation",
|
|
||||||
OnSuccess: "Created attestation",
|
|
||||||
},
|
|
||||||
Context: "cosign",
|
|
||||||
},
|
|
||||||
Value: &monitor.ShellProgress{
|
|
||||||
Reader: r,
|
|
||||||
Progressable: mon,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
execCmd.Stdout = w
|
|
||||||
execCmd.Stderr = w
|
|
||||||
|
|
||||||
// attest the SBOM
|
|
||||||
err = execCmd.Run()
|
|
||||||
if err != nil {
|
|
||||||
mon.SetError(err)
|
|
||||||
errs <- fmt.Errorf("unable to attest SBOM: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
mon.SetCompleted()
|
|
||||||
}()
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateOutputOptions(app *config.Application) error {
|
|
||||||
err := packages.ValidateOutputOptions(app)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(app.Outputs) > 1 {
|
|
||||||
return fmt.Errorf("multiple SBOM format is not supported for attest at this time")
|
|
||||||
}
|
|
||||||
|
|
||||||
// cannot use table as default output format when using template output
|
|
||||||
if slices.Contains(app.Outputs, table.ID.String()) {
|
|
||||||
app.Outputs = []string{syftjson.ID.String()}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func commandExists(cmd string) bool {
|
|
||||||
_, err := exec.LookPath(cmd)
|
|
||||||
return err == nil
|
|
||||||
}
|
|
||||||
96
cmd/syft/cli/cli.go
Normal file
96
cmd/syft/cli/cli.go
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
package cli
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
cranecmd "github.com/google/go-containerregistry/cmd/crane/cmd"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/stereoscope"
|
||||||
|
"github.com/anchore/syft/cmd/syft/cli/commands"
|
||||||
|
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"
|
||||||
|
"github.com/anchore/syft/internal/redact"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Application constructs the `syft packages` command, aliases the root command to `syft packages`,
|
||||||
|
// and constructs the `syft power-user` command. It is also responsible for
|
||||||
|
// organizing flag usage and injecting the application config for each command.
|
||||||
|
// It also constructs the syft attest command and the syft version command.
|
||||||
|
// `RunE` is the earliest that the complete application configuration can be loaded.
|
||||||
|
func Application(id clio.Identification) clio.Application {
|
||||||
|
app, _ := create(id)
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command returns the root command for the syft CLI application. This is useful for embedding the entire syft CLI
|
||||||
|
// into an existing application.
|
||||||
|
func Command(id clio.Identification) *cobra.Command {
|
||||||
|
_, cmd := create(id)
|
||||||
|
return cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
func create(id clio.Identification) (clio.Application, *cobra.Command) {
|
||||||
|
clioCfg := clio.NewSetupConfig(id).
|
||||||
|
WithGlobalConfigFlag(). // add persistent -c <path> for reading an application config from
|
||||||
|
WithGlobalLoggingFlags(). // add persistent -v and -q flags tied to the logging config
|
||||||
|
WithConfigInRootHelp(). // --help on the root command renders the full application config in the help text
|
||||||
|
WithUIConstructor(
|
||||||
|
// select a UI based on the logging configuration and state of stdin (if stdin is a tty)
|
||||||
|
func(cfg clio.Config) ([]clio.UI, error) {
|
||||||
|
noUI := ui.None(cfg.Log.Quiet)
|
||||||
|
if !cfg.Log.AllowUI(os.Stdin) || cfg.Log.Quiet {
|
||||||
|
return []clio.UI{noUI}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return []clio.UI{
|
||||||
|
ui.New(cfg.Log.Quiet,
|
||||||
|
handler.New(handler.DefaultHandlerConfig()),
|
||||||
|
),
|
||||||
|
noUI,
|
||||||
|
}, nil
|
||||||
|
},
|
||||||
|
).
|
||||||
|
WithInitializers(
|
||||||
|
func(state *clio.State) error {
|
||||||
|
// clio is setting up and providing the bus, redact store, and logger to the application. Once loaded,
|
||||||
|
// we can hoist them into the internal packages for global use.
|
||||||
|
stereoscope.SetBus(state.Bus)
|
||||||
|
bus.Set(state.Bus)
|
||||||
|
|
||||||
|
redact.Set(state.RedactStore)
|
||||||
|
|
||||||
|
log.Set(state.Logger)
|
||||||
|
stereoscope.SetLogger(state.Logger)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
).
|
||||||
|
WithPostRuns(func(state *clio.State, err error) {
|
||||||
|
stereoscope.Cleanup()
|
||||||
|
})
|
||||||
|
|
||||||
|
app := clio.New(*clioCfg)
|
||||||
|
|
||||||
|
// since root is aliased as the packages cmd we need to construct this command first
|
||||||
|
// we also need the command to have information about the `root` options because of this alias
|
||||||
|
packagesCmd := commands.Packages(app)
|
||||||
|
|
||||||
|
// rootCmd is currently an alias for the packages command
|
||||||
|
rootCmd := commands.Root(app, packagesCmd)
|
||||||
|
|
||||||
|
// add sub-commands
|
||||||
|
rootCmd.AddCommand(
|
||||||
|
packagesCmd,
|
||||||
|
commands.PowerUser(app),
|
||||||
|
commands.Attest(app),
|
||||||
|
commands.Convert(app),
|
||||||
|
clio.VersionCommand(id),
|
||||||
|
cranecmd.NewCmdAuthLogin(id.Name), // syft login uses the same command as crane
|
||||||
|
)
|
||||||
|
|
||||||
|
return app, rootCmd
|
||||||
|
}
|
||||||
@ -1,167 +0,0 @@
|
|||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
cranecmd "github.com/google/go-containerregistry/cmd/crane/cmd"
|
|
||||||
"github.com/gookit/color"
|
|
||||||
logrusUpstream "github.com/sirupsen/logrus"
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/wagoodman/go-partybus"
|
|
||||||
|
|
||||||
"github.com/anchore/go-logger/adapter/logrus"
|
|
||||||
"github.com/anchore/stereoscope"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/bus"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/anchore/syft/internal/version"
|
|
||||||
"github.com/anchore/syft/syft"
|
|
||||||
"github.com/anchore/syft/syft/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
const indent = " "
|
|
||||||
|
|
||||||
// New constructs the `syft packages` command, aliases the root command to `syft packages`,
|
|
||||||
// and constructs the `syft power-user` command. It is also responsible for
|
|
||||||
// organizing flag usage and injecting the application config for each command.
|
|
||||||
// It also constructs the syft attest command and the syft version command.
|
|
||||||
|
|
||||||
// Because of how the `cobra` library behaves, the application's configuration is initialized
|
|
||||||
// at this level. Values from the config should only be used after `app.LoadAllValues` has been called.
|
|
||||||
// Cobra does not have knowledge of the user provided flags until the `RunE` block of each command.
|
|
||||||
// `RunE` is the earliest that the complete application configuration can be loaded.
|
|
||||||
func New() (*cobra.Command, error) {
|
|
||||||
app := &config.Application{}
|
|
||||||
|
|
||||||
// allow for nested options to be specified via environment variables
|
|
||||||
// e.g. pod.context = APPNAME_POD_CONTEXT
|
|
||||||
v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_")))
|
|
||||||
|
|
||||||
// since root is aliased as the packages cmd we need to construct this command first
|
|
||||||
// we also need the command to have information about the `root` options because of this alias
|
|
||||||
ro := &options.RootOptions{}
|
|
||||||
po := &options.PackagesOptions{}
|
|
||||||
ao := &options.AttestOptions{}
|
|
||||||
packagesCmd := Packages(v, app, ro, po)
|
|
||||||
|
|
||||||
// root options are also passed to the attestCmd so that a user provided config location can be discovered
|
|
||||||
poweruserCmd := PowerUser(v, app, ro)
|
|
||||||
convertCmd := Convert(v, app, ro, po)
|
|
||||||
attestCmd := Attest(v, app, ro, po, ao)
|
|
||||||
|
|
||||||
// rootCmd is currently an alias for the packages command
|
|
||||||
rootCmd := &cobra.Command{
|
|
||||||
Use: fmt.Sprintf("%s [SOURCE]", internal.ApplicationName),
|
|
||||||
Short: packagesCmd.Short,
|
|
||||||
Long: packagesCmd.Long,
|
|
||||||
Args: packagesCmd.Args,
|
|
||||||
Example: packagesCmd.Example,
|
|
||||||
SilenceUsage: true,
|
|
||||||
SilenceErrors: true,
|
|
||||||
RunE: packagesCmd.RunE,
|
|
||||||
Version: version.FromBuild().Version,
|
|
||||||
}
|
|
||||||
rootCmd.SetVersionTemplate(fmt.Sprintf("%s {{.Version}}\n", internal.ApplicationName))
|
|
||||||
|
|
||||||
// start adding flags to all the commands
|
|
||||||
err := ro.AddFlags(rootCmd, v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// package flags need to be decorated onto the rootCmd so that rootCmd can function as a packages alias
|
|
||||||
err = po.AddFlags(rootCmd, v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// poweruser also uses the packagesCmd flags since it is a specialized version of the command
|
|
||||||
err = po.AddFlags(poweruserCmd, v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// commands to add to root
|
|
||||||
cmds := []*cobra.Command{
|
|
||||||
packagesCmd,
|
|
||||||
poweruserCmd,
|
|
||||||
convertCmd,
|
|
||||||
attestCmd,
|
|
||||||
Version(v, app),
|
|
||||||
cranecmd.NewCmdAuthLogin("syft"), // syft login uses the same command as crane
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add sub-commands.
|
|
||||||
for _, cmd := range cmds {
|
|
||||||
rootCmd.AddCommand(cmd)
|
|
||||||
}
|
|
||||||
|
|
||||||
return rootCmd, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateArgs(cmd *cobra.Command, args []string) error {
|
|
||||||
if len(args) == 0 {
|
|
||||||
// in the case that no arguments are given we want to show the help text and return with a non-0 return code.
|
|
||||||
if err := cmd.Help(); err != nil {
|
|
||||||
return fmt.Errorf("unable to display help: %w", err)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("an image/directory argument is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
return cobra.MaximumNArgs(1)(cmd, args)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkForApplicationUpdate() {
|
|
||||||
log.Debugf("checking if a new version of %s is available", internal.ApplicationName)
|
|
||||||
isAvailable, newVersion, err := version.IsUpdateAvailable()
|
|
||||||
if err != nil {
|
|
||||||
// this should never stop the application
|
|
||||||
log.Errorf(err.Error())
|
|
||||||
}
|
|
||||||
if isAvailable {
|
|
||||||
log.Infof("new version of %s is available: %s (current version is %s)", internal.ApplicationName, newVersion, version.FromBuild().Version)
|
|
||||||
|
|
||||||
bus.Publish(partybus.Event{
|
|
||||||
Type: event.CLIAppUpdateAvailable,
|
|
||||||
Value: newVersion,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
log.Debugf("no new %s update available", internal.ApplicationName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func logApplicationConfig(app *config.Application) {
|
|
||||||
versionInfo := version.FromBuild()
|
|
||||||
log.Infof("%s version: %+v", internal.ApplicationName, versionInfo.Version)
|
|
||||||
log.Debugf("application config:\n%+v", color.Magenta.Sprint(app.String()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func newLogWrapper(app *config.Application) {
|
|
||||||
cfg := logrus.Config{
|
|
||||||
EnableConsole: (app.Log.FileLocation == "" || app.Verbosity > 0) && !app.Quiet,
|
|
||||||
FileLocation: app.Log.FileLocation,
|
|
||||||
Level: app.Log.Level,
|
|
||||||
}
|
|
||||||
|
|
||||||
if app.Log.Structured {
|
|
||||||
cfg.Formatter = &logrusUpstream.JSONFormatter{
|
|
||||||
TimestampFormat: "2006-01-02 15:04:05",
|
|
||||||
DisableTimestamp: false,
|
|
||||||
DisableHTMLEscape: false,
|
|
||||||
PrettyPrint: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logWrapper, err := logrus.New(cfg)
|
|
||||||
if err != nil {
|
|
||||||
// this is kinda circular, but we can't return an error... ¯\_(ツ)_/¯
|
|
||||||
// I'm going to leave this here in case we one day have a different default logger other than the "discard" logger
|
|
||||||
log.Error("unable to initialize logger: %+v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
syft.SetLogger(logWrapper)
|
|
||||||
stereoscope.SetLogger(logWrapper.Nested("from-lib", "stereoscope"))
|
|
||||||
}
|
|
||||||
258
cmd/syft/cli/commands/attest.go
Normal file
258
cmd/syft/cli/commands/attest.go
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/wagoodman/go-partybus"
|
||||||
|
"github.com/wagoodman/go-progress"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
|
"github.com/anchore/syft/internal/bus"
|
||||||
|
"github.com/anchore/syft/internal/file"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/event"
|
||||||
|
"github.com/anchore/syft/syft/event/monitor"
|
||||||
|
"github.com/anchore/syft/syft/formats"
|
||||||
|
"github.com/anchore/syft/syft/formats/github"
|
||||||
|
"github.com/anchore/syft/syft/formats/syftjson"
|
||||||
|
"github.com/anchore/syft/syft/formats/table"
|
||||||
|
"github.com/anchore/syft/syft/formats/template"
|
||||||
|
"github.com/anchore/syft/syft/formats/text"
|
||||||
|
"github.com/anchore/syft/syft/sbom"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
attestExample = ` {{.appName}} {{.command}} --output [FORMAT] alpine:latest defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry
|
||||||
|
`
|
||||||
|
attestSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp
|
||||||
|
attestHelp = attestExample + attestSchemeHelp
|
||||||
|
)
|
||||||
|
|
||||||
|
type attestOptions struct {
|
||||||
|
options.Config `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.SingleOutput `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.Catalog `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.Attest `yaml:",inline" mapstructure:",squash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Attest(app clio.Application) *cobra.Command {
|
||||||
|
id := app.ID()
|
||||||
|
|
||||||
|
var allowableOutputs []string
|
||||||
|
for _, f := range formats.AllIDs() {
|
||||||
|
switch f {
|
||||||
|
case table.ID, text.ID, github.ID, template.ID:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
allowableOutputs = append(allowableOutputs, f.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := &attestOptions{
|
||||||
|
UpdateCheck: options.DefaultUpdateCheck(),
|
||||||
|
SingleOutput: options.SingleOutput{
|
||||||
|
AllowableOptions: allowableOutputs,
|
||||||
|
Output: syftjson.ID.String(),
|
||||||
|
},
|
||||||
|
Catalog: options.DefaultCatalog(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.SetupCommand(&cobra.Command{
|
||||||
|
Use: "attest --output [FORMAT] <IMAGE>",
|
||||||
|
Short: "Generate an SBOM as an attestation for the given [SOURCE] container image",
|
||||||
|
Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from a container image as the predicate of an in-toto attestation that will be uploaded to the image registry",
|
||||||
|
Example: internal.Tprintf(attestHelp, map[string]interface{}{
|
||||||
|
"appName": id.Name,
|
||||||
|
"command": "attest",
|
||||||
|
}),
|
||||||
|
Args: validatePackagesArgs,
|
||||||
|
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return runAttest(id, opts, args[0])
|
||||||
|
},
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:funlen
|
||||||
|
func runAttest(id clio.Identification, opts *attestOptions, userInput string) error {
|
||||||
|
_, err := exec.LookPath("cosign")
|
||||||
|
if err != nil {
|
||||||
|
// when cosign is not installed the error will be rendered like so:
|
||||||
|
// 2023/06/30 08:31:52 error during command execution: 'syft attest' requires cosign to be installed: exec: "cosign": executable file not found in $PATH
|
||||||
|
return fmt.Errorf("'syft attest' requires cosign to be installed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := buildSBOM(id, &opts.Catalog, userInput)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to build SBOM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
o := opts.Output
|
||||||
|
|
||||||
|
f, err := os.CreateTemp("", o)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create temp file: %w", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
|
||||||
|
writer, err := opts.SBOMWriter(f.Name())
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create SBOM writer: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Write(*s); err != nil {
|
||||||
|
return fmt.Errorf("unable to write SBOM to temp file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: what other validation here besides binary name?
|
||||||
|
cmd := "cosign"
|
||||||
|
if !commandExists(cmd) {
|
||||||
|
return fmt.Errorf("unable to find cosign in PATH; make sure you have it installed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Select Cosign predicate type based on defined output type
|
||||||
|
// As orientation, check: https://github.com/sigstore/cosign/blob/main/pkg/cosign/attestation/attestation.go
|
||||||
|
var predicateType string
|
||||||
|
switch strings.ToLower(o) {
|
||||||
|
case "cyclonedx-json":
|
||||||
|
predicateType = "cyclonedx"
|
||||||
|
case "spdx-tag-value", "spdx-tv":
|
||||||
|
predicateType = "spdx"
|
||||||
|
case "spdx-json", "json":
|
||||||
|
predicateType = "spdxjson"
|
||||||
|
default:
|
||||||
|
predicateType = "custom"
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{"attest", userInput, "--predicate", f.Name(), "--type", predicateType}
|
||||||
|
if opts.Attest.Key != "" {
|
||||||
|
args = append(args, "--key", opts.Attest.Key.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
execCmd := exec.Command(cmd, args...)
|
||||||
|
execCmd.Env = os.Environ()
|
||||||
|
if opts.Attest.Key != "" {
|
||||||
|
execCmd.Env = append(execCmd.Env, fmt.Sprintf("COSIGN_PASSWORD=%s", opts.Attest.Password))
|
||||||
|
} else {
|
||||||
|
// no key provided, use cosign's keyless mode
|
||||||
|
execCmd.Env = append(execCmd.Env, "COSIGN_EXPERIMENTAL=1")
|
||||||
|
}
|
||||||
|
|
||||||
|
log.WithFields("cmd", strings.Join(execCmd.Args, " ")).Trace("creating attestation")
|
||||||
|
|
||||||
|
// bus adapter for ui to hook into stdout via an os pipe
|
||||||
|
r, w, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to create os pipe: %w", err)
|
||||||
|
}
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
mon := progress.NewManual(-1)
|
||||||
|
|
||||||
|
bus.Publish(
|
||||||
|
partybus.Event{
|
||||||
|
Type: event.AttestationStarted,
|
||||||
|
Source: monitor.GenericTask{
|
||||||
|
Title: monitor.Title{
|
||||||
|
Default: "Create attestation",
|
||||||
|
WhileRunning: "Creating attestation",
|
||||||
|
OnSuccess: "Created attestation",
|
||||||
|
},
|
||||||
|
Context: "cosign",
|
||||||
|
},
|
||||||
|
Value: &monitor.ShellProgress{
|
||||||
|
Reader: r,
|
||||||
|
Progressable: mon,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
execCmd.Stdout = w
|
||||||
|
execCmd.Stderr = w
|
||||||
|
|
||||||
|
// attest the SBOM
|
||||||
|
err = execCmd.Run()
|
||||||
|
if err != nil {
|
||||||
|
mon.SetError(err)
|
||||||
|
return fmt.Errorf("unable to attest SBOM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mon.SetCompleted()
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildSBOM(id clio.Identification, opts *options.Catalog, userInput string) (*sbom.SBOM, error) {
|
||||||
|
cfg := source.DetectConfig{
|
||||||
|
DefaultImageSource: opts.DefaultImagePullSource,
|
||||||
|
}
|
||||||
|
detection, err := source.Detect(userInput, cfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("could not deteremine source: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if detection.IsContainerImage() {
|
||||||
|
return nil, fmt.Errorf("attestations are only supported for oci images at this time")
|
||||||
|
}
|
||||||
|
|
||||||
|
var platform *image.Platform
|
||||||
|
|
||||||
|
if opts.Platform != "" {
|
||||||
|
platform, err = image.NewPlatform(opts.Platform)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid platform: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashers, err := file.Hashers(opts.Source.File.Digests...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid hash: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := detection.NewSource(
|
||||||
|
source.DetectionSourceConfig{
|
||||||
|
Alias: source.Alias{
|
||||||
|
Name: opts.Source.Name,
|
||||||
|
Version: opts.Source.Version,
|
||||||
|
},
|
||||||
|
RegistryOptions: opts.Registry.ToOptions(),
|
||||||
|
Platform: platform,
|
||||||
|
Exclude: source.ExcludeConfig{
|
||||||
|
Paths: opts.Exclusions,
|
||||||
|
},
|
||||||
|
DigestAlgorithms: hashers,
|
||||||
|
BasePath: opts.BasePath,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if src != nil {
|
||||||
|
defer src.Close()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := generateSBOM(id, src, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return nil, fmt.Errorf("no SBOM produced for %q", userInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func commandExists(cmd string) bool {
|
||||||
|
_, err := exec.LookPath(cmd)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
95
cmd/syft/cli/commands/convert.go
Normal file
95
cmd/syft/cli/commands/convert.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/formats"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout
|
||||||
|
{{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output is written to the file "img.cdx.json""
|
||||||
|
{{.appName}} {{.command}} - -o spdx-json convert an SBOM from STDIN to spdx-json
|
||||||
|
`
|
||||||
|
)
|
||||||
|
|
||||||
|
type ConvertOptions struct {
|
||||||
|
options.Config `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.MultiOutput `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:dupl
|
||||||
|
func Convert(app clio.Application) *cobra.Command {
|
||||||
|
id := app.ID()
|
||||||
|
|
||||||
|
opts := &ConvertOptions{
|
||||||
|
UpdateCheck: options.DefaultUpdateCheck(),
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.SetupCommand(&cobra.Command{
|
||||||
|
Use: "convert [SOURCE-SBOM] -o [FORMAT]",
|
||||||
|
Short: "Convert between SBOM formats",
|
||||||
|
Long: "[Experimental] Convert SBOM files to, and from, SPDX, CycloneDX and Syft's format. For more info about data loss between formats see https://github.com/anchore/syft#format-conversion-experimental",
|
||||||
|
Example: internal.Tprintf(convertExample, map[string]interface{}{
|
||||||
|
"appName": id.Name,
|
||||||
|
"command": "convert",
|
||||||
|
}),
|
||||||
|
Args: validateConvertArgs,
|
||||||
|
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return RunConvert(opts, args[0])
|
||||||
|
},
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateConvertArgs(cmd *cobra.Command, args []string) error {
|
||||||
|
return validateArgs(cmd, args, "an SBOM argument is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunConvert(opts *ConvertOptions, userInput string) error {
|
||||||
|
log.Warn("convert is an experimental feature, run `syft convert -h` for help")
|
||||||
|
|
||||||
|
writer, err := opts.SBOMWriter()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var reader io.ReadCloser
|
||||||
|
|
||||||
|
if userInput == "-" {
|
||||||
|
reader = os.Stdin
|
||||||
|
} else {
|
||||||
|
f, err := os.Open(userInput)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open SBOM file: %w", err)
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
_ = f.Close()
|
||||||
|
}()
|
||||||
|
reader = f
|
||||||
|
}
|
||||||
|
|
||||||
|
s, _, err := formats.Decode(reader)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to decode SBOM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return fmt.Errorf("no SBOM produced")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Write(*s); err != nil {
|
||||||
|
return fmt.Errorf("failed to write SBOM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
253
cmd/syft/cli/commands/packages.go
Normal file
253
cmd/syft/cli/commands/packages.go
Normal file
@ -0,0 +1,253 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
"github.com/anchore/syft/cmd/syft/cli/eventloop"
|
||||||
|
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
|
"github.com/anchore/syft/internal/file"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/formats/template"
|
||||||
|
"github.com/anchore/syft/syft/sbom"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
packagesExample = ` {{.appName}} {{.command}} alpine:latest a summary of discovered packages
|
||||||
|
{{.appName}} {{.command}} alpine:latest -o json show all possible cataloging details
|
||||||
|
{{.appName}} {{.command}} alpine:latest -o cyclonedx show a CycloneDX formatted SBOM
|
||||||
|
{{.appName}} {{.command}} alpine:latest -o cyclonedx-json show a CycloneDX JSON formatted SBOM
|
||||||
|
{{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.3 Tag-Value formatted SBOM
|
||||||
|
{{.appName}} {{.command}} alpine:latest -o spdx@2.2 show a SPDX 2.2 Tag-Value formatted SBOM
|
||||||
|
{{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.3 JSON formatted SBOM
|
||||||
|
{{.appName}} {{.command}} alpine:latest -o spdx-json@2.2 show a SPDX 2.2 JSON formatted SBOM
|
||||||
|
{{.appName}} {{.command}} alpine:latest -vv show verbose debug information
|
||||||
|
{{.appName}} {{.command}} alpine:latest -o template -t my_format.tmpl show a SBOM formatted according to given template file
|
||||||
|
|
||||||
|
Supports the following image sources:
|
||||||
|
{{.appName}} {{.command}} yourrepo/yourimage:tag defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry.
|
||||||
|
{{.appName}} {{.command}} path/to/a/file/or/dir a Docker tar, OCI tar, OCI directory, SIF container, or generic filesystem directory
|
||||||
|
`
|
||||||
|
|
||||||
|
schemeHelpHeader = "You can also explicitly specify the scheme to use:"
|
||||||
|
imageSchemeHelp = ` {{.appName}} {{.command}} docker:yourrepo/yourimage:tag explicitly use the Docker daemon
|
||||||
|
{{.appName}} {{.command}} podman:yourrepo/yourimage:tag explicitly use the Podman daemon
|
||||||
|
{{.appName}} {{.command}} registry:yourrepo/yourimage:tag pull image directly from a registry (no container runtime required)
|
||||||
|
{{.appName}} {{.command}} docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save"
|
||||||
|
{{.appName}} {{.command}} oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise)
|
||||||
|
{{.appName}} {{.command}} oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
|
||||||
|
{{.appName}} {{.command}} singularity:path/to/yourimage.sif read directly from a Singularity Image Format (SIF) container on disk
|
||||||
|
`
|
||||||
|
nonImageSchemeHelp = ` {{.appName}} {{.command}} dir:path/to/yourproject read directly from a path on disk (any directory)
|
||||||
|
{{.appName}} {{.command}} file:path/to/yourproject/file read directly from a path on disk (any single file)
|
||||||
|
`
|
||||||
|
packagesSchemeHelp = "\n " + schemeHelpHeader + "\n" + imageSchemeHelp + nonImageSchemeHelp
|
||||||
|
|
||||||
|
packagesHelp = packagesExample + packagesSchemeHelp
|
||||||
|
)
|
||||||
|
|
||||||
|
type packagesOptions struct {
|
||||||
|
options.Config `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.MultiOutput `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.Catalog `yaml:",inline" mapstructure:",squash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultPackagesOptions() *packagesOptions {
|
||||||
|
return &packagesOptions{
|
||||||
|
MultiOutput: options.DefaultOutput(),
|
||||||
|
UpdateCheck: options.DefaultUpdateCheck(),
|
||||||
|
Catalog: options.DefaultCatalog(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:dupl
|
||||||
|
func Packages(app clio.Application) *cobra.Command {
|
||||||
|
id := app.ID()
|
||||||
|
|
||||||
|
opts := defaultPackagesOptions()
|
||||||
|
|
||||||
|
return app.SetupCommand(&cobra.Command{
|
||||||
|
Use: "packages [SOURCE]",
|
||||||
|
Short: "Generate a package SBOM",
|
||||||
|
Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from container images and filesystems",
|
||||||
|
Example: internal.Tprintf(packagesHelp, map[string]interface{}{
|
||||||
|
"appName": id.Name,
|
||||||
|
"command": "packages",
|
||||||
|
}),
|
||||||
|
Args: validatePackagesArgs,
|
||||||
|
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return runPackages(id, opts, args[0])
|
||||||
|
},
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatePackagesArgs(cmd *cobra.Command, args []string) error {
|
||||||
|
return validateArgs(cmd, args, "an image/directory argument is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateArgs(cmd *cobra.Command, args []string, error string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
// in the case that no arguments are given we want to show the help text and return with a non-0 return code.
|
||||||
|
if err := cmd.Help(); err != nil {
|
||||||
|
return fmt.Errorf("unable to display help: %w", err)
|
||||||
|
}
|
||||||
|
return fmt.Errorf(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cobra.MaximumNArgs(1)(cmd, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint:funlen
|
||||||
|
func runPackages(id clio.Identification, opts *packagesOptions, userInput string) error {
|
||||||
|
err := validatePackageOutputOptions(&opts.MultiOutput)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
writer, err := opts.SBOMWriter()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
detection, err := source.Detect(
|
||||||
|
userInput,
|
||||||
|
source.DetectConfig{
|
||||||
|
DefaultImageSource: opts.DefaultImagePullSource,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not deteremine source: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var platform *image.Platform
|
||||||
|
|
||||||
|
if opts.Platform != "" {
|
||||||
|
platform, err = image.NewPlatform(opts.Platform)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid platform: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashers, err := file.Hashers(opts.Source.File.Digests...)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid hash: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := detection.NewSource(
|
||||||
|
source.DetectionSourceConfig{
|
||||||
|
Alias: source.Alias{
|
||||||
|
Name: opts.Source.Name,
|
||||||
|
Version: opts.Source.Version,
|
||||||
|
},
|
||||||
|
RegistryOptions: opts.Registry.ToOptions(),
|
||||||
|
Platform: platform,
|
||||||
|
Exclude: source.ExcludeConfig{
|
||||||
|
Paths: opts.Exclusions,
|
||||||
|
},
|
||||||
|
DigestAlgorithms: hashers,
|
||||||
|
BasePath: opts.BasePath,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if src != nil {
|
||||||
|
if err := src.Close(); err != nil {
|
||||||
|
log.Tracef("unable to close source: %+v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s, err := generateSBOM(id, src, &opts.Catalog)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if s == nil {
|
||||||
|
return fmt.Errorf("no SBOM produced for %q", userInput)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := writer.Write(*s); err != nil {
|
||||||
|
return fmt.Errorf("failed to write SBOM: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateSBOM(id clio.Identification, src source.Source, opts *options.Catalog) (*sbom.SBOM, error) {
|
||||||
|
tasks, err := eventloop.Tasks(opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
s := sbom.SBOM{
|
||||||
|
Source: src.Describe(),
|
||||||
|
Descriptor: sbom.Descriptor{
|
||||||
|
Name: id.Name,
|
||||||
|
Version: id.Version,
|
||||||
|
Configuration: opts,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err = buildRelationships(&s, src, tasks)
|
||||||
|
|
||||||
|
return &s, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func buildRelationships(s *sbom.SBOM, src source.Source, tasks []eventloop.Task) error {
|
||||||
|
var errs error
|
||||||
|
|
||||||
|
var relationships []<-chan artifact.Relationship
|
||||||
|
for _, task := range tasks {
|
||||||
|
c := make(chan artifact.Relationship)
|
||||||
|
relationships = append(relationships, c)
|
||||||
|
go func(task eventloop.Task) {
|
||||||
|
err := eventloop.RunTask(task, &s.Artifacts, src, c)
|
||||||
|
if err != nil {
|
||||||
|
errs = multierror.Append(errs, err)
|
||||||
|
}
|
||||||
|
}(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Relationships = append(s.Relationships, mergeRelationships(relationships...)...)
|
||||||
|
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeRelationships(cs ...<-chan artifact.Relationship) (relationships []artifact.Relationship) {
|
||||||
|
for _, c := range cs {
|
||||||
|
for n := range c {
|
||||||
|
relationships = append(relationships, n)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return relationships
|
||||||
|
}
|
||||||
|
|
||||||
|
func validatePackageOutputOptions(cfg *options.MultiOutput) error {
|
||||||
|
var usesTemplateOutput bool
|
||||||
|
for _, o := range cfg.Outputs {
|
||||||
|
if o == template.ID.String() {
|
||||||
|
usesTemplateOutput = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if usesTemplateOutput && cfg.OutputTemplatePath == "" {
|
||||||
|
return fmt.Errorf(`must specify path to template file when using "template" output format`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
154
cmd/syft/cli/commands/poweruser.go
Normal file
154
cmd/syft/cli/commands/poweruser.go
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/gookit/color"
|
||||||
|
"github.com/hashicorp/go-multierror"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
"github.com/anchore/syft/cmd/syft/cli/eventloop"
|
||||||
|
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/formats/syftjson"
|
||||||
|
"github.com/anchore/syft/syft/sbom"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
const powerUserExample = ` {{.appName}} {{.command}} <image>
|
||||||
|
DEPRECATED - THIS COMMAND WILL BE REMOVED in v1.0.0
|
||||||
|
Template outputs are not supported.
|
||||||
|
All behavior is controlled via application configuration and environment variables (see https://github.com/anchore/syft#configuration)
|
||||||
|
`
|
||||||
|
|
||||||
|
type powerUserOptions struct {
|
||||||
|
options.Config `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.OutputFile `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
|
||||||
|
options.Catalog `yaml:",inline" mapstructure:",squash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func PowerUser(app clio.Application) *cobra.Command {
|
||||||
|
id := app.ID()
|
||||||
|
|
||||||
|
pkgs := options.DefaultCatalog()
|
||||||
|
pkgs.Secrets.Cataloger.Enabled = true
|
||||||
|
pkgs.FileMetadata.Cataloger.Enabled = true
|
||||||
|
pkgs.FileContents.Cataloger.Enabled = true
|
||||||
|
pkgs.FileClassification.Cataloger.Enabled = true
|
||||||
|
opts := &powerUserOptions{
|
||||||
|
Catalog: pkgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
return app.SetupCommand(&cobra.Command{
|
||||||
|
Use: "power-user [IMAGE]",
|
||||||
|
Short: "Run bulk operations on container images",
|
||||||
|
Example: internal.Tprintf(powerUserExample, map[string]interface{}{
|
||||||
|
"appName": id.Name,
|
||||||
|
"command": "power-user",
|
||||||
|
}),
|
||||||
|
Args: validatePackagesArgs,
|
||||||
|
Hidden: true,
|
||||||
|
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return runPowerUser(id, opts, args[0])
|
||||||
|
},
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
//nolint:funlen
|
||||||
|
func runPowerUser(id clio.Identification, opts *powerUserOptions, userInput string) error {
|
||||||
|
writer, err := opts.SBOMWriter(syftjson.Format())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
// inform user at end of run that command will be removed
|
||||||
|
deprecated := color.Style{color.Red, color.OpBold}.Sprint("DEPRECATED: This command will be removed in v1.0.0")
|
||||||
|
fmt.Fprintln(os.Stderr, deprecated)
|
||||||
|
}()
|
||||||
|
|
||||||
|
tasks, err := eventloop.Tasks(&opts.Catalog)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
detection, err := source.Detect(
|
||||||
|
userInput,
|
||||||
|
source.DetectConfig{
|
||||||
|
DefaultImageSource: opts.DefaultImagePullSource,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("could not deteremine source: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var platform *image.Platform
|
||||||
|
|
||||||
|
if opts.Platform != "" {
|
||||||
|
platform, err = image.NewPlatform(opts.Platform)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid platform: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src, err := detection.NewSource(
|
||||||
|
source.DetectionSourceConfig{
|
||||||
|
Alias: source.Alias{
|
||||||
|
Name: opts.Source.Name,
|
||||||
|
Version: opts.Source.Version,
|
||||||
|
},
|
||||||
|
RegistryOptions: opts.Registry.ToOptions(),
|
||||||
|
Platform: platform,
|
||||||
|
Exclude: source.ExcludeConfig{
|
||||||
|
Paths: opts.Exclusions,
|
||||||
|
},
|
||||||
|
DigestAlgorithms: nil,
|
||||||
|
BasePath: opts.BasePath,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
if src != nil {
|
||||||
|
defer src.Close()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
s := sbom.SBOM{
|
||||||
|
Source: src.Describe(),
|
||||||
|
Descriptor: sbom.Descriptor{
|
||||||
|
Name: id.Name,
|
||||||
|
Version: id.Version,
|
||||||
|
Configuration: opts,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var errs error
|
||||||
|
var relationships []<-chan artifact.Relationship
|
||||||
|
for _, task := range tasks {
|
||||||
|
c := make(chan artifact.Relationship)
|
||||||
|
relationships = append(relationships, c)
|
||||||
|
|
||||||
|
go func(task eventloop.Task) {
|
||||||
|
err := eventloop.RunTask(task, &s.Artifacts, src, c)
|
||||||
|
errs = multierror.Append(errs, err)
|
||||||
|
}(task)
|
||||||
|
}
|
||||||
|
|
||||||
|
if errs != nil {
|
||||||
|
return errs
|
||||||
|
}
|
||||||
|
|
||||||
|
s.Relationships = append(s.Relationships, mergeRelationships(relationships...)...)
|
||||||
|
|
||||||
|
if err := writer.Write(s); err != nil {
|
||||||
|
return fmt.Errorf("failed to write sbom: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
27
cmd/syft/cli/commands/root.go
Normal file
27
cmd/syft/cli/commands/root.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Root(app clio.Application, packagesCmd *cobra.Command) *cobra.Command {
|
||||||
|
id := app.ID()
|
||||||
|
|
||||||
|
opts := defaultPackagesOptions()
|
||||||
|
|
||||||
|
return app.SetupRootCommand(&cobra.Command{
|
||||||
|
Use: fmt.Sprintf("%s [SOURCE]", app.ID().Name),
|
||||||
|
Short: packagesCmd.Short,
|
||||||
|
Long: packagesCmd.Long,
|
||||||
|
Args: packagesCmd.Args,
|
||||||
|
Example: packagesCmd.Example,
|
||||||
|
PreRunE: applicationUpdateCheck(id, &opts.UpdateCheck),
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return runPackages(id, opts, args[0])
|
||||||
|
},
|
||||||
|
}, opts)
|
||||||
|
}
|
||||||
121
cmd/syft/cli/commands/update.go
Normal file
121
cmd/syft/cli/commands/update.go
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/wagoodman/go-partybus"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
hashiVersion "github.com/anchore/go-version"
|
||||||
|
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||||
|
"github.com/anchore/syft/cmd/syft/internal"
|
||||||
|
"github.com/anchore/syft/internal/bus"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/event"
|
||||||
|
"github.com/anchore/syft/syft/event/parsers"
|
||||||
|
)
|
||||||
|
|
||||||
|
var latestAppVersionURL = struct {
|
||||||
|
host string
|
||||||
|
path string
|
||||||
|
}{
|
||||||
|
host: "https://toolbox-data.anchore.io",
|
||||||
|
path: "/syft/releases/latest/VERSION",
|
||||||
|
}
|
||||||
|
|
||||||
|
func applicationUpdateCheck(id clio.Identification, check *options.UpdateCheck) func(cmd *cobra.Command, args []string) error {
|
||||||
|
return func(cmd *cobra.Command, args []string) error {
|
||||||
|
if check.CheckForAppUpdate {
|
||||||
|
checkForApplicationUpdate(id)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkForApplicationUpdate(id clio.Identification) {
|
||||||
|
log.Debugf("checking if a new version of %s is available", id.Name)
|
||||||
|
isAvailable, newVersion, err := isUpdateAvailable(id)
|
||||||
|
if err != nil {
|
||||||
|
// this should never stop the application
|
||||||
|
log.Errorf(err.Error())
|
||||||
|
}
|
||||||
|
if isAvailable {
|
||||||
|
log.Infof("new version of %s is available: %s (current version is %s)", id.Name, newVersion, id.Version)
|
||||||
|
|
||||||
|
bus.Publish(partybus.Event{
|
||||||
|
Type: event.CLIAppUpdateAvailable,
|
||||||
|
Value: parsers.UpdateCheck{
|
||||||
|
New: newVersion,
|
||||||
|
Current: id.Version,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
log.Debugf("no new %s update available", id.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// isUpdateAvailable indicates if there is a newer application version available, and if so, what the new version is.
|
||||||
|
func isUpdateAvailable(id clio.Identification) (bool, string, error) {
|
||||||
|
if !isProductionBuild(id.Version) {
|
||||||
|
// don't allow for non-production builds to check for a version.
|
||||||
|
return false, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
currentVersion, err := hashiVersion.NewVersion(id.Version)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", fmt.Errorf("failed to parse current application version: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
latestVersion, err := fetchLatestApplicationVersion(id)
|
||||||
|
if err != nil {
|
||||||
|
return false, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if latestVersion.GreaterThan(currentVersion) {
|
||||||
|
return true, latestVersion.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isProductionBuild(version string) bool {
|
||||||
|
if strings.Contains(version, "SNAPSHOT") || strings.Contains(version, internal.NotProvided) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchLatestApplicationVersion(id clio.Identification) (*hashiVersion.Version, error) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet, latestAppVersionURL.host+latestAppVersionURL.path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create request for latest version: %w", err)
|
||||||
|
}
|
||||||
|
req.Header.Add("User-Agent", fmt.Sprintf("%v %v", id.Name, id.Version))
|
||||||
|
|
||||||
|
client := http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to fetch latest version: %w", err)
|
||||||
|
}
|
||||||
|
defer func() { _ = resp.Body.Close() }()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("HTTP %d on fetching latest version: %s", resp.StatusCode, resp.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
versionBytes, err := io.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read latest version: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
versionStr := strings.TrimSuffix(string(versionBytes), "\n")
|
||||||
|
if len(versionStr) > 50 {
|
||||||
|
return nil, fmt.Errorf("version too long: %q", versionStr[:50])
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashiVersion.NewVersion(versionStr)
|
||||||
|
}
|
||||||
@ -1,11 +1,13 @@
|
|||||||
package version
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
hashiVersion "github.com/anchore/go-version"
|
hashiVersion "github.com/anchore/go-version"
|
||||||
|
"github.com/anchore/syft/cmd/syft/internal"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestIsUpdateAvailable(t *testing.T) {
|
func TestIsUpdateAvailable(t *testing.T) {
|
||||||
@ -74,7 +76,7 @@ func TestIsUpdateAvailable(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "NoBuildVersion",
|
name: "NoBuildVersion",
|
||||||
buildVersion: valueNotProvided,
|
buildVersion: internal.NotProvided,
|
||||||
latestVersion: "1.0.0",
|
latestVersion: "1.0.0",
|
||||||
code: 200,
|
code: 200,
|
||||||
isAvailable: false,
|
isAvailable: false,
|
||||||
@ -105,7 +107,7 @@ func TestIsUpdateAvailable(t *testing.T) {
|
|||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
// setup mocks
|
// setup mocks
|
||||||
// local...
|
// local...
|
||||||
version = test.buildVersion
|
id := clio.Identification{Name: "Syft", Version: test.buildVersion}
|
||||||
// remote...
|
// remote...
|
||||||
handler := http.NewServeMux()
|
handler := http.NewServeMux()
|
||||||
handler.HandleFunc(latestAppVersionURL.path, func(w http.ResponseWriter, r *http.Request) {
|
handler.HandleFunc(latestAppVersionURL.path, func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -116,7 +118,7 @@ func TestIsUpdateAvailable(t *testing.T) {
|
|||||||
latestAppVersionURL.host = mockSrv.URL
|
latestAppVersionURL.host = mockSrv.URL
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
isAvailable, newVersion, err := IsUpdateAvailable()
|
isAvailable, newVersion, err := isUpdateAvailable(id)
|
||||||
if err != nil && !test.err {
|
if err != nil && !test.err {
|
||||||
t.Fatalf("got error but expected none: %+v", err)
|
t.Fatalf("got error but expected none: %+v", err)
|
||||||
} else if err == nil && test.err {
|
} else if err == nil && test.err {
|
||||||
@ -141,47 +143,62 @@ func TestFetchLatestApplicationVersion(t *testing.T) {
|
|||||||
response string
|
response string
|
||||||
code int
|
code int
|
||||||
err bool
|
err bool
|
||||||
|
id clio.Identification
|
||||||
expected *hashiVersion.Version
|
expected *hashiVersion.Version
|
||||||
|
expectedHeaders map[string]string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "gocase",
|
name: "gocase",
|
||||||
response: "1.0.0",
|
response: "1.0.0",
|
||||||
code: 200,
|
code: 200,
|
||||||
|
id: clio.Identification{Name: "Syft", Version: "0.0.0"},
|
||||||
expected: hashiVersion.Must(hashiVersion.NewVersion("1.0.0")),
|
expected: hashiVersion.Must(hashiVersion.NewVersion("1.0.0")),
|
||||||
|
expectedHeaders: map[string]string{"User-Agent": "Syft 0.0.0"},
|
||||||
|
err: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "garbage",
|
name: "garbage",
|
||||||
response: "garbage",
|
response: "garbage",
|
||||||
code: 200,
|
code: 200,
|
||||||
|
id: clio.Identification{Name: "Syft", Version: "0.0.0"},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
|
expectedHeaders: nil,
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "http 500",
|
name: "http 500",
|
||||||
response: "1.0.0",
|
response: "1.0.0",
|
||||||
code: 500,
|
code: 500,
|
||||||
|
id: clio.Identification{Name: "Syft", Version: "0.0.0"},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
|
expectedHeaders: nil,
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "http 404",
|
name: "http 404",
|
||||||
response: "1.0.0",
|
response: "1.0.0",
|
||||||
code: 404,
|
code: 404,
|
||||||
|
id: clio.Identification{Name: "Syft", Version: "0.0.0"},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
|
expectedHeaders: nil,
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "empty",
|
name: "empty",
|
||||||
response: "",
|
response: "",
|
||||||
code: 200,
|
code: 200,
|
||||||
|
id: clio.Identification{Name: "Syft", Version: "0.0.0"},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
|
expectedHeaders: nil,
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "too long",
|
name: "too long",
|
||||||
response: "this is really long this is really long this is really long this is really long this is really long this is really long this is really long this is really long ",
|
response: "this is really long this is really long this is really long this is really long this is really long this is really long this is really long this is really long ",
|
||||||
code: 200,
|
code: 200,
|
||||||
|
id: clio.Identification{Name: "Syft", Version: "0.0.0"},
|
||||||
expected: nil,
|
expected: nil,
|
||||||
|
expectedHeaders: nil,
|
||||||
err: true,
|
err: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -191,6 +208,15 @@ func TestFetchLatestApplicationVersion(t *testing.T) {
|
|||||||
// setup mock
|
// setup mock
|
||||||
handler := http.NewServeMux()
|
handler := http.NewServeMux()
|
||||||
handler.HandleFunc(latestAppVersionURL.path, func(w http.ResponseWriter, r *http.Request) {
|
handler.HandleFunc(latestAppVersionURL.path, func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if test.expectedHeaders != nil {
|
||||||
|
for headerName, headerValue := range test.expectedHeaders {
|
||||||
|
actualHeader := r.Header.Get(headerName)
|
||||||
|
if actualHeader != headerValue {
|
||||||
|
t.Fatalf("expected header %v=%v but got %v", headerName, headerValue, actualHeader)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
w.WriteHeader(test.code)
|
w.WriteHeader(test.code)
|
||||||
_, _ = w.Write([]byte(test.response))
|
_, _ = w.Write([]byte(test.response))
|
||||||
})
|
})
|
||||||
@ -198,7 +224,7 @@ func TestFetchLatestApplicationVersion(t *testing.T) {
|
|||||||
latestAppVersionURL.host = mockSrv.URL
|
latestAppVersionURL.host = mockSrv.URL
|
||||||
defer mockSrv.Close()
|
defer mockSrv.Close()
|
||||||
|
|
||||||
actual, err := fetchLatestApplicationVersion()
|
actual, err := fetchLatestApplicationVersion(test.id)
|
||||||
if err != nil && !test.err {
|
if err != nil && !test.err {
|
||||||
t.Fatalf("got error but expected none: %+v", err)
|
t.Fatalf("got error but expected none: %+v", err)
|
||||||
} else if err == nil && test.err {
|
} else if err == nil && test.err {
|
||||||
@ -1,58 +0,0 @@
|
|||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/convert"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout
|
|
||||||
{{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output is written to the file "img.cdx.json""
|
|
||||||
{{.appName}} {{.command}} - -o spdx-json convert an SBOM from STDIN to spdx-json
|
|
||||||
`
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint:dupl
|
|
||||||
func Convert(v *viper.Viper, app *config.Application, ro *options.RootOptions, po *options.PackagesOptions) *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "convert [SOURCE-SBOM] -o [FORMAT]",
|
|
||||||
Short: "Convert between SBOM formats",
|
|
||||||
Long: "[Experimental] Convert SBOM files to, and from, SPDX, CycloneDX and Syft's format. For more info about data loss between formats see https://github.com/anchore/syft#format-conversion-experimental",
|
|
||||||
Example: internal.Tprintf(convertExample, map[string]interface{}{
|
|
||||||
"appName": internal.ApplicationName,
|
|
||||||
"command": "convert",
|
|
||||||
}),
|
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if err := app.LoadAllValues(v, ro.Config); err != nil {
|
|
||||||
return fmt.Errorf("invalid application config: %w", err)
|
|
||||||
}
|
|
||||||
newLogWrapper(app)
|
|
||||||
logApplicationConfig(app)
|
|
||||||
return validateArgs(cmd, args)
|
|
||||||
},
|
|
||||||
SilenceUsage: true,
|
|
||||||
SilenceErrors: true,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if app.CheckForAppUpdate {
|
|
||||||
checkForApplicationUpdate()
|
|
||||||
// TODO: this is broke, the bus isn't available yet
|
|
||||||
}
|
|
||||||
return convert.Run(cmd.Context(), app, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := po.AddFlags(cmd, v)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
package convert
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/wagoodman/go-partybus"
|
|
||||||
|
|
||||||
"github.com/anchore/stereoscope"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/eventloop"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
|
||||||
"github.com/anchore/syft/internal/bus"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/anchore/syft/syft"
|
|
||||||
"github.com/anchore/syft/syft/formats"
|
|
||||||
"github.com/anchore/syft/syft/sbom"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Run(_ context.Context, app *config.Application, args []string) error {
|
|
||||||
log.Warn("convert is an experimental feature, run `syft convert -h` for help")
|
|
||||||
|
|
||||||
writer, err := options.MakeSBOMWriter(app.Outputs, app.File, app.OutputTemplatePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// could be an image or a directory, with or without a scheme
|
|
||||||
userInput := args[0]
|
|
||||||
|
|
||||||
var reader io.ReadCloser
|
|
||||||
|
|
||||||
if userInput == "-" {
|
|
||||||
reader = os.Stdin
|
|
||||||
} else {
|
|
||||||
f, err := os.Open(userInput)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open SBOM file: %w", err)
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
_ = f.Close()
|
|
||||||
}()
|
|
||||||
reader = f
|
|
||||||
}
|
|
||||||
|
|
||||||
eventBus := partybus.NewBus()
|
|
||||||
stereoscope.SetBus(eventBus)
|
|
||||||
syft.SetBus(eventBus)
|
|
||||||
subscription := eventBus.Subscribe()
|
|
||||||
|
|
||||||
return eventloop.EventLoop(
|
|
||||||
execWorker(reader, writer),
|
|
||||||
eventloop.SetupSignals(),
|
|
||||||
subscription,
|
|
||||||
stereoscope.Cleanup,
|
|
||||||
ui.Select(options.IsVerbose(app), app.Quiet)...,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func execWorker(reader io.Reader, writer sbom.Writer) <-chan error {
|
|
||||||
errs := make(chan error)
|
|
||||||
go func() {
|
|
||||||
defer close(errs)
|
|
||||||
defer bus.Exit()
|
|
||||||
|
|
||||||
s, _, err := formats.Decode(reader)
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("failed to decode SBOM: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s == nil {
|
|
||||||
errs <- fmt.Errorf("no SBOM produced")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writer.Write(*s); err != nil {
|
|
||||||
errs <- fmt.Errorf("failed to write SBOM: %w", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
@ -1,98 +0,0 @@
|
|||||||
package eventloop
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/hashicorp/go-multierror"
|
|
||||||
"github.com/wagoodman/go-partybus"
|
|
||||||
|
|
||||||
"github.com/anchore/clio"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EventLoop listens to worker errors (from execution path), worker events (from a partybus subscription), and
|
|
||||||
// signal interrupts. Is responsible for handling each event relative to a given UI an to coordinate eventing until
|
|
||||||
// an eventual graceful exit.
|
|
||||||
func EventLoop(workerErrs <-chan error, signals <-chan os.Signal, subscription *partybus.Subscription, cleanupFn func(), uxs ...clio.UI) error {
|
|
||||||
defer cleanupFn()
|
|
||||||
events := subscription.Events()
|
|
||||||
var err error
|
|
||||||
var ux clio.UI
|
|
||||||
|
|
||||||
if ux, err = setupUI(subscription, uxs...); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
var retErr error
|
|
||||||
var forceTeardown bool
|
|
||||||
|
|
||||||
for {
|
|
||||||
if workerErrs == nil && events == nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case err, isOpen := <-workerErrs:
|
|
||||||
if !isOpen {
|
|
||||||
workerErrs = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// capture the error from the worker and unsubscribe to complete a graceful shutdown
|
|
||||||
retErr = multierror.Append(retErr, err)
|
|
||||||
_ = subscription.Unsubscribe()
|
|
||||||
// the worker has exited, we may have been mid-handling events for the UI which should now be
|
|
||||||
// ignored, in which case forcing a teardown of the UI irregardless of the state is required.
|
|
||||||
forceTeardown = true
|
|
||||||
}
|
|
||||||
case e, isOpen := <-events:
|
|
||||||
if !isOpen {
|
|
||||||
events = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ux.Handle(e); err != nil {
|
|
||||||
if errors.Is(err, partybus.ErrUnsubscribe) {
|
|
||||||
events = nil
|
|
||||||
} else {
|
|
||||||
retErr = multierror.Append(retErr, err)
|
|
||||||
// TODO: should we unsubscribe? should we try to halt execution? or continue?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case <-signals:
|
|
||||||
// ignore further results from any event source and exit ASAP, but ensure that all cache is cleaned up.
|
|
||||||
// we ignore further errors since cleaning up the tmp directories will affect running catalogers that are
|
|
||||||
// reading/writing from/to their nested temp dirs. This is acceptable since we are bailing without result.
|
|
||||||
|
|
||||||
// TODO: potential future improvement would be to pass context into workers with a cancel function that is
|
|
||||||
// to the event loop. In this way we can have a more controlled shutdown even at the most nested levels
|
|
||||||
// of processing.
|
|
||||||
events = nil
|
|
||||||
workerErrs = nil
|
|
||||||
forceTeardown = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := ux.Teardown(forceTeardown); err != nil {
|
|
||||||
retErr = multierror.Append(retErr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return retErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// setupUI takes one or more UIs that responds to events and takes a event bus unsubscribe function for use
|
|
||||||
// during teardown. With the given UIs, the first UI which the ui.Setup() function does not return an error
|
|
||||||
// will be utilized in execution. Providing a set of UIs allows for the caller to provide graceful fallbacks
|
|
||||||
// when there are environmental problem (e.g. unable to setup a TUI with the current TTY).
|
|
||||||
func setupUI(subscription *partybus.Subscription, uis ...clio.UI) (clio.UI, error) {
|
|
||||||
for _, ux := range uis {
|
|
||||||
if err := ux.Setup(subscription); err != nil {
|
|
||||||
log.Warnf("unable to setup given UI, falling back to alternative UI: %+v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
return ux, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("unable to setup any UI")
|
|
||||||
}
|
|
||||||
@ -1,459 +0,0 @@
|
|||||||
package eventloop
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"syscall"
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/mock"
|
|
||||||
"github.com/wagoodman/go-partybus"
|
|
||||||
|
|
||||||
"github.com/anchore/clio"
|
|
||||||
"github.com/anchore/syft/syft/event"
|
|
||||||
)
|
|
||||||
|
|
||||||
var _ clio.UI = (*uiMock)(nil)
|
|
||||||
|
|
||||||
type uiMock struct {
|
|
||||||
t *testing.T
|
|
||||||
finalEvent partybus.Event
|
|
||||||
subscription partybus.Unsubscribable
|
|
||||||
mock.Mock
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *uiMock) Setup(unsubscribe partybus.Unsubscribable) error {
|
|
||||||
u.t.Helper()
|
|
||||||
u.t.Logf("UI Setup called")
|
|
||||||
u.subscription = unsubscribe
|
|
||||||
return u.Called(unsubscribe.Unsubscribe).Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *uiMock) Handle(event partybus.Event) error {
|
|
||||||
u.t.Helper()
|
|
||||||
u.t.Logf("UI Handle called: %+v", event.Type)
|
|
||||||
if event == u.finalEvent {
|
|
||||||
assert.NoError(u.t, u.subscription.Unsubscribe())
|
|
||||||
}
|
|
||||||
return u.Called(event).Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *uiMock) Teardown(_ bool) error {
|
|
||||||
u.t.Helper()
|
|
||||||
u.t.Logf("UI Teardown called")
|
|
||||||
return u.Called().Error(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_EventLoop_gracefulExit(t *testing.T) {
|
|
||||||
test := func(t *testing.T) {
|
|
||||||
|
|
||||||
testBus := partybus.NewBus()
|
|
||||||
subscription := testBus.Subscribe()
|
|
||||||
t.Cleanup(testBus.Close)
|
|
||||||
|
|
||||||
finalEvent := partybus.Event{
|
|
||||||
Type: event.CLIExit,
|
|
||||||
}
|
|
||||||
|
|
||||||
worker := func() <-chan error {
|
|
||||||
ret := make(chan error)
|
|
||||||
go func() {
|
|
||||||
t.Log("worker running")
|
|
||||||
// send an empty item (which is ignored) ensuring we've entered the select statement,
|
|
||||||
// then close (a partial shutdown).
|
|
||||||
ret <- nil
|
|
||||||
t.Log("worker sent nothing")
|
|
||||||
close(ret)
|
|
||||||
t.Log("worker closed")
|
|
||||||
// do the other half of the shutdown
|
|
||||||
testBus.Publish(finalEvent)
|
|
||||||
t.Log("worker published final event")
|
|
||||||
}()
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
signaler := func() <-chan os.Signal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ux := &uiMock{
|
|
||||||
t: t,
|
|
||||||
finalEvent: finalEvent,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure the mock sees at least the final event
|
|
||||||
ux.On("Handle", finalEvent).Return(nil)
|
|
||||||
// ensure the mock sees basic setup/teardown events
|
|
||||||
ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil)
|
|
||||||
ux.On("Teardown").Return(nil)
|
|
||||||
|
|
||||||
var cleanupCalled bool
|
|
||||||
cleanupFn := func() {
|
|
||||||
t.Log("cleanup called")
|
|
||||||
cleanupCalled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t,
|
|
||||||
EventLoop(
|
|
||||||
worker(),
|
|
||||||
signaler(),
|
|
||||||
subscription,
|
|
||||||
cleanupFn,
|
|
||||||
ux,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.True(t, cleanupCalled, "cleanup function not called")
|
|
||||||
ux.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is a bug, then there is a risk of the event loop never returning
|
|
||||||
testWithTimeout(t, 5*time.Second, test)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_EventLoop_workerError(t *testing.T) {
|
|
||||||
test := func(t *testing.T) {
|
|
||||||
|
|
||||||
testBus := partybus.NewBus()
|
|
||||||
subscription := testBus.Subscribe()
|
|
||||||
t.Cleanup(testBus.Close)
|
|
||||||
|
|
||||||
workerErr := fmt.Errorf("worker error")
|
|
||||||
|
|
||||||
worker := func() <-chan error {
|
|
||||||
ret := make(chan error)
|
|
||||||
go func() {
|
|
||||||
t.Log("worker running")
|
|
||||||
// send an empty item (which is ignored) ensuring we've entered the select statement,
|
|
||||||
// then close (a partial shutdown).
|
|
||||||
ret <- nil
|
|
||||||
t.Log("worker sent nothing")
|
|
||||||
ret <- workerErr
|
|
||||||
t.Log("worker sent error")
|
|
||||||
close(ret)
|
|
||||||
t.Log("worker closed")
|
|
||||||
// note: NO final event is fired
|
|
||||||
}()
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
signaler := func() <-chan os.Signal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ux := &uiMock{
|
|
||||||
t: t,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure the mock sees basic setup/teardown events
|
|
||||||
ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil)
|
|
||||||
ux.On("Teardown").Return(nil)
|
|
||||||
|
|
||||||
var cleanupCalled bool
|
|
||||||
cleanupFn := func() {
|
|
||||||
t.Log("cleanup called")
|
|
||||||
cleanupCalled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we see an error returned
|
|
||||||
assert.ErrorIs(t,
|
|
||||||
EventLoop(
|
|
||||||
worker(),
|
|
||||||
signaler(),
|
|
||||||
subscription,
|
|
||||||
cleanupFn,
|
|
||||||
ux,
|
|
||||||
),
|
|
||||||
workerErr,
|
|
||||||
"should have seen a worker error, but did not",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.True(t, cleanupCalled, "cleanup function not called")
|
|
||||||
ux.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is a bug, then there is a risk of the event loop never returning
|
|
||||||
testWithTimeout(t, 5*time.Second, test)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_EventLoop_unsubscribeError(t *testing.T) {
|
|
||||||
test := func(t *testing.T) {
|
|
||||||
|
|
||||||
testBus := partybus.NewBus()
|
|
||||||
subscription := testBus.Subscribe()
|
|
||||||
t.Cleanup(testBus.Close)
|
|
||||||
|
|
||||||
finalEvent := partybus.Event{
|
|
||||||
Type: event.CLIExit,
|
|
||||||
}
|
|
||||||
|
|
||||||
worker := func() <-chan error {
|
|
||||||
ret := make(chan error)
|
|
||||||
go func() {
|
|
||||||
t.Log("worker running")
|
|
||||||
// send an empty item (which is ignored) ensuring we've entered the select statement,
|
|
||||||
// then close (a partial shutdown).
|
|
||||||
ret <- nil
|
|
||||||
t.Log("worker sent nothing")
|
|
||||||
close(ret)
|
|
||||||
t.Log("worker closed")
|
|
||||||
// do the other half of the shutdown
|
|
||||||
testBus.Publish(finalEvent)
|
|
||||||
t.Log("worker published final event")
|
|
||||||
}()
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
signaler := func() <-chan os.Signal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ux := &uiMock{
|
|
||||||
t: t,
|
|
||||||
finalEvent: finalEvent,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure the mock sees at least the final event... note the unsubscribe error here
|
|
||||||
ux.On("Handle", finalEvent).Return(partybus.ErrUnsubscribe)
|
|
||||||
// ensure the mock sees basic setup/teardown events
|
|
||||||
ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil)
|
|
||||||
ux.On("Teardown").Return(nil)
|
|
||||||
|
|
||||||
var cleanupCalled bool
|
|
||||||
cleanupFn := func() {
|
|
||||||
t.Log("cleanup called")
|
|
||||||
cleanupCalled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// unsubscribe errors should be handled and ignored, not propagated. We are additionally asserting that
|
|
||||||
// this case is handled as a controlled shutdown (this test should not timeout)
|
|
||||||
assert.NoError(t,
|
|
||||||
EventLoop(
|
|
||||||
worker(),
|
|
||||||
signaler(),
|
|
||||||
subscription,
|
|
||||||
cleanupFn,
|
|
||||||
ux,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.True(t, cleanupCalled, "cleanup function not called")
|
|
||||||
ux.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is a bug, then there is a risk of the event loop never returning
|
|
||||||
testWithTimeout(t, 5*time.Second, test)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_EventLoop_handlerError(t *testing.T) {
|
|
||||||
test := func(t *testing.T) {
|
|
||||||
|
|
||||||
testBus := partybus.NewBus()
|
|
||||||
subscription := testBus.Subscribe()
|
|
||||||
t.Cleanup(testBus.Close)
|
|
||||||
|
|
||||||
finalEvent := partybus.Event{
|
|
||||||
Type: event.CLIExit,
|
|
||||||
Error: fmt.Errorf("an exit error occured"),
|
|
||||||
}
|
|
||||||
|
|
||||||
worker := func() <-chan error {
|
|
||||||
ret := make(chan error)
|
|
||||||
go func() {
|
|
||||||
t.Log("worker running")
|
|
||||||
// send an empty item (which is ignored) ensuring we've entered the select statement,
|
|
||||||
// then close (a partial shutdown).
|
|
||||||
ret <- nil
|
|
||||||
t.Log("worker sent nothing")
|
|
||||||
close(ret)
|
|
||||||
t.Log("worker closed")
|
|
||||||
// do the other half of the shutdown
|
|
||||||
testBus.Publish(finalEvent)
|
|
||||||
t.Log("worker published final event")
|
|
||||||
}()
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
signaler := func() <-chan os.Signal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ux := &uiMock{
|
|
||||||
t: t,
|
|
||||||
finalEvent: finalEvent,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure the mock sees at least the final event... note the event error is propagated
|
|
||||||
ux.On("Handle", finalEvent).Return(finalEvent.Error)
|
|
||||||
// ensure the mock sees basic setup/teardown events
|
|
||||||
ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil)
|
|
||||||
ux.On("Teardown").Return(nil)
|
|
||||||
|
|
||||||
var cleanupCalled bool
|
|
||||||
cleanupFn := func() {
|
|
||||||
t.Log("cleanup called")
|
|
||||||
cleanupCalled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle errors SHOULD propagate the event loop. We are additionally asserting that this case is
|
|
||||||
// handled as a controlled shutdown (this test should not timeout)
|
|
||||||
assert.ErrorIs(t,
|
|
||||||
EventLoop(
|
|
||||||
worker(),
|
|
||||||
signaler(),
|
|
||||||
subscription,
|
|
||||||
cleanupFn,
|
|
||||||
ux,
|
|
||||||
),
|
|
||||||
finalEvent.Error,
|
|
||||||
"should have seen a event error, but did not",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.True(t, cleanupCalled, "cleanup function not called")
|
|
||||||
ux.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is a bug, then there is a risk of the event loop never returning
|
|
||||||
testWithTimeout(t, 5*time.Second, test)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_EventLoop_signalsStopExecution(t *testing.T) {
|
|
||||||
test := func(t *testing.T) {
|
|
||||||
|
|
||||||
testBus := partybus.NewBus()
|
|
||||||
subscription := testBus.Subscribe()
|
|
||||||
t.Cleanup(testBus.Close)
|
|
||||||
|
|
||||||
worker := func() <-chan error {
|
|
||||||
// the worker will never return work and the event loop will always be waiting...
|
|
||||||
return make(chan error)
|
|
||||||
}
|
|
||||||
|
|
||||||
signaler := func() <-chan os.Signal {
|
|
||||||
ret := make(chan os.Signal)
|
|
||||||
go func() {
|
|
||||||
ret <- syscall.SIGINT
|
|
||||||
// note: we do NOT close the channel to ensure the event loop does not depend on that behavior to exit
|
|
||||||
}()
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
ux := &uiMock{
|
|
||||||
t: t,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure the mock sees basic setup/teardown events
|
|
||||||
ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil)
|
|
||||||
ux.On("Teardown").Return(nil)
|
|
||||||
|
|
||||||
var cleanupCalled bool
|
|
||||||
cleanupFn := func() {
|
|
||||||
t.Log("cleanup called")
|
|
||||||
cleanupCalled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.NoError(t,
|
|
||||||
EventLoop(
|
|
||||||
worker(),
|
|
||||||
signaler(),
|
|
||||||
subscription,
|
|
||||||
cleanupFn,
|
|
||||||
ux,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.True(t, cleanupCalled, "cleanup function not called")
|
|
||||||
ux.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is a bug, then there is a risk of the event loop never returning
|
|
||||||
testWithTimeout(t, 5*time.Second, test)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_EventLoop_uiTeardownError(t *testing.T) {
|
|
||||||
test := func(t *testing.T) {
|
|
||||||
|
|
||||||
testBus := partybus.NewBus()
|
|
||||||
subscription := testBus.Subscribe()
|
|
||||||
t.Cleanup(testBus.Close)
|
|
||||||
|
|
||||||
finalEvent := partybus.Event{
|
|
||||||
Type: event.CLIExit,
|
|
||||||
}
|
|
||||||
|
|
||||||
worker := func() <-chan error {
|
|
||||||
ret := make(chan error)
|
|
||||||
go func() {
|
|
||||||
t.Log("worker running")
|
|
||||||
// send an empty item (which is ignored) ensuring we've entered the select statement,
|
|
||||||
// then close (a partial shutdown).
|
|
||||||
ret <- nil
|
|
||||||
t.Log("worker sent nothing")
|
|
||||||
close(ret)
|
|
||||||
t.Log("worker closed")
|
|
||||||
// do the other half of the shutdown
|
|
||||||
testBus.Publish(finalEvent)
|
|
||||||
t.Log("worker published final event")
|
|
||||||
}()
|
|
||||||
return ret
|
|
||||||
}
|
|
||||||
|
|
||||||
signaler := func() <-chan os.Signal {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
ux := &uiMock{
|
|
||||||
t: t,
|
|
||||||
finalEvent: finalEvent,
|
|
||||||
}
|
|
||||||
|
|
||||||
teardownError := fmt.Errorf("sorry, dave, the UI doesn't want to be torn down")
|
|
||||||
|
|
||||||
// ensure the mock sees at least the final event... note the event error is propagated
|
|
||||||
ux.On("Handle", finalEvent).Return(nil)
|
|
||||||
// ensure the mock sees basic setup/teardown events
|
|
||||||
ux.On("Setup", mock.AnythingOfType("func() error")).Return(nil)
|
|
||||||
ux.On("Teardown").Return(teardownError)
|
|
||||||
|
|
||||||
var cleanupCalled bool
|
|
||||||
cleanupFn := func() {
|
|
||||||
t.Log("cleanup called")
|
|
||||||
cleanupCalled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure we see an error returned
|
|
||||||
assert.ErrorIs(t,
|
|
||||||
EventLoop(
|
|
||||||
worker(),
|
|
||||||
signaler(),
|
|
||||||
subscription,
|
|
||||||
cleanupFn,
|
|
||||||
ux,
|
|
||||||
),
|
|
||||||
teardownError,
|
|
||||||
"should have seen a UI teardown error, but did not",
|
|
||||||
)
|
|
||||||
|
|
||||||
assert.True(t, cleanupCalled, "cleanup function not called")
|
|
||||||
ux.AssertExpectations(t)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if there is a bug, then there is a risk of the event loop never returning
|
|
||||||
testWithTimeout(t, 5*time.Second, test)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testWithTimeout(t *testing.T, timeout time.Duration, test func(*testing.T)) {
|
|
||||||
done := make(chan bool)
|
|
||||||
go func() {
|
|
||||||
test(t)
|
|
||||||
done <- true
|
|
||||||
}()
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(timeout):
|
|
||||||
t.Fatal("test timed out")
|
|
||||||
case <-done:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
package eventloop
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
)
|
|
||||||
|
|
||||||
func SetupSignals() <-chan os.Signal {
|
|
||||||
c := make(chan os.Signal, 1) // Note: A buffered channel is recommended for this; see https://golang.org/pkg/os/signal/#Notify
|
|
||||||
|
|
||||||
interruptions := []os.Signal{
|
|
||||||
syscall.SIGINT,
|
|
||||||
syscall.SIGTERM,
|
|
||||||
}
|
|
||||||
|
|
||||||
signal.Notify(c, interruptions...)
|
|
||||||
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
package eventloop
|
package eventloop
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/anchore/syft/internal/config"
|
"github.com/anchore/syft/cmd/syft/cli/options"
|
||||||
"github.com/anchore/syft/internal/file"
|
"github.com/anchore/syft/internal/file"
|
||||||
"github.com/anchore/syft/syft"
|
"github.com/anchore/syft/syft"
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
@ -15,10 +15,10 @@ import (
|
|||||||
|
|
||||||
type Task func(*sbom.Artifacts, source.Source) ([]artifact.Relationship, error)
|
type Task func(*sbom.Artifacts, source.Source) ([]artifact.Relationship, error)
|
||||||
|
|
||||||
func Tasks(app *config.Application) ([]Task, error) {
|
func Tasks(opts *options.Catalog) ([]Task, error) {
|
||||||
var tasks []Task
|
var tasks []Task
|
||||||
|
|
||||||
generators := []func(app *config.Application) (Task, error){
|
generators := []func(opts *options.Catalog) (Task, error){
|
||||||
generateCatalogPackagesTask,
|
generateCatalogPackagesTask,
|
||||||
generateCatalogFileMetadataTask,
|
generateCatalogFileMetadataTask,
|
||||||
generateCatalogFileDigestsTask,
|
generateCatalogFileDigestsTask,
|
||||||
@ -27,7 +27,7 @@ func Tasks(app *config.Application) ([]Task, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, generator := range generators {
|
for _, generator := range generators {
|
||||||
task, err := generator(app)
|
task, err := generator(opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -40,13 +40,13 @@ func Tasks(app *config.Application) ([]Task, error) {
|
|||||||
return tasks, nil
|
return tasks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateCatalogPackagesTask(app *config.Application) (Task, error) {
|
func generateCatalogPackagesTask(opts *options.Catalog) (Task, error) {
|
||||||
if !app.Package.Cataloger.Enabled {
|
if !opts.Package.Cataloger.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
||||||
packageCatalog, relationships, theDistro, err := syft.CatalogPackages(src, app.ToCatalogerConfig())
|
packageCatalog, relationships, theDistro, err := syft.CatalogPackages(src, opts.ToCatalogerConfig())
|
||||||
|
|
||||||
results.Packages = packageCatalog
|
results.Packages = packageCatalog
|
||||||
results.LinuxDistribution = theDistro
|
results.LinuxDistribution = theDistro
|
||||||
@ -57,15 +57,15 @@ func generateCatalogPackagesTask(app *config.Application) (Task, error) {
|
|||||||
return task, nil
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateCatalogFileMetadataTask(app *config.Application) (Task, error) {
|
func generateCatalogFileMetadataTask(opts *options.Catalog) (Task, error) {
|
||||||
if !app.FileMetadata.Cataloger.Enabled {
|
if !opts.FileMetadata.Cataloger.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
metadataCataloger := filemetadata.NewCataloger()
|
metadataCataloger := filemetadata.NewCataloger()
|
||||||
|
|
||||||
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
||||||
resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt)
|
resolver, err := src.FileResolver(opts.FileMetadata.Cataloger.GetScope())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -81,12 +81,12 @@ func generateCatalogFileMetadataTask(app *config.Application) (Task, error) {
|
|||||||
return task, nil
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateCatalogFileDigestsTask(app *config.Application) (Task, error) {
|
func generateCatalogFileDigestsTask(opts *options.Catalog) (Task, error) {
|
||||||
if !app.FileMetadata.Cataloger.Enabled {
|
if !opts.FileMetadata.Cataloger.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
hashes, err := file.Hashers(app.FileMetadata.Digests...)
|
hashes, err := file.Hashers(opts.FileMetadata.Digests...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -94,7 +94,7 @@ func generateCatalogFileDigestsTask(app *config.Application) (Task, error) {
|
|||||||
digestsCataloger := filedigest.NewCataloger(hashes)
|
digestsCataloger := filedigest.NewCataloger(hashes)
|
||||||
|
|
||||||
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
||||||
resolver, err := src.FileResolver(app.FileMetadata.Cataloger.ScopeOpt)
|
resolver, err := src.FileResolver(opts.FileMetadata.Cataloger.GetScope())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -110,23 +110,23 @@ func generateCatalogFileDigestsTask(app *config.Application) (Task, error) {
|
|||||||
return task, nil
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateCatalogSecretsTask(app *config.Application) (Task, error) {
|
func generateCatalogSecretsTask(opts *options.Catalog) (Task, error) {
|
||||||
if !app.Secrets.Cataloger.Enabled {
|
if !opts.Secrets.Cataloger.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
patterns, err := secrets.GenerateSearchPatterns(secrets.DefaultSecretsPatterns, app.Secrets.AdditionalPatterns, app.Secrets.ExcludePatternNames)
|
patterns, err := secrets.GenerateSearchPatterns(secrets.DefaultSecretsPatterns, opts.Secrets.AdditionalPatterns, opts.Secrets.ExcludePatternNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
secretsCataloger, err := secrets.NewCataloger(patterns, app.Secrets.RevealValues, app.Secrets.SkipFilesAboveSize) //nolint:staticcheck
|
secretsCataloger, err := secrets.NewCataloger(patterns, opts.Secrets.RevealValues, opts.Secrets.SkipFilesAboveSize) //nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
||||||
resolver, err := src.FileResolver(app.Secrets.Cataloger.ScopeOpt)
|
resolver, err := src.FileResolver(opts.Secrets.Cataloger.GetScope())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -142,18 +142,18 @@ func generateCatalogSecretsTask(app *config.Application) (Task, error) {
|
|||||||
return task, nil
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateCatalogContentsTask(app *config.Application) (Task, error) {
|
func generateCatalogContentsTask(opts *options.Catalog) (Task, error) {
|
||||||
if !app.FileContents.Cataloger.Enabled {
|
if !opts.FileContents.Cataloger.Enabled {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
contentsCataloger, err := filecontent.NewCataloger(app.FileContents.Globs, app.FileContents.SkipFilesAboveSize) //nolint:staticcheck
|
contentsCataloger, err := filecontent.NewCataloger(opts.FileContents.Globs, opts.FileContents.SkipFilesAboveSize) //nolint:staticcheck
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
task := func(results *sbom.Artifacts, src source.Source) ([]artifact.Relationship, error) {
|
||||||
resolver, err := src.FileResolver(app.FileContents.Cataloger.ScopeOpt)
|
resolver, err := src.FileResolver(opts.FileContents.Cataloger.GetScope())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -169,16 +169,17 @@ func generateCatalogContentsTask(app *config.Application) (Task, error) {
|
|||||||
return task, nil
|
return task, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunTask(t Task, a *sbom.Artifacts, src source.Source, c chan<- artifact.Relationship, errs chan<- error) {
|
func RunTask(t Task, a *sbom.Artifacts, src source.Source, c chan<- artifact.Relationship) error {
|
||||||
defer close(c)
|
defer close(c)
|
||||||
|
|
||||||
relationships, err := t(a, src)
|
relationships, err := t(a, src)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs <- err
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, relationship := range relationships {
|
for _, relationship := range relationships {
|
||||||
c <- relationship
|
c <- relationship
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +1,17 @@
|
|||||||
package options
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/anchore/clio"
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type AttestOptions struct {
|
type Attest struct {
|
||||||
Key string
|
// IMPORTANT: do not show the attestation key/password in any YAML/JSON output (sensitive information)
|
||||||
|
Key secret `yaml:"key" json:"key" mapstructure:"key"`
|
||||||
|
Password secret `yaml:"password" json:"password" mapstructure:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var _ Interface = (*AttestOptions)(nil)
|
var _ clio.FlagAdder = (*Attest)(nil)
|
||||||
|
|
||||||
func (o AttestOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
func (o Attest) AddFlags(flags clio.FlagSet) {
|
||||||
cmd.Flags().StringVarP(&o.Key, "key", "k", "", "the key to use for the attestation")
|
flags.StringVarP((*string)(&o.Key), "key", "k", "the key to use for the attestation")
|
||||||
return bindAttestConfigOptions(cmd.Flags(), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:revive
|
|
||||||
func bindAttestConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
|
||||||
if err := v.BindPFlag("attest.key", flags.Lookup("key")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
168
cmd/syft/cli/options/catalog.go
Normal file
168
cmd/syft/cli/options/catalog.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/iancoleman/strcase"
|
||||||
|
"github.com/mitchellh/go-homedir"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/fangs"
|
||||||
|
"github.com/anchore/syft/internal"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger"
|
||||||
|
golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
|
||||||
|
pythonCataloger "github.com/anchore/syft/syft/pkg/cataloger/python"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Catalog struct {
|
||||||
|
Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"`
|
||||||
|
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
|
||||||
|
Golang golang `yaml:"golang" json:"golang" mapstructure:"golang"`
|
||||||
|
LinuxKernel linuxKernel `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"`
|
||||||
|
Python python `yaml:"python" json:"python" mapstructure:"python"`
|
||||||
|
FileMetadata fileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
|
||||||
|
FileClassification fileClassification `yaml:"file-classification" json:"file-classification" mapstructure:"file-classification"`
|
||||||
|
FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"`
|
||||||
|
Secrets secrets `yaml:"secrets" json:"secrets" mapstructure:"secrets"`
|
||||||
|
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
|
||||||
|
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
|
||||||
|
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
|
||||||
|
Name string `yaml:"name" json:"name" mapstructure:"name"`
|
||||||
|
Source sourceCfg `yaml:"source" json:"source" mapstructure:"source"`
|
||||||
|
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
|
||||||
|
DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` // specify default image pull source
|
||||||
|
BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths
|
||||||
|
ExcludeBinaryOverlapByOwnership bool `yaml:"exclude-binary-overlap-by-ownership" json:"exclude-binary-overlap-by-ownership" mapstructure:"exclude-binary-overlap-by-ownership"` // exclude synthetic binary packages owned by os package files
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ interface {
|
||||||
|
clio.FlagAdder
|
||||||
|
clio.PostLoader
|
||||||
|
} = (*Catalog)(nil)
|
||||||
|
|
||||||
|
func DefaultCatalog() Catalog {
|
||||||
|
return Catalog{
|
||||||
|
Package: defaultPkg(),
|
||||||
|
LinuxKernel: defaultLinuxKernel(),
|
||||||
|
FileMetadata: defaultFileMetadata(),
|
||||||
|
FileClassification: defaultFileClassification(),
|
||||||
|
FileContents: defaultFileContents(),
|
||||||
|
Secrets: defaultSecrets(),
|
||||||
|
Source: defaultSourceCfg(),
|
||||||
|
Parallelism: 1,
|
||||||
|
ExcludeBinaryOverlapByOwnership: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Catalog) AddFlags(flags clio.FlagSet) {
|
||||||
|
var validScopeValues []string
|
||||||
|
for _, scope := range source.AllScopes {
|
||||||
|
validScopeValues = append(validScopeValues, strcase.ToDelimited(string(scope), '-'))
|
||||||
|
}
|
||||||
|
flags.StringVarP(&cfg.Package.Cataloger.Scope, "scope", "s",
|
||||||
|
fmt.Sprintf("selection of layers to catalog, options=%v", validScopeValues))
|
||||||
|
|
||||||
|
flags.StringVarP(&cfg.Platform, "platform", "",
|
||||||
|
"an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')")
|
||||||
|
|
||||||
|
flags.StringArrayVarP(&cfg.Exclusions, "exclude", "",
|
||||||
|
"exclude paths from being scanned using a glob expression")
|
||||||
|
|
||||||
|
flags.StringArrayVarP(&cfg.Catalogers, "catalogers", "",
|
||||||
|
"enable one or more package catalogers")
|
||||||
|
|
||||||
|
flags.StringVarP(&cfg.Source.Name, "name", "",
|
||||||
|
"set the name of the target being analyzed")
|
||||||
|
|
||||||
|
if pfp, ok := flags.(fangs.PFlagSetProvider); ok {
|
||||||
|
flagSet := pfp.PFlagSet()
|
||||||
|
flagSet.Lookup("name").Deprecated = "use: source-name"
|
||||||
|
}
|
||||||
|
|
||||||
|
flags.StringVarP(&cfg.Source.Name, "source-name", "",
|
||||||
|
"set the name of the target being analyzed")
|
||||||
|
|
||||||
|
flags.StringVarP(&cfg.Source.Version, "source-version", "",
|
||||||
|
"set the version of the target being analyzed")
|
||||||
|
|
||||||
|
flags.StringVarP(&cfg.BasePath, "base-path", "",
|
||||||
|
"base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *Catalog) PostLoad() error {
|
||||||
|
// parse options on this struct
|
||||||
|
var catalogers []string
|
||||||
|
for _, c := range cfg.Catalogers {
|
||||||
|
for _, f := range strings.Split(c, ",") {
|
||||||
|
catalogers = append(catalogers, strings.TrimSpace(f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(catalogers)
|
||||||
|
cfg.Catalogers = catalogers
|
||||||
|
|
||||||
|
if err := checkDefaultSourceValues(cfg.DefaultImagePullSource); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.Name != "" {
|
||||||
|
log.Warnf("name parameter is deprecated. please use: source-name. name will be removed in a future version")
|
||||||
|
if cfg.Source.Name == "" {
|
||||||
|
cfg.Source.Name = cfg.Name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg Catalog) ToCatalogerConfig() cataloger.Config {
|
||||||
|
return cataloger.Config{
|
||||||
|
Search: cataloger.SearchConfig{
|
||||||
|
IncludeIndexedArchives: cfg.Package.SearchIndexedArchives,
|
||||||
|
IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives,
|
||||||
|
Scope: cfg.Package.Cataloger.GetScope(),
|
||||||
|
},
|
||||||
|
Catalogers: cfg.Catalogers,
|
||||||
|
Parallelism: cfg.Parallelism,
|
||||||
|
Golang: golangCataloger.NewGoCatalogerOpts().
|
||||||
|
WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses).
|
||||||
|
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
|
||||||
|
WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses).
|
||||||
|
WithProxy(cfg.Golang.Proxy).
|
||||||
|
WithNoProxy(cfg.Golang.NoProxy),
|
||||||
|
LinuxKernel: kernel.LinuxCatalogerConfig{
|
||||||
|
CatalogModules: cfg.LinuxKernel.CatalogModules,
|
||||||
|
},
|
||||||
|
Python: pythonCataloger.CatalogerConfig{
|
||||||
|
GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
|
||||||
|
},
|
||||||
|
ExcludeBinaryOverlapByOwnership: cfg.ExcludeBinaryOverlapByOwnership,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var validDefaultSourceValues = []string{"registry", "docker", "podman", ""}
|
||||||
|
|
||||||
|
func checkDefaultSourceValues(source string) error {
|
||||||
|
validValues := internal.NewStringSet(validDefaultSourceValues...)
|
||||||
|
if !validValues.Contains(source) {
|
||||||
|
validValuesString := strings.Join(validDefaultSourceValues, ", ")
|
||||||
|
return fmt.Errorf("%s is not a valid default source; please use one of the following: %s''", source, validValuesString)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func expandFilePath(file string) (string, error) {
|
||||||
|
if file != "" {
|
||||||
|
expandedPath, err := homedir.Expand(file)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("unable to expand file path=%q: %w", file, err)
|
||||||
|
}
|
||||||
|
file = expandedPath
|
||||||
|
}
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
6
cmd/syft/cli/options/config.go
Normal file
6
cmd/syft/cli/options/config.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
// Config holds a reference to the specific config file that was used to load application configuration
|
||||||
|
type Config struct {
|
||||||
|
ConfigFile string `yaml:"config" json:"config" mapstructure:"config"`
|
||||||
|
}
|
||||||
17
cmd/syft/cli/options/file_classification.go
Normal file
17
cmd/syft/cli/options/file_classification.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileClassification struct {
|
||||||
|
Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultFileClassification() fileClassification {
|
||||||
|
return fileClassification{
|
||||||
|
Cataloger: scope{
|
||||||
|
Scope: source.SquashedScope.String(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
21
cmd/syft/cli/options/file_contents.go
Normal file
21
cmd/syft/cli/options/file_contents.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anchore/syft/internal/file"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileContents struct {
|
||||||
|
Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||||
|
SkipFilesAboveSize int64 `yaml:"skip-files-above-size" json:"skip-files-above-size" mapstructure:"skip-files-above-size"`
|
||||||
|
Globs []string `yaml:"globs" json:"globs" mapstructure:"globs"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultFileContents() fileContents {
|
||||||
|
return fileContents{
|
||||||
|
Cataloger: scope{
|
||||||
|
Scope: source.SquashedScope.String(),
|
||||||
|
},
|
||||||
|
SkipFilesAboveSize: 1 * file.MB,
|
||||||
|
}
|
||||||
|
}
|
||||||
19
cmd/syft/cli/options/file_metadata.go
Normal file
19
cmd/syft/cli/options/file_metadata.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fileMetadata struct {
|
||||||
|
Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||||
|
Digests []string `yaml:"digests" json:"digests" mapstructure:"digests"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultFileMetadata() fileMetadata {
|
||||||
|
return fileMetadata{
|
||||||
|
Cataloger: scope{
|
||||||
|
Scope: source.SquashedScope.String(),
|
||||||
|
},
|
||||||
|
Digests: []string{"sha256"},
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,49 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
const defaultFulcioURL = "https://fulcio.sigstore.dev"
|
|
||||||
|
|
||||||
// FulcioOptions is the wrapper for Fulcio related options.
|
|
||||||
type FulcioOptions struct {
|
|
||||||
URL string
|
|
||||||
IdentityToken string
|
|
||||||
InsecureSkipFulcioVerify bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Interface = (*FulcioOptions)(nil)
|
|
||||||
|
|
||||||
// AddFlags implements Interface
|
|
||||||
func (o *FulcioOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
|
||||||
// TODO: change this back to api.SigstorePublicServerURL after the v1 migration is complete.
|
|
||||||
cmd.Flags().StringVar(&o.URL, "fulcio-url", defaultFulcioURL,
|
|
||||||
"address of sigstore PKI server")
|
|
||||||
|
|
||||||
cmd.Flags().StringVar(&o.IdentityToken, "identity-token", "",
|
|
||||||
"identity token to use for certificate from fulcio")
|
|
||||||
|
|
||||||
cmd.Flags().BoolVar(&o.InsecureSkipFulcioVerify, "insecure-skip-verify", false,
|
|
||||||
"skip verifying fulcio certificat and the SCT (Signed Certificate Timestamp) (this should only be used for testing).")
|
|
||||||
return bindFulcioConfigOptions(cmd.Flags(), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:revive
|
|
||||||
func bindFulcioConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
|
||||||
if err := v.BindPFlag("attest.fulcio-url", flags.Lookup("fulcio-url")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("attest.fulcio-identity-token", flags.Lookup("identity-token")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("attest.insecure-skip-verify", flags.Lookup("insecure-skip-verify")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,6 +1,4 @@
|
|||||||
package config
|
package options
|
||||||
|
|
||||||
import "github.com/spf13/viper"
|
|
||||||
|
|
||||||
type golang struct {
|
type golang struct {
|
||||||
SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
|
SearchLocalModCacheLicenses bool `json:"search-local-mod-cache-licenses" yaml:"search-local-mod-cache-licenses" mapstructure:"search-local-mod-cache-licenses"`
|
||||||
@ -9,11 +7,3 @@ type golang struct {
|
|||||||
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
|
Proxy string `json:"proxy" yaml:"proxy" mapstructure:"proxy"`
|
||||||
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
|
NoProxy string `json:"no-proxy" yaml:"no-proxy" mapstructure:"no-proxy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg golang) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("golang.search-local-mod-cache-licenses", false)
|
|
||||||
v.SetDefault("golang.local-mod-cache-dir", "")
|
|
||||||
v.SetDefault("golang.search-remote-licenses", false)
|
|
||||||
v.SetDefault("golang.proxy", "")
|
|
||||||
v.SetDefault("golang.no-proxy", "")
|
|
||||||
}
|
|
||||||
11
cmd/syft/cli/options/linux_kernel.go
Normal file
11
cmd/syft/cli/options/linux_kernel.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
type linuxKernel struct {
|
||||||
|
CatalogModules bool `json:"catalog-modules" yaml:"catalog-modules" mapstructure:"catalog-modules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultLinuxKernel() linuxKernel {
|
||||||
|
return linuxKernel{
|
||||||
|
CatalogModules: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,49 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
const DefaultOIDCIssuerURL = "https://oauth2.sigstore.dev/auth"
|
|
||||||
|
|
||||||
// OIDCOptions is the wrapper for OIDC related options.
|
|
||||||
type OIDCOptions struct {
|
|
||||||
Issuer string
|
|
||||||
ClientID string
|
|
||||||
RedirectURL string
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Interface = (*OIDCOptions)(nil)
|
|
||||||
|
|
||||||
// AddFlags implements Interface
|
|
||||||
func (o *OIDCOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
|
||||||
cmd.Flags().StringVar(&o.Issuer, "oidc-issuer", DefaultOIDCIssuerURL,
|
|
||||||
"OIDC provider to be used to issue ID token")
|
|
||||||
|
|
||||||
cmd.Flags().StringVar(&o.ClientID, "oidc-client-id", "sigstore",
|
|
||||||
"OIDC client ID for application")
|
|
||||||
|
|
||||||
cmd.Flags().StringVar(&o.RedirectURL, "oidc-redirect-url", "",
|
|
||||||
"OIDC redirect URL (Optional)")
|
|
||||||
|
|
||||||
return bindOIDCConfigOptions(cmd.Flags(), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:revive
|
|
||||||
func bindOIDCConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
|
||||||
if err := v.BindPFlag("attest.oidc-issuer", flags.Lookup("oidc-issuer")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("attest.oidc-client-id", flags.Lookup("oidc-client-id")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("attest.oidc-redirect-url", flags.Lookup("oidc-redirect-url")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Interface interface {
|
|
||||||
// AddFlags adds this options' flags to the cobra command.
|
|
||||||
AddFlags(cmd *cobra.Command, v *viper.Viper) error
|
|
||||||
}
|
|
||||||
100
cmd/syft/cli/options/output.go
Normal file
100
cmd/syft/cli/options/output.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/fangs"
|
||||||
|
"github.com/anchore/syft/syft/formats"
|
||||||
|
"github.com/anchore/syft/syft/formats/table"
|
||||||
|
"github.com/anchore/syft/syft/formats/template"
|
||||||
|
"github.com/anchore/syft/syft/sbom"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MultiOutput has the standard output options syft accepts: multiple -o, --file, --template
|
||||||
|
type MultiOutput struct {
|
||||||
|
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output
|
||||||
|
OutputFile `yaml:",inline" json:"" mapstructure:",squash"`
|
||||||
|
OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ interface {
|
||||||
|
clio.FlagAdder
|
||||||
|
} = (*MultiOutput)(nil)
|
||||||
|
|
||||||
|
func DefaultOutput() MultiOutput {
|
||||||
|
return MultiOutput{
|
||||||
|
Outputs: []string{string(table.ID)},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *MultiOutput) AddFlags(flags clio.FlagSet) {
|
||||||
|
flags.StringArrayVarP(&o.Outputs, "output", "o",
|
||||||
|
fmt.Sprintf("report output format (<format>=<file> to output to a file), formats=%v", formats.AllIDs()))
|
||||||
|
|
||||||
|
flags.StringVarP(&o.OutputTemplatePath, "template", "t",
|
||||||
|
"specify the path to a Go template file")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *MultiOutput) SBOMWriter() (sbom.Writer, error) {
|
||||||
|
return makeSBOMWriter(o.Outputs, o.File, o.OutputTemplatePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SingleOutput allows only 1 output to be specified, with a user able to set what options are allowed by setting AllowableOptions
|
||||||
|
type SingleOutput struct {
|
||||||
|
AllowableOptions []string `yaml:"-" json:"-" mapstructure:"-"`
|
||||||
|
Output string `yaml:"output" json:"output" mapstructure:"output"`
|
||||||
|
OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ clio.FlagAdder = (*SingleOutput)(nil)
|
||||||
|
|
||||||
|
func (o *SingleOutput) AddFlags(flags clio.FlagSet) {
|
||||||
|
flags.StringVarP(&o.Output, "output", "o",
|
||||||
|
fmt.Sprintf("report output format, options=%v", o.AllowableOptions))
|
||||||
|
|
||||||
|
if slices.Contains(o.AllowableOptions, template.ID.String()) {
|
||||||
|
flags.StringVarP(&o.OutputTemplatePath, "template", "t",
|
||||||
|
"specify the path to a Go template file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *SingleOutput) SBOMWriter(file string) (sbom.Writer, error) {
|
||||||
|
return makeSBOMWriter([]string{o.Output}, file, o.OutputTemplatePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deprecated: OutputFile is only the --file argument
|
||||||
|
type OutputFile struct {
|
||||||
|
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ interface {
|
||||||
|
clio.FlagAdder
|
||||||
|
clio.PostLoader
|
||||||
|
} = (*OutputFile)(nil)
|
||||||
|
|
||||||
|
func (o *OutputFile) AddFlags(flags clio.FlagSet) {
|
||||||
|
flags.StringVarP(&o.File, "file", "",
|
||||||
|
"file to write the default report output to (default is STDOUT)")
|
||||||
|
|
||||||
|
if pfp, ok := flags.(fangs.PFlagSetProvider); ok {
|
||||||
|
flagSet := pfp.PFlagSet()
|
||||||
|
flagSet.Lookup("file").Deprecated = "use: output"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OutputFile) PostLoad() error {
|
||||||
|
if o.File != "" {
|
||||||
|
file, err := expandFilePath(o.File)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
o.File = file
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *OutputFile) SBOMWriter(f sbom.Format) (sbom.Writer, error) {
|
||||||
|
return makeSBOMWriterForFormat(f, o.File)
|
||||||
|
}
|
||||||
@ -1,118 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/formats"
|
|
||||||
"github.com/anchore/syft/syft/formats/table"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger"
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PackagesOptions struct {
|
|
||||||
Scope string
|
|
||||||
Output []string
|
|
||||||
OutputTemplatePath string
|
|
||||||
File string
|
|
||||||
Platform string
|
|
||||||
Exclude []string
|
|
||||||
Catalogers []string
|
|
||||||
SourceName string
|
|
||||||
SourceVersion string
|
|
||||||
BasePath string
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Interface = (*PackagesOptions)(nil)
|
|
||||||
|
|
||||||
func (o *PackagesOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
|
||||||
cmd.Flags().StringVarP(&o.Scope, "scope", "s", cataloger.DefaultSearchConfig().Scope.String(),
|
|
||||||
fmt.Sprintf("selection of layers to catalog, options=%v", source.AllScopes))
|
|
||||||
|
|
||||||
cmd.Flags().StringArrayVarP(&o.Output, "output", "o", []string{string(table.ID)},
|
|
||||||
fmt.Sprintf("report output format, options=%v", formats.AllIDs()))
|
|
||||||
|
|
||||||
cmd.Flags().StringVarP(&o.File, "file", "", "",
|
|
||||||
"file to write the default report output to (default is STDOUT)")
|
|
||||||
|
|
||||||
cmd.Flags().StringVarP(&o.OutputTemplatePath, "template", "t", "",
|
|
||||||
"specify the path to a Go template file")
|
|
||||||
|
|
||||||
cmd.Flags().StringVarP(&o.Platform, "platform", "", "",
|
|
||||||
"an optional platform specifier for container image sources (e.g. 'linux/arm64', 'linux/arm64/v8', 'arm64', 'linux')")
|
|
||||||
|
|
||||||
cmd.Flags().StringArrayVarP(&o.Exclude, "exclude", "", nil,
|
|
||||||
"exclude paths from being scanned using a glob expression")
|
|
||||||
|
|
||||||
cmd.Flags().StringArrayVarP(&o.Catalogers, "catalogers", "", nil,
|
|
||||||
"enable one or more package catalogers")
|
|
||||||
|
|
||||||
cmd.Flags().StringVarP(&o.SourceName, "name", "", "",
|
|
||||||
"set the name of the target being analyzed")
|
|
||||||
cmd.Flags().Lookup("name").Deprecated = "use: source-name"
|
|
||||||
|
|
||||||
cmd.Flags().StringVarP(&o.SourceName, "source-name", "", "",
|
|
||||||
"set the name of the target being analyzed")
|
|
||||||
|
|
||||||
cmd.Flags().StringVarP(&o.SourceVersion, "source-version", "", "",
|
|
||||||
"set the name of the target being analyzed")
|
|
||||||
|
|
||||||
cmd.Flags().StringVarP(&o.BasePath, "base-path", "", "",
|
|
||||||
"base directory for scanning, no links will be followed above this directory, and all paths will be reported relative to this directory")
|
|
||||||
|
|
||||||
return bindPackageConfigOptions(cmd.Flags(), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:revive
|
|
||||||
func bindPackageConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
|
||||||
// Formatting & Input options //////////////////////////////////////////////
|
|
||||||
|
|
||||||
if err := v.BindPFlag("package.cataloger.scope", flags.Lookup("scope")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("file", flags.Lookup("file")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("exclude", flags.Lookup("exclude")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("catalogers", flags.Lookup("catalogers")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("name", flags.Lookup("name")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("source.name", flags.Lookup("source-name")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("source.version", flags.Lookup("source-version")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("output", flags.Lookup("output")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("output-template-path", flags.Lookup("template")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("platform", flags.Lookup("platform")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := v.BindPFlag("base-path", flags.Lookup("base-path")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
23
cmd/syft/cli/options/pkg.go
Normal file
23
cmd/syft/cli/options/pkg.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger"
|
||||||
|
)
|
||||||
|
|
||||||
|
type pkg struct {
|
||||||
|
Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||||
|
SearchUnindexedArchives bool `yaml:"search-unindexed-archives" json:"search-unindexed-archives" mapstructure:"search-unindexed-archives"`
|
||||||
|
SearchIndexedArchives bool `yaml:"search-indexed-archives" json:"search-indexed-archives" mapstructure:"search-indexed-archives"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultPkg() pkg {
|
||||||
|
c := cataloger.DefaultSearchConfig()
|
||||||
|
return pkg{
|
||||||
|
SearchIndexedArchives: c.IncludeIndexedArchives,
|
||||||
|
SearchUnindexedArchives: c.IncludeUnindexedArchives,
|
||||||
|
Cataloger: scope{
|
||||||
|
Enabled: true,
|
||||||
|
Scope: c.Scope.String(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,5 @@
|
|||||||
package config
|
package options
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type python struct {
|
type python struct {
|
||||||
GuessUnpinnedRequirements bool `json:"guess-unpinned-requirements" yaml:"guess-unpinned-requirements" mapstructure:"guess-unpinned-requirements"`
|
GuessUnpinnedRequirements bool `json:"guess-unpinned-requirements" yaml:"guess-unpinned-requirements" mapstructure:"guess-unpinned-requirements"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg python) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("python.guess-unpinned-requirements", false)
|
|
||||||
}
|
|
||||||
84
cmd/syft/cli/options/registry.go
Normal file
84
cmd/syft/cli/options/registry.go
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RegistryCredentials struct {
|
||||||
|
Authority string `yaml:"authority" json:"authority" mapstructure:"authority"`
|
||||||
|
// IMPORTANT: do not show any credential information, use secret type to automatically redact the values
|
||||||
|
Username secret `yaml:"username" json:"username" mapstructure:"username"`
|
||||||
|
Password secret `yaml:"password" json:"password" mapstructure:"password"`
|
||||||
|
Token secret `yaml:"token" json:"token" mapstructure:"token"`
|
||||||
|
|
||||||
|
TLSCert string `yaml:"tls-cert,omitempty" json:"tls-cert,omitempty" mapstructure:"tls-cert"`
|
||||||
|
TLSKey string `yaml:"tls-key,omitempty" json:"tls-key,omitempty" mapstructure:"tls-key"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type registry struct {
|
||||||
|
InsecureSkipTLSVerify bool `yaml:"insecure-skip-tls-verify" json:"insecure-skip-tls-verify" mapstructure:"insecure-skip-tls-verify"`
|
||||||
|
InsecureUseHTTP bool `yaml:"insecure-use-http" json:"insecure-use-http" mapstructure:"insecure-use-http"`
|
||||||
|
Auth []RegistryCredentials `yaml:"auth" json:"auth" mapstructure:"auth"`
|
||||||
|
CACert string `yaml:"ca-cert" json:"ca-cert" mapstructure:"ca-cert"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ clio.PostLoader = (*registry)(nil)
|
||||||
|
|
||||||
|
func (cfg *registry) PostLoad() error {
|
||||||
|
// there may be additional credentials provided by env var that should be appended to the set of credentials
|
||||||
|
authority, username, password, token, tlsCert, tlsKey :=
|
||||||
|
os.Getenv("SYFT_REGISTRY_AUTH_AUTHORITY"),
|
||||||
|
os.Getenv("SYFT_REGISTRY_AUTH_USERNAME"),
|
||||||
|
os.Getenv("SYFT_REGISTRY_AUTH_PASSWORD"),
|
||||||
|
os.Getenv("SYFT_REGISTRY_AUTH_TOKEN"),
|
||||||
|
os.Getenv("SYFT_REGISTRY_AUTH_TLS_CERT"),
|
||||||
|
os.Getenv("SYFT_REGISTRY_AUTH_TLS_KEY")
|
||||||
|
|
||||||
|
if hasNonEmptyCredentials(username, password, token, tlsCert, tlsKey) {
|
||||||
|
// note: we prepend the credentials such that the environment variables take precedence over on-disk configuration.
|
||||||
|
// since this PostLoad is called before the PostLoad on the Auth credentials list,
|
||||||
|
// all appropriate redactions will be added
|
||||||
|
cfg.Auth = append([]RegistryCredentials{
|
||||||
|
{
|
||||||
|
Authority: authority,
|
||||||
|
Username: secret(username),
|
||||||
|
Password: secret(password),
|
||||||
|
Token: secret(token),
|
||||||
|
TLSCert: tlsCert,
|
||||||
|
TLSKey: tlsKey,
|
||||||
|
},
|
||||||
|
}, cfg.Auth...)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasNonEmptyCredentials(username, password, token, tlsCert, tlsKey string) bool {
|
||||||
|
hasUserPass := username != "" && password != ""
|
||||||
|
hasToken := token != ""
|
||||||
|
hasTLSMaterial := tlsCert != "" && tlsKey != ""
|
||||||
|
return hasUserPass || hasToken || hasTLSMaterial
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *registry) ToOptions() *image.RegistryOptions {
|
||||||
|
var auth = make([]image.RegistryCredentials, len(cfg.Auth))
|
||||||
|
for i, a := range cfg.Auth {
|
||||||
|
auth[i] = image.RegistryCredentials{
|
||||||
|
Authority: a.Authority,
|
||||||
|
Username: a.Username.String(),
|
||||||
|
Password: a.Password.String(),
|
||||||
|
Token: a.Token.String(),
|
||||||
|
ClientCert: a.TLSCert,
|
||||||
|
ClientKey: a.TLSKey,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &image.RegistryOptions{
|
||||||
|
InsecureSkipTLSVerify: cfg.InsecureSkipTLSVerify,
|
||||||
|
InsecureUseHTTP: cfg.InsecureUseHTTP,
|
||||||
|
Credentials: auth,
|
||||||
|
CAFileOrDir: cfg.CACert,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package config
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -11,48 +11,60 @@ import (
|
|||||||
|
|
||||||
func TestHasNonEmptyCredentials(t *testing.T) {
|
func TestHasNonEmptyCredentials(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
username, password, token string
|
username, password, token, cert, key string
|
||||||
expected bool
|
expected bool
|
||||||
}{
|
}{
|
||||||
|
|
||||||
{
|
{
|
||||||
"", "", "",
|
"", "", "", "", "",
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"user", "", "",
|
"user", "", "", "", "",
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"", "pass", "",
|
"", "pass", "", "", "",
|
||||||
false,
|
false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"", "pass", "tok",
|
"", "pass", "tok", "", "",
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"user", "", "tok",
|
"user", "", "tok", "", "",
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"", "", "tok",
|
"", "", "tok", "", "",
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"user", "pass", "tok",
|
"user", "pass", "tok", "", "",
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
"user", "pass", "",
|
"user", "pass", "", "", "",
|
||||||
true,
|
true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"", "", "", "cert", "key",
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"", "", "", "cert", "",
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"", "", "", "", "key",
|
||||||
|
false,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(fmt.Sprintf("%+v", test), func(t *testing.T) {
|
t.Run(fmt.Sprintf("%+v", test), func(t *testing.T) {
|
||||||
assert.Equal(t, test.expected, hasNonEmptyCredentials(test.username, test.password, test.token))
|
assert.Equal(t, test.expected, hasNonEmptyCredentials(test.username, test.password, test.token, test.cert, test.key))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -102,6 +114,29 @@ func Test_registry_ToOptions(t *testing.T) {
|
|||||||
Credentials: []image.RegistryCredentials{},
|
Credentials: []image.RegistryCredentials{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "provide all tls configuration",
|
||||||
|
input: registry{
|
||||||
|
CACert: "ca.crt",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
Auth: []RegistryCredentials{
|
||||||
|
{
|
||||||
|
TLSCert: "client.crt",
|
||||||
|
TLSKey: "client.key",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: image.RegistryOptions{
|
||||||
|
CAFileOrDir: "ca.crt",
|
||||||
|
InsecureSkipTLSVerify: true,
|
||||||
|
Credentials: []image.RegistryCredentials{
|
||||||
|
{
|
||||||
|
ClientCert: "client.crt",
|
||||||
|
ClientKey: "client.key",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
@ -1,33 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
const DefaultRekorURL = "https://rekor.sigstore.dev"
|
|
||||||
|
|
||||||
// RekorOptions is the wrapper for Rekor related options.
|
|
||||||
type RekorOptions struct {
|
|
||||||
URL string
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Interface = (*RekorOptions)(nil)
|
|
||||||
|
|
||||||
// AddFlags implements Interface
|
|
||||||
func (o *RekorOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
|
||||||
cmd.Flags().StringVar(&o.URL, "rekor-url", DefaultRekorURL,
|
|
||||||
"address of rekor STL server")
|
|
||||||
return bindRekorConfigOptions(cmd.Flags(), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:revive
|
|
||||||
func bindRekorConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
|
||||||
// TODO: config re-design
|
|
||||||
if err := v.BindPFlag("attest.rekor-url", flags.Lookup("rekor-url")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/pflag"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RootOptions struct {
|
|
||||||
Config string
|
|
||||||
Quiet bool
|
|
||||||
Verbose int
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Interface = (*RootOptions)(nil)
|
|
||||||
|
|
||||||
func (o *RootOptions) AddFlags(cmd *cobra.Command, v *viper.Viper) error {
|
|
||||||
cmd.PersistentFlags().StringVarP(&o.Config, "config", "c", "", "application config file")
|
|
||||||
cmd.PersistentFlags().CountVarP(&o.Verbose, "verbose", "v", "increase verbosity (-v = info, -vv = debug)")
|
|
||||||
cmd.PersistentFlags().BoolVarP(&o.Quiet, "quiet", "q", false, "suppress all logging output")
|
|
||||||
|
|
||||||
return bindRootConfigOptions(cmd.PersistentFlags(), v)
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:revive
|
|
||||||
func bindRootConfigOptions(flags *pflag.FlagSet, v *viper.Viper) error {
|
|
||||||
if err := v.BindPFlag("config", flags.Lookup("config")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := v.BindPFlag("verbosity", flags.Lookup("verbose")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := v.BindPFlag("quiet", flags.Lookup("quiet")); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
27
cmd/syft/cli/options/scope.go
Normal file
27
cmd/syft/cli/options/scope.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/syft/syft/source"
|
||||||
|
)
|
||||||
|
|
||||||
|
type scope struct {
|
||||||
|
Enabled bool `yaml:"enabled" json:"enabled" mapstructure:"enabled"`
|
||||||
|
Scope string `yaml:"scope" json:"scope" mapstructure:"scope"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ clio.PostLoader = (*scope)(nil)
|
||||||
|
|
||||||
|
func (opt *scope) PostLoad() error {
|
||||||
|
s := opt.GetScope()
|
||||||
|
if s == source.UnknownScope {
|
||||||
|
return fmt.Errorf("bad scope value %v", opt.Scope)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt scope) GetScope() source.Scope {
|
||||||
|
return source.ParseScope(opt.Scope)
|
||||||
|
}
|
||||||
25
cmd/syft/cli/options/secret.go
Normal file
25
cmd/syft/cli/options/secret.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/syft/internal/redact"
|
||||||
|
)
|
||||||
|
|
||||||
|
type secret string
|
||||||
|
|
||||||
|
var _ interface {
|
||||||
|
fmt.Stringer
|
||||||
|
clio.PostLoader
|
||||||
|
} = (*secret)(nil)
|
||||||
|
|
||||||
|
// PostLoad needs to use a pointer receiver, even if it's not modifying the value
|
||||||
|
func (r *secret) PostLoad() error {
|
||||||
|
redact.Add(string(*r))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r secret) String() string {
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
@ -1,29 +1,23 @@
|
|||||||
package config
|
package options
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/file"
|
"github.com/anchore/syft/internal/file"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
type secrets struct {
|
type secrets struct {
|
||||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
Cataloger scope `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
||||||
AdditionalPatterns map[string]string `yaml:"additional-patterns" json:"additional-patterns" mapstructure:"additional-patterns"`
|
AdditionalPatterns map[string]string `yaml:"additional-patterns" json:"additional-patterns" mapstructure:"additional-patterns"`
|
||||||
ExcludePatternNames []string `yaml:"exclude-pattern-names" json:"exclude-pattern-names" mapstructure:"exclude-pattern-names"`
|
ExcludePatternNames []string `yaml:"exclude-pattern-names" json:"exclude-pattern-names" mapstructure:"exclude-pattern-names"`
|
||||||
RevealValues bool `yaml:"reveal-values" json:"reveal-values" mapstructure:"reveal-values"`
|
RevealValues bool `yaml:"reveal-values" json:"reveal-values" mapstructure:"reveal-values"`
|
||||||
SkipFilesAboveSize int64 `yaml:"skip-files-above-size" json:"skip-files-above-size" mapstructure:"skip-files-above-size"`
|
SkipFilesAboveSize int64 `yaml:"skip-files-above-size" json:"skip-files-above-size" mapstructure:"skip-files-above-size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg secrets) loadDefaultValues(v *viper.Viper) {
|
func defaultSecrets() secrets {
|
||||||
v.SetDefault("secrets.cataloger.enabled", catalogerEnabledDefault)
|
return secrets{
|
||||||
v.SetDefault("secrets.cataloger.scope", source.AllLayersScope)
|
Cataloger: scope{
|
||||||
v.SetDefault("secrets.reveal-values", false)
|
Scope: source.AllLayersScope.String(),
|
||||||
v.SetDefault("secrets.skip-files-above-size", 1*file.MB)
|
},
|
||||||
v.SetDefault("secrets.additional-patterns", map[string]string{})
|
SkipFilesAboveSize: 1 * file.MB,
|
||||||
v.SetDefault("secrets.exclude-pattern-names", []string{})
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *secrets) parseConfigValues() error {
|
|
||||||
return cfg.Cataloger.parseConfigValues()
|
|
||||||
}
|
}
|
||||||
@ -1,6 +1,4 @@
|
|||||||
package config
|
package options
|
||||||
|
|
||||||
import "github.com/spf13/viper"
|
|
||||||
|
|
||||||
type sourceCfg struct {
|
type sourceCfg struct {
|
||||||
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
Name string `json:"name" yaml:"name" mapstructure:"name"`
|
||||||
@ -12,6 +10,10 @@ type fileSource struct {
|
|||||||
Digests []string `json:"digests" yaml:"digests" mapstructure:"digests"`
|
Digests []string `json:"digests" yaml:"digests" mapstructure:"digests"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg sourceCfg) loadDefaultValues(v *viper.Viper) {
|
func defaultSourceCfg() sourceCfg {
|
||||||
v.SetDefault("source.file.digests", []string{"sha256"})
|
return sourceCfg{
|
||||||
|
File: fileSource{
|
||||||
|
Digests: []string{"sha256"},
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
11
cmd/syft/cli/options/update_check.go
Normal file
11
cmd/syft/cli/options/update_check.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package options
|
||||||
|
|
||||||
|
type UpdateCheck struct {
|
||||||
|
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultUpdateCheck() UpdateCheck {
|
||||||
|
return UpdateCheck{
|
||||||
|
CheckForAppUpdate: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,18 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsVerbose(app *config.Application) (result bool) {
|
|
||||||
isPipedInput, err := internal.IsPipedInput()
|
|
||||||
if err != nil {
|
|
||||||
// since we can't tell if there was piped input we assume that there could be to disable the ETUI
|
|
||||||
log.Warnf("unable to determine if there is piped input: %+v", err)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
// verbosity should consider if there is piped input (in which case we should not show the ETUI)
|
|
||||||
return app.Verbosity > 0 || isPipedInput
|
|
||||||
}
|
|
||||||
@ -1,17 +0,0 @@
|
|||||||
package options
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type VersionOptions struct {
|
|
||||||
Output string
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ Interface = (*VersionOptions)(nil)
|
|
||||||
|
|
||||||
func (o *VersionOptions) AddFlags(cmd *cobra.Command, _ *viper.Viper) error {
|
|
||||||
cmd.Flags().StringVarP(&o.Output, "output", "o", "text", "format to show version information (available=[text, json])")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -26,9 +26,9 @@ var _ interface {
|
|||||||
sbom.Writer
|
sbom.Writer
|
||||||
} = (*sbomStreamWriter)(nil)
|
} = (*sbomStreamWriter)(nil)
|
||||||
|
|
||||||
// MakeSBOMWriter creates a sbom.Writer for output or returns an error. this will either return a valid writer
|
// makeSBOMWriter creates a sbom.Writer for output or returns an error. this will either return a valid writer
|
||||||
// or an error but neither both and if there is no error, sbom.Writer.Close() should be called
|
// or an error but neither both and if there is no error, sbom.Writer.Close() should be called
|
||||||
func MakeSBOMWriter(outputs []string, defaultFile, templateFilePath string) (sbom.Writer, error) {
|
func makeSBOMWriter(outputs []string, defaultFile, templateFilePath string) (sbom.Writer, error) {
|
||||||
outputOptions, err := parseSBOMOutputFlags(outputs, defaultFile, templateFilePath)
|
outputOptions, err := parseSBOMOutputFlags(outputs, defaultFile, templateFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -42,8 +42,8 @@ func MakeSBOMWriter(outputs []string, defaultFile, templateFilePath string) (sbo
|
|||||||
return writer, nil
|
return writer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// MakeSBOMWriterForFormat creates a sbom.Writer for for the given format or returns an error.
|
// makeSBOMWriterForFormat creates a sbom.Writer for for the given format or returns an error.
|
||||||
func MakeSBOMWriterForFormat(format sbom.Format, path string) (sbom.Writer, error) {
|
func makeSBOMWriterForFormat(format sbom.Format, path string) (sbom.Writer, error) {
|
||||||
writer, err := newSBOMMultiWriter(newSBOMWriterDescription(format, path))
|
writer, err := newSBOMMultiWriter(newSBOMWriterDescription(format, path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@ -34,7 +34,7 @@ func Test_MakeSBOMWriter(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
_, err := MakeSBOMWriter(tt.outputs, "", "")
|
_, err := makeSBOMWriter(tt.outputs, "", "")
|
||||||
tt.wantErr(t, err)
|
tt.wantErr(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -183,11 +183,7 @@ func Test_newSBOMMultiWriter(t *testing.T) {
|
|||||||
switch w := mw.writers[i].(type) {
|
switch w := mw.writers[i].(type) {
|
||||||
case *sbomStreamWriter:
|
case *sbomStreamWriter:
|
||||||
assert.Equal(t, string(w.format.ID()), e.format)
|
assert.Equal(t, string(w.format.ID()), e.format)
|
||||||
if e.file != "" {
|
|
||||||
assert.NotNil(t, w.out)
|
assert.NotNil(t, w.out)
|
||||||
} else {
|
|
||||||
assert.NotNil(t, w.out)
|
|
||||||
}
|
|
||||||
if e.file != "" {
|
if e.file != "" {
|
||||||
assert.FileExists(t, tmp+e.file)
|
assert.FileExists(t, tmp+e.file)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,86 +0,0 @@
|
|||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/packages"
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
packagesExample = ` {{.appName}} {{.command}} alpine:latest a summary of discovered packages
|
|
||||||
{{.appName}} {{.command}} alpine:latest -o json show all possible cataloging details
|
|
||||||
{{.appName}} {{.command}} alpine:latest -o cyclonedx show a CycloneDX formatted SBOM
|
|
||||||
{{.appName}} {{.command}} alpine:latest -o cyclonedx-json show a CycloneDX JSON formatted SBOM
|
|
||||||
{{.appName}} {{.command}} alpine:latest -o spdx show a SPDX 2.3 Tag-Value formatted SBOM
|
|
||||||
{{.appName}} {{.command}} alpine:latest -o spdx@2.2 show a SPDX 2.2 Tag-Value formatted SBOM
|
|
||||||
{{.appName}} {{.command}} alpine:latest -o spdx-json show a SPDX 2.3 JSON formatted SBOM
|
|
||||||
{{.appName}} {{.command}} alpine:latest -o spdx-json@2.2 show a SPDX 2.2 JSON formatted SBOM
|
|
||||||
{{.appName}} {{.command}} alpine:latest -vv show verbose debug information
|
|
||||||
{{.appName}} {{.command}} alpine:latest -o template -t my_format.tmpl show a SBOM formatted according to given template file
|
|
||||||
|
|
||||||
Supports the following image sources:
|
|
||||||
{{.appName}} {{.command}} yourrepo/yourimage:tag defaults to using images from a Docker daemon. If Docker is not present, the image is pulled directly from the registry.
|
|
||||||
{{.appName}} {{.command}} path/to/a/file/or/dir a Docker tar, OCI tar, OCI directory, SIF container, or generic filesystem directory
|
|
||||||
`
|
|
||||||
|
|
||||||
schemeHelpHeader = "You can also explicitly specify the scheme to use:"
|
|
||||||
imageSchemeHelp = ` {{.appName}} {{.command}} docker:yourrepo/yourimage:tag explicitly use the Docker daemon
|
|
||||||
{{.appName}} {{.command}} podman:yourrepo/yourimage:tag explicitly use the Podman daemon
|
|
||||||
{{.appName}} {{.command}} registry:yourrepo/yourimage:tag pull image directly from a registry (no container runtime required)
|
|
||||||
{{.appName}} {{.command}} docker-archive:path/to/yourimage.tar use a tarball from disk for archives created from "docker save"
|
|
||||||
{{.appName}} {{.command}} oci-archive:path/to/yourimage.tar use a tarball from disk for OCI archives (from Skopeo or otherwise)
|
|
||||||
{{.appName}} {{.command}} oci-dir:path/to/yourimage read directly from a path on disk for OCI layout directories (from Skopeo or otherwise)
|
|
||||||
{{.appName}} {{.command}} singularity:path/to/yourimage.sif read directly from a Singularity Image Format (SIF) container on disk
|
|
||||||
`
|
|
||||||
nonImageSchemeHelp = ` {{.appName}} {{.command}} dir:path/to/yourproject read directly from a path on disk (any directory)
|
|
||||||
{{.appName}} {{.command}} file:path/to/yourproject/file read directly from a path on disk (any single file)
|
|
||||||
`
|
|
||||||
packagesSchemeHelp = "\n" + indent + schemeHelpHeader + "\n" + imageSchemeHelp + nonImageSchemeHelp
|
|
||||||
|
|
||||||
packagesHelp = packagesExample + packagesSchemeHelp
|
|
||||||
)
|
|
||||||
|
|
||||||
//nolint:dupl
|
|
||||||
func Packages(v *viper.Viper, app *config.Application, ro *options.RootOptions, po *options.PackagesOptions) *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "packages [SOURCE]",
|
|
||||||
Short: "Generate a package SBOM",
|
|
||||||
Long: "Generate a packaged-based Software Bill Of Materials (SBOM) from container images and filesystems",
|
|
||||||
Example: internal.Tprintf(packagesHelp, map[string]interface{}{
|
|
||||||
"appName": internal.ApplicationName,
|
|
||||||
"command": "packages",
|
|
||||||
}),
|
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if err := app.LoadAllValues(v, ro.Config); err != nil {
|
|
||||||
return fmt.Errorf("invalid application config: %w", err)
|
|
||||||
}
|
|
||||||
// configure logging for command
|
|
||||||
newLogWrapper(app)
|
|
||||||
logApplicationConfig(app)
|
|
||||||
return validateArgs(cmd, args)
|
|
||||||
},
|
|
||||||
SilenceUsage: true,
|
|
||||||
SilenceErrors: true,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if app.CheckForAppUpdate {
|
|
||||||
// TODO: this is broke, the bus isn't available yet
|
|
||||||
checkForApplicationUpdate()
|
|
||||||
}
|
|
||||||
return packages.Run(cmd.Context(), app, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := po.AddFlags(cmd, v)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
@ -1,192 +0,0 @@
|
|||||||
package packages
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/wagoodman/go-partybus"
|
|
||||||
|
|
||||||
"github.com/anchore/stereoscope"
|
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/eventloop"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/bus"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
"github.com/anchore/syft/internal/file"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/anchore/syft/internal/version"
|
|
||||||
"github.com/anchore/syft/syft"
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
|
||||||
"github.com/anchore/syft/syft/formats/template"
|
|
||||||
"github.com/anchore/syft/syft/sbom"
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Run(_ context.Context, app *config.Application, args []string) error {
|
|
||||||
err := ValidateOutputOptions(app)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
writer, err := options.MakeSBOMWriter(app.Outputs, app.File, app.OutputTemplatePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// could be an image or a directory, with or without a scheme
|
|
||||||
userInput := args[0]
|
|
||||||
|
|
||||||
eventBus := partybus.NewBus()
|
|
||||||
stereoscope.SetBus(eventBus)
|
|
||||||
syft.SetBus(eventBus)
|
|
||||||
subscription := eventBus.Subscribe()
|
|
||||||
|
|
||||||
return eventloop.EventLoop(
|
|
||||||
execWorker(app, userInput, writer),
|
|
||||||
eventloop.SetupSignals(),
|
|
||||||
subscription,
|
|
||||||
stereoscope.Cleanup,
|
|
||||||
ui.Select(options.IsVerbose(app), app.Quiet)...,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:funlen
|
|
||||||
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
|
|
||||||
errs := make(chan error)
|
|
||||||
go func() {
|
|
||||||
defer close(errs)
|
|
||||||
defer bus.Exit()
|
|
||||||
|
|
||||||
detection, err := source.Detect(
|
|
||||||
userInput,
|
|
||||||
source.DetectConfig{
|
|
||||||
DefaultImageSource: app.DefaultImagePullSource,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("could not deteremine source: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var platform *image.Platform
|
|
||||||
|
|
||||||
if app.Platform != "" {
|
|
||||||
platform, err = image.NewPlatform(app.Platform)
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("invalid platform: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hashers, err := file.Hashers(app.Source.File.Digests...)
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("invalid hash: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
src, err := detection.NewSource(
|
|
||||||
source.DetectionSourceConfig{
|
|
||||||
Alias: source.Alias{
|
|
||||||
Name: app.Source.Name,
|
|
||||||
Version: app.Source.Version,
|
|
||||||
},
|
|
||||||
RegistryOptions: app.Registry.ToOptions(),
|
|
||||||
Platform: platform,
|
|
||||||
Exclude: source.ExcludeConfig{
|
|
||||||
Paths: app.Exclusions,
|
|
||||||
},
|
|
||||||
DigestAlgorithms: hashers,
|
|
||||||
BasePath: app.BasePath,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if src != nil {
|
|
||||||
if err := src.Close(); err != nil {
|
|
||||||
log.Tracef("unable to close source: %+v", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
s, err := GenerateSBOM(src, errs, app)
|
|
||||||
if err != nil {
|
|
||||||
errs <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if s == nil {
|
|
||||||
errs <- fmt.Errorf("no SBOM produced for %q", userInput)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := writer.Write(*s); err != nil {
|
|
||||||
errs <- fmt.Errorf("failed to write SBOM: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func GenerateSBOM(src source.Source, errs chan error, app *config.Application) (*sbom.SBOM, error) {
|
|
||||||
tasks, err := eventloop.Tasks(app)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s := sbom.SBOM{
|
|
||||||
Source: src.Describe(),
|
|
||||||
Descriptor: sbom.Descriptor{
|
|
||||||
Name: internal.ApplicationName,
|
|
||||||
Version: version.FromBuild().Version,
|
|
||||||
Configuration: app,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
buildRelationships(&s, src, tasks, errs)
|
|
||||||
|
|
||||||
return &s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildRelationships(s *sbom.SBOM, src source.Source, tasks []eventloop.Task, errs chan error) {
|
|
||||||
var relationships []<-chan artifact.Relationship
|
|
||||||
for _, task := range tasks {
|
|
||||||
c := make(chan artifact.Relationship)
|
|
||||||
relationships = append(relationships, c)
|
|
||||||
go eventloop.RunTask(task, &s.Artifacts, src, c, errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Relationships = append(s.Relationships, MergeRelationships(relationships...)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func MergeRelationships(cs ...<-chan artifact.Relationship) (relationships []artifact.Relationship) {
|
|
||||||
for _, c := range cs {
|
|
||||||
for n := range c {
|
|
||||||
relationships = append(relationships, n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return relationships
|
|
||||||
}
|
|
||||||
|
|
||||||
func ValidateOutputOptions(app *config.Application) error {
|
|
||||||
var usesTemplateOutput bool
|
|
||||||
for _, o := range app.Outputs {
|
|
||||||
if o == template.ID.String() {
|
|
||||||
usesTemplateOutput = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if usesTemplateOutput && app.OutputTemplatePath == "" {
|
|
||||||
return fmt.Errorf(`must specify path to template file when using "template" output format`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/poweruser"
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
)
|
|
||||||
|
|
||||||
const powerUserExample = ` {{.appName}} {{.command}} <image>
|
|
||||||
DEPRECATED - THIS COMMAND WILL BE REMOVED in v1.0.0
|
|
||||||
Only image sources are supported (e.g. docker: , podman: , docker-archive: , oci: , etc.), the directory source (dir:) is not supported, template outputs are not supported.
|
|
||||||
All behavior is controlled via application configuration and environment variables (see https://github.com/anchore/syft#configuration)
|
|
||||||
`
|
|
||||||
|
|
||||||
func PowerUser(v *viper.Viper, app *config.Application, ro *options.RootOptions) *cobra.Command {
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "power-user [IMAGE]",
|
|
||||||
Short: "Run bulk operations on container images",
|
|
||||||
Example: internal.Tprintf(powerUserExample, map[string]interface{}{
|
|
||||||
"appName": internal.ApplicationName,
|
|
||||||
"command": "power-user",
|
|
||||||
}),
|
|
||||||
Args: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if err := app.LoadAllValues(v, ro.Config); err != nil {
|
|
||||||
return fmt.Errorf("invalid application config: %w", err)
|
|
||||||
}
|
|
||||||
// configure logging for command
|
|
||||||
newLogWrapper(app)
|
|
||||||
logApplicationConfig(app)
|
|
||||||
return validateArgs(cmd, args)
|
|
||||||
},
|
|
||||||
Hidden: true,
|
|
||||||
SilenceUsage: true,
|
|
||||||
SilenceErrors: true,
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
if app.CheckForAppUpdate {
|
|
||||||
checkForApplicationUpdate()
|
|
||||||
// TODO: this is broke, the bus isn't available yet
|
|
||||||
}
|
|
||||||
return poweruser.Run(cmd.Context(), app, args)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
@ -1,144 +0,0 @@
|
|||||||
package poweruser
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/gookit/color"
|
|
||||||
"github.com/wagoodman/go-partybus"
|
|
||||||
|
|
||||||
"github.com/anchore/stereoscope"
|
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/eventloop"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/packages"
|
|
||||||
"github.com/anchore/syft/cmd/syft/internal/ui"
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/bus"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
"github.com/anchore/syft/internal/version"
|
|
||||||
"github.com/anchore/syft/syft"
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
|
||||||
"github.com/anchore/syft/syft/formats/syftjson"
|
|
||||||
"github.com/anchore/syft/syft/sbom"
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Run(_ context.Context, app *config.Application, args []string) error {
|
|
||||||
f := syftjson.Format()
|
|
||||||
writer, err := options.MakeSBOMWriterForFormat(f, app.File)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
// inform user at end of run that command will be removed
|
|
||||||
deprecated := color.Style{color.Red, color.OpBold}.Sprint("DEPRECATED: This command will be removed in v1.0.0")
|
|
||||||
fmt.Fprintln(os.Stderr, deprecated)
|
|
||||||
}()
|
|
||||||
|
|
||||||
userInput := args[0]
|
|
||||||
|
|
||||||
eventBus := partybus.NewBus()
|
|
||||||
stereoscope.SetBus(eventBus)
|
|
||||||
syft.SetBus(eventBus)
|
|
||||||
subscription := eventBus.Subscribe()
|
|
||||||
|
|
||||||
return eventloop.EventLoop(
|
|
||||||
execWorker(app, userInput, writer),
|
|
||||||
eventloop.SetupSignals(),
|
|
||||||
subscription,
|
|
||||||
stereoscope.Cleanup,
|
|
||||||
ui.Select(options.IsVerbose(app), app.Quiet)...,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:funlen
|
|
||||||
func execWorker(app *config.Application, userInput string, writer sbom.Writer) <-chan error {
|
|
||||||
errs := make(chan error)
|
|
||||||
go func() {
|
|
||||||
defer close(errs)
|
|
||||||
defer bus.Exit()
|
|
||||||
|
|
||||||
app.Secrets.Cataloger.Enabled = true
|
|
||||||
app.FileMetadata.Cataloger.Enabled = true
|
|
||||||
app.FileContents.Cataloger.Enabled = true
|
|
||||||
app.FileClassification.Cataloger.Enabled = true
|
|
||||||
tasks, err := eventloop.Tasks(app)
|
|
||||||
if err != nil {
|
|
||||||
errs <- err
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
detection, err := source.Detect(
|
|
||||||
userInput,
|
|
||||||
source.DetectConfig{
|
|
||||||
DefaultImageSource: app.DefaultImagePullSource,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("could not deteremine source: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var platform *image.Platform
|
|
||||||
|
|
||||||
if app.Platform != "" {
|
|
||||||
platform, err = image.NewPlatform(app.Platform)
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("invalid platform: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
src, err := detection.NewSource(
|
|
||||||
source.DetectionSourceConfig{
|
|
||||||
Alias: source.Alias{
|
|
||||||
Name: app.Source.Name,
|
|
||||||
Version: app.Source.Version,
|
|
||||||
},
|
|
||||||
RegistryOptions: app.Registry.ToOptions(),
|
|
||||||
Platform: platform,
|
|
||||||
Exclude: source.ExcludeConfig{
|
|
||||||
Paths: app.Exclusions,
|
|
||||||
},
|
|
||||||
DigestAlgorithms: nil,
|
|
||||||
BasePath: app.BasePath,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
if src != nil {
|
|
||||||
defer src.Close()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
errs <- fmt.Errorf("failed to construct source from user input %q: %w", userInput, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s := sbom.SBOM{
|
|
||||||
Source: src.Describe(),
|
|
||||||
Descriptor: sbom.Descriptor{
|
|
||||||
Name: internal.ApplicationName,
|
|
||||||
Version: version.FromBuild().Version,
|
|
||||||
Configuration: app,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var relationships []<-chan artifact.Relationship
|
|
||||||
for _, task := range tasks {
|
|
||||||
c := make(chan artifact.Relationship)
|
|
||||||
relationships = append(relationships, c)
|
|
||||||
|
|
||||||
go eventloop.RunTask(task, &s.Artifacts, src, c, errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.Relationships = append(s.Relationships, packages.MergeRelationships(relationships...)...)
|
|
||||||
|
|
||||||
if err := writer.Write(s); err != nil {
|
|
||||||
errs <- fmt.Errorf("failed to write sbom: %w", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
190
cmd/syft/cli/ui/handle_pull_containerd_image.go
Normal file
190
cmd/syft/cli/ui/handle_pull_containerd_image.go
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
package ui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
|
"github.com/wagoodman/go-partybus"
|
||||||
|
"github.com/wagoodman/go-progress"
|
||||||
|
|
||||||
|
"github.com/anchore/bubbly/bubbles/taskprogress"
|
||||||
|
stereoscopeParsers "github.com/anchore/stereoscope/pkg/event/parsers"
|
||||||
|
"github.com/anchore/stereoscope/pkg/image/containerd"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ interface {
|
||||||
|
progress.Stager
|
||||||
|
progress.Progressable
|
||||||
|
} = (*containerdPullProgressAdapter)(nil)
|
||||||
|
|
||||||
|
type containerdPullStatus interface {
|
||||||
|
Complete() bool
|
||||||
|
Layers() []containerd.LayerID
|
||||||
|
Current(containerd.LayerID) progress.Progressable
|
||||||
|
}
|
||||||
|
|
||||||
|
type containerdPullProgressAdapter struct {
|
||||||
|
status containerdPullStatus
|
||||||
|
formatter containerdPullStatusFormatter
|
||||||
|
}
|
||||||
|
|
||||||
|
type containerdPullStatusFormatter struct {
|
||||||
|
auxInfoStyle lipgloss.Style
|
||||||
|
pullCompletedStyle lipgloss.Style
|
||||||
|
pullDownloadStyle lipgloss.Style
|
||||||
|
pullStageChars []string
|
||||||
|
layerCaps []string
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if pullStatus == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
tsk := m.newTaskProgress(
|
||||||
|
taskprogress.Title{
|
||||||
|
Default: "Pull image",
|
||||||
|
Running: "Pulling image",
|
||||||
|
Success: "Pulled image",
|
||||||
|
},
|
||||||
|
taskprogress.WithStagedProgressable(
|
||||||
|
newContainerdPullProgressAdapter(pullStatus),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
tsk.HintStyle = lipgloss.NewStyle()
|
||||||
|
tsk.HintEndCaps = nil
|
||||||
|
|
||||||
|
return []tea.Model{tsk}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContainerdPullProgressAdapter(status *containerd.PullStatus) *containerdPullProgressAdapter {
|
||||||
|
return &containerdPullProgressAdapter{
|
||||||
|
status: status,
|
||||||
|
formatter: newContainerdPullStatusFormatter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContainerdPullStatusFormatter() containerdPullStatusFormatter {
|
||||||
|
return containerdPullStatusFormatter{
|
||||||
|
auxInfoStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")),
|
||||||
|
pullCompletedStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#fcba03")),
|
||||||
|
pullDownloadStyle: lipgloss.NewStyle().Foreground(lipgloss.Color("#777777")),
|
||||||
|
pullStageChars: strings.Split("▁▃▄▅▆▇█", ""),
|
||||||
|
layerCaps: strings.Split("▕▏", ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d containerdPullProgressAdapter) Size() int64 {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d containerdPullProgressAdapter) Current() int64 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d containerdPullProgressAdapter) Error() error {
|
||||||
|
if d.status.Complete() {
|
||||||
|
return progress.ErrCompleted
|
||||||
|
}
|
||||||
|
// TODO: return intermediate error indications
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d containerdPullProgressAdapter) Stage() string {
|
||||||
|
return d.formatter.Render(d.status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render crafts the given docker image pull status summarized into a single line.
|
||||||
|
func (f containerdPullStatusFormatter) Render(pullStatus containerdPullStatus) string {
|
||||||
|
var size, current uint64
|
||||||
|
|
||||||
|
layers := pullStatus.Layers()
|
||||||
|
status := make(map[containerd.LayerID]progress.Progressable)
|
||||||
|
completed := make([]string, len(layers))
|
||||||
|
|
||||||
|
// fetch the current state
|
||||||
|
for idx, layer := range layers {
|
||||||
|
completed[idx] = " "
|
||||||
|
status[layer] = pullStatus.Current(layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
numCompleted := 0
|
||||||
|
for idx, layer := range layers {
|
||||||
|
prog := status[layer]
|
||||||
|
curN := prog.Current()
|
||||||
|
curSize := prog.Size()
|
||||||
|
|
||||||
|
if progress.IsCompleted(prog) {
|
||||||
|
input := f.pullStageChars[len(f.pullStageChars)-1]
|
||||||
|
completed[idx] = f.formatPullPhase(prog.Error() != nil, input)
|
||||||
|
} else if curN != 0 {
|
||||||
|
var ratio float64
|
||||||
|
switch {
|
||||||
|
case curN == 0 || curSize < 0:
|
||||||
|
ratio = 0
|
||||||
|
case curN >= curSize:
|
||||||
|
ratio = 1
|
||||||
|
default:
|
||||||
|
ratio = float64(curN) / float64(curSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
i := int(ratio * float64(len(f.pullStageChars)-1))
|
||||||
|
input := f.pullStageChars[i]
|
||||||
|
completed[idx] = f.formatPullPhase(status[layer].Error() != nil, input)
|
||||||
|
}
|
||||||
|
|
||||||
|
if progress.IsErrCompleted(status[layer].Error()) {
|
||||||
|
numCompleted++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, layer := range layers {
|
||||||
|
prog := status[layer]
|
||||||
|
size += uint64(prog.Size())
|
||||||
|
current += uint64(prog.Current())
|
||||||
|
}
|
||||||
|
|
||||||
|
var progStr, auxInfo string
|
||||||
|
if len(layers) > 0 {
|
||||||
|
render := strings.Join(completed, "")
|
||||||
|
prefix := f.pullCompletedStyle.Render(fmt.Sprintf("%d Layers", len(layers)))
|
||||||
|
auxInfo = f.auxInfoStyle.Render(fmt.Sprintf("[%s / %s]", humanize.Bytes(current), humanize.Bytes(size)))
|
||||||
|
if len(layers) == numCompleted {
|
||||||
|
auxInfo = f.auxInfoStyle.Render(fmt.Sprintf("[%s] Extracting...", humanize.Bytes(size)))
|
||||||
|
}
|
||||||
|
|
||||||
|
progStr = fmt.Sprintf("%s%s%s%s", prefix, f.layerCap(false), render, f.layerCap(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
return progStr + auxInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// formatPullPhase returns a single character that represents the status of a layer pull.
|
||||||
|
func (f containerdPullStatusFormatter) formatPullPhase(completed bool, inputStr string) string {
|
||||||
|
if completed {
|
||||||
|
return f.pullCompletedStyle.Render(f.pullStageChars[len(f.pullStageChars)-1])
|
||||||
|
}
|
||||||
|
return f.pullDownloadStyle.Render(inputStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f containerdPullStatusFormatter) layerCap(end bool) string {
|
||||||
|
l := len(f.layerCaps)
|
||||||
|
if l == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if end {
|
||||||
|
return f.layerCaps[l-1]
|
||||||
|
}
|
||||||
|
return f.layerCaps[0]
|
||||||
|
}
|
||||||
@ -49,6 +49,7 @@ func New(cfg HandlerConfig) *Handler {
|
|||||||
// register all supported event types with the respective handler functions
|
// register all supported event types with the respective handler functions
|
||||||
d.AddHandlers(map[partybus.EventType]bubbly.EventHandlerFn{
|
d.AddHandlers(map[partybus.EventType]bubbly.EventHandlerFn{
|
||||||
stereoscopeEvent.PullDockerImage: h.handlePullDockerImage,
|
stereoscopeEvent.PullDockerImage: h.handlePullDockerImage,
|
||||||
|
stereoscopeEvent.PullContainerdImage: h.handlePullContainerdImage,
|
||||||
stereoscopeEvent.ReadImage: h.handleReadImage,
|
stereoscopeEvent.ReadImage: h.handleReadImage,
|
||||||
stereoscopeEvent.FetchImage: h.handleFetchImage,
|
stereoscopeEvent.FetchImage: h.handleFetchImage,
|
||||||
syftEvent.PackageCatalogerStarted: h.handlePackageCatalogerStarted,
|
syftEvent.PackageCatalogerStarted: h.handlePackageCatalogerStarted,
|
||||||
|
|||||||
@ -1,72 +0,0 @@
|
|||||||
package cli
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/cmd/syft/cli/options"
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/config"
|
|
||||||
"github.com/anchore/syft/internal/version"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Version(v *viper.Viper, _ *config.Application) *cobra.Command {
|
|
||||||
o := &options.VersionOptions{}
|
|
||||||
cmd := &cobra.Command{
|
|
||||||
Use: "version",
|
|
||||||
Short: "show the version",
|
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
|
||||||
return printVersion(o.Output)
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
err := o.AddFlags(cmd, v)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return cmd
|
|
||||||
}
|
|
||||||
|
|
||||||
func printVersion(output string) error {
|
|
||||||
versionInfo := version.FromBuild()
|
|
||||||
|
|
||||||
switch output {
|
|
||||||
case "text":
|
|
||||||
fmt.Println("Application: ", internal.ApplicationName)
|
|
||||||
fmt.Println("Version: ", versionInfo.Version)
|
|
||||||
fmt.Println("JsonSchemaVersion: ", internal.JSONSchemaVersion)
|
|
||||||
fmt.Println("BuildDate: ", versionInfo.BuildDate)
|
|
||||||
fmt.Println("GitCommit: ", versionInfo.GitCommit)
|
|
||||||
fmt.Println("GitDescription: ", versionInfo.GitDescription)
|
|
||||||
fmt.Println("Platform: ", versionInfo.Platform)
|
|
||||||
fmt.Println("GoVersion: ", versionInfo.GoVersion)
|
|
||||||
fmt.Println("Compiler: ", versionInfo.Compiler)
|
|
||||||
|
|
||||||
case "json":
|
|
||||||
enc := json.NewEncoder(os.Stdout)
|
|
||||||
enc.SetEscapeHTML(false)
|
|
||||||
enc.SetIndent("", " ")
|
|
||||||
err := enc.Encode(&struct {
|
|
||||||
version.Version
|
|
||||||
Application string `json:"application"`
|
|
||||||
}{
|
|
||||||
Version: versionInfo,
|
|
||||||
Application: internal.ApplicationName,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("failed to show version information: %+v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
fmt.Printf("unsupported output format: %s\n", output)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
5
cmd/syft/internal/constants.go
Normal file
5
cmd/syft/internal/constants.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
const (
|
||||||
|
NotProvided = "[not provided]"
|
||||||
|
)
|
||||||
@ -27,12 +27,7 @@ report 1!!>
|
|||||||
|
|
||||||
<notification 2>
|
<notification 2>
|
||||||
<notification 3>
|
<notification 3>
|
||||||
|
A newer version of syft is available for download: v0.33.0 (installed version is [not provided])
|
||||||
|
|
||||||
<my app can be updated!!
|
|
||||||
...to this version>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -33,8 +33,6 @@ func (n *NoUI) Handle(e partybus.Event) error {
|
|||||||
case event.CLIReport, event.CLINotification:
|
case event.CLIReport, event.CLINotification:
|
||||||
// keep these for when the UI is terminated to show to the screen (or perform other events)
|
// keep these for when the UI is terminated to show to the screen (or perform other events)
|
||||||
n.finalizeEvents = append(n.finalizeEvents, e)
|
n.finalizeEvents = append(n.finalizeEvents, e)
|
||||||
case event.CLIExit:
|
|
||||||
return n.subscription.Unsubscribe()
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -118,12 +118,19 @@ func writeAppUpdate(writer io.Writer, events ...partybus.Event) error {
|
|||||||
style := lipgloss.NewStyle().Foreground(lipgloss.Color("13")).Italic(true)
|
style := lipgloss.NewStyle().Foreground(lipgloss.Color("13")).Italic(true)
|
||||||
|
|
||||||
for _, e := range events {
|
for _, e := range events {
|
||||||
notice, err := parsers.ParseCLIAppUpdateAvailable(e)
|
updateCheck, err := parsers.ParseCLIAppUpdateAvailable(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.WithFields("error", err).Warn("failed to parse app update notification")
|
log.WithFields("error", err).Warn("failed to parse app update notification")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if updateCheck.Current == updateCheck.New {
|
||||||
|
log.Tracef("update check event with identical versions: %s", updateCheck.Current)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
notice := fmt.Sprintf("A newer version of syft is available for download: %s (installed version is %s)", updateCheck.New, updateCheck.Current)
|
||||||
|
|
||||||
if _, err := fmt.Fprintln(writer, style.Render(notice)); err != nil {
|
if _, err := fmt.Fprintln(writer, style.Render(notice)); err != nil {
|
||||||
// don't let this be fatal
|
// don't let this be fatal
|
||||||
log.WithFields("error", err).Warn("failed to write app update notification")
|
log.WithFields("error", err).Warn("failed to write app update notification")
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/event"
|
"github.com/anchore/syft/syft/event"
|
||||||
|
"github.com/anchore/syft/syft/event/parsers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_postUIEventWriter_write(t *testing.T) {
|
func Test_postUIEventWriter_write(t *testing.T) {
|
||||||
@ -35,7 +36,10 @@ func Test_postUIEventWriter_write(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: event.CLIAppUpdateAvailable,
|
Type: event.CLIAppUpdateAvailable,
|
||||||
Value: "\n\n<my app can be updated!!\n...to this version>\n\n",
|
Value: parsers.UpdateCheck{
|
||||||
|
New: "v0.33.0",
|
||||||
|
Current: "[not provided]",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: event.CLINotification,
|
Type: event.CLINotification,
|
||||||
@ -62,7 +66,10 @@ func Test_postUIEventWriter_write(t *testing.T) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: event.CLIAppUpdateAvailable,
|
Type: event.CLIAppUpdateAvailable,
|
||||||
Value: "<app update>",
|
Value: parsers.UpdateCheck{
|
||||||
|
New: "<new version>",
|
||||||
|
Current: "<current version>",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: event.CLIReport,
|
Type: event.CLIReport,
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
//go:build linux || darwin || netbsd
|
|
||||||
// +build linux darwin netbsd
|
|
||||||
|
|
||||||
package ui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
|
|
||||||
"golang.org/x/term"
|
|
||||||
|
|
||||||
"github.com/anchore/clio"
|
|
||||||
handler "github.com/anchore/syft/cmd/syft/cli/ui"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Select is responsible for determining the specific UI function given select user option, the current platform
|
|
||||||
// config values, and environment status (such as a TTY being present). The first UI in the returned slice of UIs
|
|
||||||
// is intended to be used and the UIs that follow are meant to be attempted only in a fallback posture when there
|
|
||||||
// are environmental problems (e.g. cannot write to the terminal). A writer is provided to capture the output of
|
|
||||||
// the final SBOM report.
|
|
||||||
func Select(verbose, quiet bool) (uis []clio.UI) {
|
|
||||||
isStdoutATty := term.IsTerminal(int(os.Stdout.Fd()))
|
|
||||||
isStderrATty := term.IsTerminal(int(os.Stderr.Fd()))
|
|
||||||
notATerminal := !isStderrATty && !isStdoutATty
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case runtime.GOOS == "windows" || verbose || quiet || notATerminal || !isStderrATty:
|
|
||||||
uis = append(uis, None(quiet))
|
|
||||||
default:
|
|
||||||
// TODO: it may make sense in the future to pass handler options into select
|
|
||||||
h := handler.New(handler.DefaultHandlerConfig())
|
|
||||||
uis = append(uis, New(h, verbose, quiet))
|
|
||||||
}
|
|
||||||
|
|
||||||
return uis
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
//go:build windows
|
|
||||||
// +build windows
|
|
||||||
|
|
||||||
package ui
|
|
||||||
|
|
||||||
import "github.com/anchore/clio"
|
|
||||||
|
|
||||||
// Select is responsible for determining the specific UI function given select user option, the current platform
|
|
||||||
// config values, and environment status (such as a TTY being present). The first UI in the returned slice of UIs
|
|
||||||
// is intended to be used and the UIs that follow are meant to be attempted only in a fallback posture when there
|
|
||||||
// are environmental problems (e.g. cannot write to the terminal). A writer is provided to capture the output of
|
|
||||||
// the final SBOM report.
|
|
||||||
func Select(verbose, quiet bool) (uis []clio.UI) {
|
|
||||||
return append(uis, None(quiet))
|
|
||||||
}
|
|
||||||
@ -1,16 +1,18 @@
|
|||||||
package ui
|
package ui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
|
|
||||||
|
"github.com/anchore/bubbly"
|
||||||
"github.com/anchore/bubbly/bubbles/frame"
|
"github.com/anchore/bubbly/bubbles/frame"
|
||||||
"github.com/anchore/clio"
|
"github.com/anchore/clio"
|
||||||
"github.com/anchore/go-logger"
|
"github.com/anchore/go-logger"
|
||||||
handler "github.com/anchore/syft/cmd/syft/cli/ui"
|
|
||||||
"github.com/anchore/syft/internal/bus"
|
"github.com/anchore/syft/internal/bus"
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/syft/internal/log"
|
||||||
"github.com/anchore/syft/syft/event"
|
"github.com/anchore/syft/syft/event"
|
||||||
@ -29,13 +31,13 @@ type UI struct {
|
|||||||
subscription partybus.Unsubscribable
|
subscription partybus.Unsubscribable
|
||||||
finalizeEvents []partybus.Event
|
finalizeEvents []partybus.Event
|
||||||
|
|
||||||
handler *handler.Handler
|
handler *bubbly.HandlerCollection
|
||||||
frame tea.Model
|
frame tea.Model
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(h *handler.Handler, _, quiet bool) *UI {
|
func New(quiet bool, handlers ...bubbly.EventHandler) *UI {
|
||||||
return &UI{
|
return &UI{
|
||||||
handler: h,
|
handler: bubbly.NewHandlerCollection(handlers...),
|
||||||
frame: frame.New(),
|
frame: frame.New(),
|
||||||
running: &sync.WaitGroup{},
|
running: &sync.WaitGroup{},
|
||||||
quiet: quiet,
|
quiet: quiet,
|
||||||
@ -49,38 +51,30 @@ func (m *UI) Setup(subscription partybus.Unsubscribable) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
m.subscription = subscription
|
m.subscription = subscription
|
||||||
m.program = tea.NewProgram(m, tea.WithOutput(os.Stderr), tea.WithInput(os.Stdin))
|
m.program = tea.NewProgram(m, tea.WithOutput(os.Stderr), tea.WithInput(os.Stdin), tea.WithoutSignalHandler())
|
||||||
m.running.Add(1)
|
m.running.Add(1)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
defer m.running.Done()
|
defer m.running.Done()
|
||||||
if _, err := m.program.Run(); err != nil {
|
if _, err := m.program.Run(); err != nil {
|
||||||
log.Errorf("unable to start UI: %+v", err)
|
log.Errorf("unable to start UI: %+v", err)
|
||||||
m.exit()
|
bus.ExitWithInterrupt()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UI) exit() {
|
|
||||||
// stop the event loop
|
|
||||||
bus.Exit()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *UI) Handle(e partybus.Event) error {
|
func (m *UI) Handle(e partybus.Event) error {
|
||||||
if m.program != nil {
|
if m.program != nil {
|
||||||
m.program.Send(e)
|
m.program.Send(e)
|
||||||
if e.Type == event.CLIExit {
|
|
||||||
return m.subscription.Unsubscribe()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *UI) Teardown(force bool) error {
|
func (m *UI) Teardown(force bool) error {
|
||||||
if !force {
|
if !force {
|
||||||
m.handler.Running.Wait()
|
m.handler.Wait()
|
||||||
m.program.Quit()
|
m.program.Quit()
|
||||||
// typically in all cases we would want to wait for the UI to finish. However there are still error cases
|
// typically in all cases we would want to wait for the UI to finish. However there are still error cases
|
||||||
// that are not accounted for, resulting in hangs. For now, we'll just wait for the UI to finish in the
|
// that are not accounted for, resulting in hangs. For now, we'll just wait for the UI to finish in the
|
||||||
@ -88,7 +82,19 @@ func (m *UI) Teardown(force bool) error {
|
|||||||
// string from the worker (outside of the UI after teardown).
|
// string from the worker (outside of the UI after teardown).
|
||||||
m.running.Wait()
|
m.running.Wait()
|
||||||
} else {
|
} else {
|
||||||
m.program.Kill()
|
_ = runWithTimeout(250*time.Millisecond, func() error {
|
||||||
|
m.handler.Wait()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
// it may be tempting to use Kill() however it has been found that this can cause the terminal to be left in
|
||||||
|
// a bad state (where Ctrl+C and other control characters no longer works for future processes in that terminal).
|
||||||
|
m.program.Quit()
|
||||||
|
|
||||||
|
_ = runWithTimeout(250*time.Millisecond, func() error {
|
||||||
|
m.running.Wait()
|
||||||
|
return nil
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: allow for writing out the full log output to the screen (only a partial log is shown currently)
|
// TODO: allow for writing out the full log output to the screen (only a partial log is shown currently)
|
||||||
@ -107,7 +113,6 @@ func (m UI) RespondsTo() []partybus.EventType {
|
|||||||
return append([]partybus.EventType{
|
return append([]partybus.EventType{
|
||||||
event.CLIReport,
|
event.CLIReport,
|
||||||
event.CLINotification,
|
event.CLINotification,
|
||||||
event.CLIExit,
|
|
||||||
event.CLIAppUpdateAvailable,
|
event.CLIAppUpdateAvailable,
|
||||||
}, m.handler.RespondsTo()...)
|
}, m.handler.RespondsTo()...)
|
||||||
}
|
}
|
||||||
@ -126,8 +131,10 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
|
// today we treat esc and ctrl+c the same, but in the future when the worker has a graceful way to
|
||||||
|
// cancel in-flight work via a context, we can wire up esc to this path with bus.Exit()
|
||||||
case "esc", "ctrl+c":
|
case "esc", "ctrl+c":
|
||||||
m.exit()
|
bus.ExitWithInterrupt()
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,12 +142,12 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
log.WithFields("component", "ui").Tracef("event: %q", msg.Type)
|
log.WithFields("component", "ui").Tracef("event: %q", msg.Type)
|
||||||
|
|
||||||
switch msg.Type {
|
switch msg.Type {
|
||||||
case event.CLIReport, event.CLINotification, event.CLIExit, event.CLIAppUpdateAvailable:
|
case event.CLIReport, event.CLINotification, event.CLIAppUpdateAvailable:
|
||||||
// keep these for when the UI is terminated to show to the screen (or perform other events)
|
// keep these for when the UI is terminated to show to the screen (or perform other events)
|
||||||
m.finalizeEvents = append(m.finalizeEvents, msg)
|
m.finalizeEvents = append(m.finalizeEvents, msg)
|
||||||
|
|
||||||
// why not return tea.Quit here for exit events? because there may be UI components that still need the update-render loop.
|
// why not return tea.Quit here for exit events? because there may be UI components that still need the update-render loop.
|
||||||
// for this reason we'll let the syft event loop call Teardown() which will explicitly wait for these components
|
// for this reason we'll let the event loop call Teardown() which will explicitly wait for these components
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,3 +171,17 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
func (m UI) View() string {
|
func (m UI) View() string {
|
||||||
return m.frame.View()
|
return m.frame.View()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runWithTimeout(timeout time.Duration, fn func() error) (err error) {
|
||||||
|
c := make(chan struct{}, 1)
|
||||||
|
go func() {
|
||||||
|
err = fn()
|
||||||
|
c <- struct{}{}
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case <-c:
|
||||||
|
case <-time.After(timeout):
|
||||||
|
return fmt.Errorf("timed out after %v", timeout)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|||||||
@ -1,20 +1,34 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
|
|
||||||
_ "modernc.org/sqlite"
|
_ "modernc.org/sqlite"
|
||||||
|
|
||||||
|
"github.com/anchore/clio"
|
||||||
"github.com/anchore/syft/cmd/syft/cli"
|
"github.com/anchore/syft/cmd/syft/cli"
|
||||||
|
"github.com/anchore/syft/cmd/syft/internal"
|
||||||
|
)
|
||||||
|
|
||||||
|
// applicationName is the non-capitalized name of the application (do not change this)
|
||||||
|
const applicationName = "syft"
|
||||||
|
|
||||||
|
// all variables here are provided as build-time arguments, with clear default values
|
||||||
|
var (
|
||||||
|
version = internal.NotProvided
|
||||||
|
buildDate = internal.NotProvided
|
||||||
|
gitCommit = internal.NotProvided
|
||||||
|
gitDescription = internal.NotProvided
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cli, err := cli.New()
|
app := cli.Application(
|
||||||
if err != nil {
|
clio.Identification{
|
||||||
log.Fatalf("error during command construction: %v", err)
|
Name: applicationName,
|
||||||
}
|
Version: version,
|
||||||
|
BuildDate: buildDate,
|
||||||
|
GitCommit: gitCommit,
|
||||||
|
GitDescription: gitDescription,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
if err := cli.Execute(); err != nil {
|
app.Run()
|
||||||
log.Fatalf("error during command execution: %v", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
151
go.mod
151
go.mod
@ -1,109 +1,121 @@
|
|||||||
module github.com/anchore/syft
|
module github.com/anchore/syft
|
||||||
|
|
||||||
go 1.19
|
go 1.21.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/CycloneDX/cyclonedx-go v0.7.2
|
||||||
|
github.com/Masterminds/semver v1.5.0
|
||||||
|
github.com/Masterminds/sprig/v3 v3.2.3
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||||
github.com/acobaugh/osrelease v0.1.0
|
github.com/acobaugh/osrelease v0.1.0
|
||||||
github.com/adrg/xdg v0.4.0
|
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461
|
||||||
|
github.com/anchore/clio v0.0.0-20230823172630-c42d666061af
|
||||||
|
github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe
|
||||||
|
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a
|
||||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
||||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||||
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501
|
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501
|
||||||
|
github.com/anchore/stereoscope v0.0.0-20230925132944-bf05af58eb44
|
||||||
|
// 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.6.0
|
github.com/bmatcuk/doublestar/v4 v4.6.0
|
||||||
|
github.com/charmbracelet/bubbletea v0.24.2
|
||||||
|
github.com/charmbracelet/lipgloss v0.9.1
|
||||||
|
github.com/dave/jennifer v1.7.0
|
||||||
|
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||||
|
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||||
|
github.com/docker/docker v24.0.6+incompatible
|
||||||
github.com/dustin/go-humanize v1.0.1
|
github.com/dustin/go-humanize v1.0.1
|
||||||
github.com/facebookincubator/nvdtools v0.1.5
|
github.com/facebookincubator/nvdtools v0.1.5
|
||||||
|
github.com/github/go-spdx/v2 v2.2.0
|
||||||
|
github.com/gkampitakis/go-snaps v0.4.11
|
||||||
|
github.com/go-git/go-billy/v5 v5.5.0
|
||||||
|
github.com/go-git/go-git/v5 v5.9.0
|
||||||
github.com/go-test/deep v1.1.0
|
github.com/go-test/deep v1.1.0
|
||||||
github.com/google/go-cmp v0.5.9
|
github.com/google/go-cmp v0.6.0
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/go-containerregistry v0.16.1
|
||||||
|
github.com/google/licensecheck v0.3.1
|
||||||
|
github.com/google/uuid v1.3.1
|
||||||
github.com/gookit/color v1.5.4
|
github.com/gookit/color v1.5.4
|
||||||
github.com/hashicorp/go-multierror v1.1.1
|
github.com/hashicorp/go-multierror v1.1.1
|
||||||
github.com/jinzhu/copier v0.3.5
|
github.com/iancoleman/strcase v0.3.0
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
github.com/invopop/jsonschema v0.7.0
|
||||||
|
github.com/jinzhu/copier v0.4.0
|
||||||
|
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||||
|
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
|
||||||
github.com/mholt/archiver/v3 v3.5.1
|
github.com/mholt/archiver/v3 v3.5.1
|
||||||
github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5
|
github.com/microsoft/go-rustaudit v0.0.0-20220730194248-4b17361d90a5
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
|
github.com/saferwall/pe v1.4.7
|
||||||
|
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
|
||||||
|
github.com/sassoftware/go-rpmutils v0.2.0
|
||||||
// pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5
|
// 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/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
|
||||||
github.com/sergi/go-diff v1.3.1
|
github.com/sergi/go-diff v1.3.1
|
||||||
github.com/sirupsen/logrus v1.9.3
|
|
||||||
github.com/spdx/tools-golang v0.5.3
|
github.com/spdx/tools-golang v0.5.3
|
||||||
github.com/spf13/afero v1.9.5
|
github.com/spf13/afero v1.10.0
|
||||||
github.com/spf13/cobra v1.7.0
|
github.com/spf13/cobra v1.7.0
|
||||||
github.com/spf13/pflag v1.0.5
|
|
||||||
github.com/spf13/viper v1.16.0
|
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
|
github.com/vbatts/go-mtree v0.5.3
|
||||||
github.com/vifraa/gopom v1.0.0
|
github.com/vifraa/gopom v1.0.0
|
||||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
|
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
|
||||||
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5
|
github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0
|
||||||
github.com/xeipuuv/gojsonschema v1.2.0
|
github.com/xeipuuv/gojsonschema v1.2.0
|
||||||
golang.org/x/mod v0.12.0
|
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
|
||||||
golang.org/x/net v0.14.0
|
golang.org/x/mod v0.13.0
|
||||||
golang.org/x/term v0.11.0
|
golang.org/x/net v0.17.0
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
golang.org/x/term v0.13.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
modernc.org/sqlite v1.26.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/CycloneDX/cyclonedx-go v0.7.1
|
github.com/distribution/reference v0.5.0
|
||||||
github.com/Masterminds/semver v1.5.0
|
github.com/sanity-io/litter v1.5.5
|
||||||
github.com/Masterminds/sprig/v3 v3.2.3
|
|
||||||
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461
|
|
||||||
github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0
|
|
||||||
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe
|
|
||||||
github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e
|
|
||||||
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
|
|
||||||
github.com/charmbracelet/bubbletea v0.24.2
|
|
||||||
github.com/charmbracelet/lipgloss v0.7.1
|
|
||||||
github.com/dave/jennifer v1.7.0
|
|
||||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
|
||||||
github.com/docker/distribution v2.8.2+incompatible
|
|
||||||
github.com/docker/docker v24.0.5+incompatible
|
|
||||||
github.com/github/go-spdx/v2 v2.1.2
|
|
||||||
github.com/gkampitakis/go-snaps v0.4.7
|
|
||||||
github.com/go-git/go-billy/v5 v5.4.1
|
|
||||||
github.com/go-git/go-git/v5 v5.8.1
|
|
||||||
github.com/google/go-containerregistry v0.16.1
|
|
||||||
github.com/google/licensecheck v0.3.1
|
|
||||||
github.com/invopop/jsonschema v0.7.0
|
|
||||||
github.com/knqyf263/go-rpmdb v0.0.0-20230301153543-ba94b245509b
|
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
|
||||||
github.com/saferwall/pe v1.4.4
|
|
||||||
github.com/sassoftware/go-rpmutils v0.2.0
|
|
||||||
github.com/vbatts/go-mtree v0.5.3
|
|
||||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
|
|
||||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b
|
|
||||||
gopkg.in/yaml.v3 v3.0.1
|
|
||||||
modernc.org/sqlite v1.25.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 // indirect
|
||||||
|
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 // indirect
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||||
github.com/DataDog/zstd v1.4.5 // indirect
|
github.com/DataDog/zstd v1.4.5 // indirect
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||||
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
github.com/Masterminds/semver/v3 v3.2.0 // indirect
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
github.com/Microsoft/go-winio v0.6.1 // indirect
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 // indirect
|
github.com/Microsoft/hcsshim v0.10.0-rc.7 // indirect
|
||||||
|
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 // indirect
|
||||||
github.com/acomagu/bufpipe v1.0.4 // indirect
|
github.com/acomagu/bufpipe v1.0.4 // indirect
|
||||||
github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba // indirect
|
github.com/adrg/xdg v0.4.0 // indirect
|
||||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
|
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/aquasecurity/go-version v0.0.0-20210121072130-637058cfe492 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/becheran/wildmatch-go v1.0.0 // indirect
|
github.com/becheran/wildmatch-go v1.0.0 // indirect
|
||||||
github.com/charmbracelet/bubbles v0.16.1 // indirect
|
github.com/charmbracelet/bubbles v0.16.1 // indirect
|
||||||
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
github.com/charmbracelet/harmonica v0.2.0 // indirect
|
||||||
github.com/cloudflare/circl v1.3.3 // indirect
|
github.com/cloudflare/circl v1.3.3 // indirect
|
||||||
|
github.com/containerd/cgroups v1.1.0 // indirect
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
|
||||||
github.com/containerd/containerd v1.7.0 // indirect
|
github.com/containerd/containerd v1.7.0 // indirect
|
||||||
|
github.com/containerd/continuity v0.3.0 // indirect
|
||||||
|
github.com/containerd/fifo v1.1.0 // indirect
|
||||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
||||||
|
github.com/containerd/ttrpc v1.2.1 // indirect
|
||||||
|
github.com/containerd/typeurl/v2 v2.1.0 // indirect
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/docker/cli v24.0.0+incompatible // indirect
|
github.com/docker/cli v24.0.0+incompatible // indirect
|
||||||
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
github.com/docker/docker-credential-helpers v0.7.0 // indirect
|
||||||
github.com/docker/go-connections v0.4.0 // indirect
|
github.com/docker/go-connections v0.4.0 // indirect
|
||||||
|
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
|
||||||
github.com/docker/go-units v0.5.0 // indirect
|
github.com/docker/go-units v0.5.0 // indirect
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
|
||||||
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
github.com/edsrzf/mmap-go v1.1.0 // indirect
|
||||||
@ -111,9 +123,11 @@ require (
|
|||||||
github.com/felixge/fgprof v0.9.3 // indirect
|
github.com/felixge/fgprof v0.9.3 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.0 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.0 // indirect
|
||||||
github.com/gkampitakis/ciinfo v0.2.4 // indirect
|
github.com/gkampitakis/ciinfo v0.2.5 // indirect
|
||||||
github.com/gkampitakis/go-diff v1.3.2 // 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-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||||
|
github.com/go-logr/logr v1.2.3 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
github.com/go-restruct/restruct v1.2.0-alpha // indirect
|
github.com/go-restruct/restruct v1.2.0-alpha // indirect
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
github.com/gogo/protobuf v1.3.2 // indirect
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||||
@ -139,34 +153,45 @@ require (
|
|||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
|
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||||
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
|
github.com/moby/locker v1.0.1 // indirect
|
||||||
|
github.com/moby/sys/mountinfo v0.6.2 // indirect
|
||||||
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
|
github.com/moby/sys/signal v0.7.0 // indirect
|
||||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
|
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
|
||||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||||
github.com/muesli/reflow v0.3.0 // indirect
|
github.com/muesli/reflow v0.3.0 // indirect
|
||||||
github.com/muesli/termenv v0.15.1 // indirect
|
github.com/muesli/termenv v0.15.2 // indirect
|
||||||
github.com/nwaples/rardecode v1.1.0 // indirect
|
github.com/nwaples/rardecode v1.1.0 // indirect
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
|
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
|
||||||
|
github.com/opencontainers/runc v1.1.5 // indirect
|
||||||
|
github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect
|
||||||
|
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||||
github.com/pborman/indent v1.2.1 // indirect
|
github.com/pborman/indent v1.2.1 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||||
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
github.com/pierrec/lz4/v4 v4.1.15 // indirect
|
||||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||||
|
github.com/pkg/errors v0.9.1 // indirect
|
||||||
github.com/pkg/profile v1.7.0 // indirect
|
github.com/pkg/profile v1.7.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/rivo/uniseg v0.2.0 // indirect
|
github.com/rivo/uniseg v0.2.0 // indirect
|
||||||
github.com/rogpeppe/go-internal v1.9.0 // indirect
|
github.com/rogpeppe/go-internal v1.11.0 // indirect
|
||||||
github.com/shopspring/decimal v1.2.0 // indirect
|
github.com/shopspring/decimal v1.2.0 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||||
github.com/skeema/knownhosts v1.2.0 // indirect
|
github.com/skeema/knownhosts v1.2.0 // indirect
|
||||||
github.com/spf13/cast v1.5.1 // indirect
|
github.com/spf13/cast v1.5.1 // indirect
|
||||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||||
github.com/stretchr/objx v0.5.0 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
|
github.com/spf13/viper v1.16.0 // indirect
|
||||||
github.com/subosito/gotenv v1.4.2 // indirect
|
github.com/subosito/gotenv v1.4.2 // indirect
|
||||||
github.com/sylabs/sif/v2 v2.11.5 // indirect
|
github.com/sylabs/sif/v2 v2.11.5 // indirect
|
||||||
github.com/sylabs/squashfs v0.6.1 // indirect
|
github.com/sylabs/squashfs v0.6.1 // indirect
|
||||||
github.com/therootcompany/xz v1.0.1 // indirect
|
github.com/therootcompany/xz v1.0.1 // indirect
|
||||||
github.com/tidwall/gjson v1.14.4 // indirect
|
github.com/tidwall/gjson v1.16.0 // indirect
|
||||||
github.com/tidwall/match v1.1.1 // indirect
|
github.com/tidwall/match v1.1.1 // indirect
|
||||||
github.com/tidwall/pretty v1.2.1 // indirect
|
github.com/tidwall/pretty v1.2.1 // indirect
|
||||||
github.com/tidwall/sjson v1.2.5 // indirect
|
github.com/tidwall/sjson v1.2.5 // indirect
|
||||||
@ -178,10 +203,14 @@ require (
|
|||||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||||
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
go.mozilla.org/pkcs7 v0.0.0-20210826202110-33d05740a352 // indirect
|
||||||
golang.org/x/sync v0.2.0 // indirect
|
go.opencensus.io v0.24.0 // indirect
|
||||||
golang.org/x/sys v0.11.0 // indirect
|
go.opentelemetry.io/otel v1.14.0 // indirect
|
||||||
golang.org/x/text v0.12.0 // indirect
|
go.opentelemetry.io/otel/trace v1.14.0 // indirect
|
||||||
golang.org/x/tools v0.9.1 // indirect
|
golang.org/x/crypto v0.14.0 // indirect
|
||||||
|
golang.org/x/sync v0.3.0 // indirect
|
||||||
|
golang.org/x/sys v0.13.0 // indirect
|
||||||
|
golang.org/x/text v0.13.0 // indirect
|
||||||
|
golang.org/x/tools v0.13.0 // indirect
|
||||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||||
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
|
||||||
google.golang.org/grpc v1.55.0 // indirect
|
google.golang.org/grpc v1.55.0 // indirect
|
||||||
@ -199,14 +228,6 @@ require (
|
|||||||
modernc.org/token v1.0.1 // indirect
|
modernc.org/token v1.0.1 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
|
||||||
// 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/andybalholm/brotli v1.0.4 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
golang.org/x/crypto v0.12.0 // indirect
|
|
||||||
)
|
|
||||||
|
|
||||||
retract (
|
retract (
|
||||||
v0.53.2
|
v0.53.2
|
||||||
v0.53.1 // Published accidentally with incorrect license in depdencies
|
v0.53.1 // Published accidentally with incorrect license in depdencies
|
||||||
|
|||||||
244
go.sum
244
go.sum
@ -51,14 +51,18 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f
|
|||||||
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
|
||||||
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||||
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1 h1:EKPd1INOIyr5hWOWhvpmQpY6tKjeG0hT1s3AMC/9fic=
|
||||||
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230106234847-43070de90fa1/go.mod h1:VzwV+t+dZ9j/H867F1M2ziD+yLHtB46oM35FxxMJ4d0=
|
||||||
|
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652 h1:+vTEFqeoeur6XSq06bs+roX3YiT49gUniJK7Zky7Xjg=
|
||||||
|
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20221215162035-5330a85ea652/go.mod h1:OahwfttHWG6eJ0clwcfBAHoDI6X/LV/15hx/wlMZSrU=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
|
||||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
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 v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||||
github.com/CycloneDX/cyclonedx-go v0.7.1 h1:5w1SxjGm9MTMNTuRbEPyw21ObdbaagTWF/KfF0qHTRE=
|
github.com/CycloneDX/cyclonedx-go v0.7.2 h1:kKQ0t1dPOlugSIYVOMiMtFqeXI2wp/f5DBIdfux8gnQ=
|
||||||
github.com/CycloneDX/cyclonedx-go v0.7.1/go.mod h1:N/nrdWQI2SIjaACyyDs/u7+ddCkyl/zkNs8xFsHF2Ps=
|
github.com/CycloneDX/cyclonedx-go v0.7.2/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk=
|
||||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||||
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
|
||||||
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||||
@ -73,9 +77,11 @@ github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBa
|
|||||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||||
|
github.com/Microsoft/hcsshim v0.10.0-rc.7 h1:HBytQPxcv8Oy4244zbQbe6hnOnx544eL5QPUqhJldz8=
|
||||||
|
github.com/Microsoft/hcsshim v0.10.0-rc.7/go.mod h1:ILuwjA+kNW+MrN/w5un7n3mTqkwsFu4Bp05/okFUZlE=
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95 h1:KLq8BE0KwCL+mmXnjLWEAOYO+2l2AE4YMmqG1ZpZHBs=
|
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371 h1:kkhsdkhsCvIsutKu5zLMgWtgh9YxGCNAw8Ad8hjwfYg=
|
||||||
github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
github.com/ProtonMail/go-crypto v0.0.0-20230828082145-3c4c8a2d2371/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
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/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 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
|
||||||
@ -90,12 +96,12 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
|
|||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||||
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 h1:xGu4/uMWucwWV0YV3fpFIQZ6KVfS/Wfhmma8t0s0vRo=
|
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461 h1:xGu4/uMWucwWV0YV3fpFIQZ6KVfS/Wfhmma8t0s0vRo=
|
||||||
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461/go.mod h1:Ger02eh5NpPm2IqkPAy396HU1KlK3BhOeCljDYXySSk=
|
github.com/anchore/bubbly v0.0.0-20230801194016-acdb4981b461/go.mod h1:Ger02eh5NpPm2IqkPAy396HU1KlK3BhOeCljDYXySSk=
|
||||||
github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0 h1:g0UqRW60JDrf5fb40RUyIwwcfQ3nAJqGj4aUCVTwFE4=
|
github.com/anchore/clio v0.0.0-20230823172630-c42d666061af h1:dBVKZyMZeA0oZK0+aCCRoqxhxUvx/7xy/VEaLMMMnb0=
|
||||||
github.com/anchore/clio v0.0.0-20230602170917-e747e60c4aa0/go.mod h1:0IQVIROfgRX4WZFMfgsbNZmMgLKqW/KgByyJDYvWiDE=
|
github.com/anchore/clio v0.0.0-20230823172630-c42d666061af/go.mod h1:XryJ3CIF1T7SbacQV+OPykfKKIbfXnBssYfpjy2peUg=
|
||||||
github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba h1:tJ186HK8e0Lf+hhNWX4fJrq14yj3mw8JQkkLhA0nFhE=
|
github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe h1:pVpLCGWdNeskAw7vGNdCAcGMezrNljHIqOc9HaOja5M=
|
||||||
github.com/anchore/fangs v0.0.0-20230531202914-48a718c6b4ba/go.mod h1:E3zNHEz7mizIFGJhuX+Ga7AbCmEN5TfzVDxmOfj7XZw=
|
github.com/anchore/fangs v0.0.0-20230818131516-2186b10924fe/go.mod h1:82EGoxZTfBXSW0/zollEP+Qs3wkiKmip5yBT5j+eZpY=
|
||||||
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe h1:Df867YMmymdMG6z5IW8pR0/2CRpLIjYnaTXLp6j+s0k=
|
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a h1:nJ2G8zWKASyVClGVgG7sfM5mwoZlZ2zYpIzN2OhjWkw=
|
||||||
github.com/anchore/go-logger v0.0.0-20230531193951-db5ae83e7dbe/go.mod h1:ubLFmlsv8/DFUQrZwY5syT5/8Er3ugSr4rDFwHsE3hg=
|
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a/go.mod h1:ubLFmlsv8/DFUQrZwY5syT5/8Er3ugSr4rDFwHsE3hg=
|
||||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU=
|
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb h1:iDMnx6LIjtjZ46C0akqveX83WFzhpTD3eqOthawb5vU=
|
||||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb/go.mod h1:DmTY2Mfcv38hsHbG78xMiTDdxFtkHpgYNVDPsF2TgHk=
|
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb/go.mod h1:DmTY2Mfcv38hsHbG78xMiTDdxFtkHpgYNVDPsF2TgHk=
|
||||||
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
|
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
|
||||||
@ -106,13 +112,14 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
|||||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||||
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8=
|
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8=
|
||||||
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
|
github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
|
||||||
github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e h1:S6IhYpsBCpvphlHA1tN0glSG/kjVvFzC6OJuU2qW5Pc=
|
github.com/anchore/stereoscope v0.0.0-20230925132944-bf05af58eb44 h1:dKMvcpgqsRrX1ZWyqG53faVW+BahlaAO1RUEc7/rOjA=
|
||||||
github.com/anchore/stereoscope v0.0.0-20230727211946-d1f3d766295e/go.mod h1:0LsgHgXO4QFnk2hsYwtqd3fR18PIZXlFLIl2qb9tu3g=
|
github.com/anchore/stereoscope v0.0.0-20230925132944-bf05af58eb44/go.mod h1:RtbeDCho0pxkPqrB1QNf/Jlxfc9juLmtYZAf2UbpJfk=
|
||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
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.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
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.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
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/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 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-pep440-version v0.0.0-20210121094942-22b2f8951d46/go.mod h1:olhPNdiiAAMiSujemd1O/sc6GcyePr23f/6uGKtthNg=
|
||||||
@ -124,6 +131,7 @@ github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb
|
|||||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
|
github.com/becheran/wildmatch-go v1.0.0 h1:mE3dGGkTmpKtT4Z+88t8RStG40yN9T+kFEGj2PZFSzA=
|
||||||
@ -135,6 +143,7 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
|
|||||||
github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc=
|
github.com/bmatcuk/doublestar/v4 v4.6.0 h1:HTuxyug8GyFbRkrffIpzNCSK4luc0TY3wzXvzIZhEXc=
|
||||||
github.com/bmatcuk/doublestar/v4 v4.6.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
github.com/bmatcuk/doublestar/v4 v4.6.0/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 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
|
||||||
|
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||||
@ -147,11 +156,13 @@ github.com/charmbracelet/bubbletea v0.24.2 h1:uaQIKx9Ai6Gdh5zpTbGiWpytMU+CfsPp06
|
|||||||
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
|
github.com/charmbracelet/bubbletea v0.24.2/go.mod h1:XdrNrV4J8GiyshTtx3DNuYkR1FDaJmO3l2nejekbsgg=
|
||||||
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
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/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||||
github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
|
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg=
|
||||||
github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
|
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I=
|
||||||
|
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||||
|
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
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/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
@ -167,36 +178,56 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
|
|||||||
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
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/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY=
|
||||||
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
|
||||||
github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg=
|
github.com/containerd/containerd v1.7.0 h1:G/ZQr3gMZs6ZT0qPUZ15znx5QSdQdASW11nXTLTM2Pg=
|
||||||
github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc=
|
github.com/containerd/containerd v1.7.0/go.mod h1:QfR7Efgb/6X2BDpTPJRvPTYDE9rsF0FsXX9J8sIs/sc=
|
||||||
|
github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg=
|
||||||
|
github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
|
||||||
|
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
|
||||||
|
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
|
||||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
|
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.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
|
||||||
|
github.com/containerd/ttrpc v1.2.1 h1:VWv/Rzx023TBLv4WQ+9WPXlBG/s3rsRjY3i9AJ2BJdE=
|
||||||
|
github.com/containerd/ttrpc v1.2.1/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak=
|
||||||
|
github.com/containerd/typeurl/v2 v2.1.0 h1:yNAhJvbNEANt7ck48IlEGOxP7YAp6LLpGn5jZACDNIE=
|
||||||
|
github.com/containerd/typeurl/v2 v2.1.0/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
|
||||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
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/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.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.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.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
|
||||||
|
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||||
github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE=
|
github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE=
|
||||||
github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||||
|
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
|
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
|
||||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
|
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
|
||||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||||
|
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
|
||||||
|
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
|
||||||
github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM=
|
github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM=
|
||||||
github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||||
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
|
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||||
github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||||
github.com/docker/docker v24.0.5+incompatible h1:WmgcE4fxyI6EEXxBRxsHnZXrO1pQ3smi0k/jho4HLeY=
|
github.com/docker/docker v24.0.6+incompatible h1:hceabKCtUgDqPu+qm0NgsaXf28Ljf4/pWFL7xjWWDgE=
|
||||||
github.com/docker/docker v24.0.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
github.com/docker/docker v24.0.6+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 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
|
||||||
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
|
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 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||||
|
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.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
|
||||||
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||||
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
|
||||||
@ -206,7 +237,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/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 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ=
|
||||||
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
|
||||||
github.com/elazarl/goproxy v0.0.0-20221015165544-a0805db90819 h1:RIB4cRk+lBqKK3Oy0r2gRX4ui7tuhiZq2SuTtTCi0/0=
|
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/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
@ -230,30 +262,35 @@ 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/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 h1:VvyZxILNuCiUCSXtPtYmmtGvb65nqXh2QFWc0Wpf2/g=
|
||||||
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw=
|
||||||
|
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
|
||||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||||
|
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
|
||||||
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
|
||||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro=
|
github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
|
github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/github/go-spdx/v2 v2.1.2 h1:p+Tv0yMgcuO0/vnMe9Qh4tmUgYhI6AsLVlakZ/Sx+DM=
|
github.com/github/go-spdx/v2 v2.2.0 h1:yBBLMasHA70Ujd35OpL/OjJOWWVNXcJGbars0GinGRI=
|
||||||
github.com/github/go-spdx/v2 v2.1.2/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w=
|
github.com/github/go-spdx/v2 v2.2.0/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w=
|
||||||
github.com/gkampitakis/ciinfo v0.2.4 h1:Ip1hf4K7ISRuVlDrheuhaeffg1VOhlyeFGaQ/vTxrtE=
|
github.com/gkampitakis/ciinfo v0.2.5 h1:K0mac90lGguc1conc46l0YEsB7/nioWCqSnJp/6z8Eo=
|
||||||
github.com/gkampitakis/ciinfo v0.2.4/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
github.com/gkampitakis/ciinfo v0.2.5/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||||
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
|
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-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
|
||||||
github.com/gkampitakis/go-snaps v0.4.7 h1:w1hOUdiKELxiTcRySQLmLuV3KW40mIRw3rzcYJGrb3I=
|
github.com/gkampitakis/go-snaps v0.4.11 h1:7qKaozbTQEvHeG0bt6osdjdTDTnWYdIrLx43a7DEDu4=
|
||||||
github.com/gkampitakis/go-snaps v0.4.7/go.mod h1:8HW4KX3JKV8M0GSw69CvT+Jqhd1AlBPMPpBfjBI3bdY=
|
github.com/gkampitakis/go-snaps v0.4.11/go.mod h1:N4TpqxI4CqKUfHzDFqrqZ5UP0I0ESz2g2NMslh7MiJw=
|
||||||
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
|
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.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
github.com/gliderlabs/ssh v0.3.5 h1:OcaySEmAQJgyYcArR+gGGTHCyE7nvhEMTlYY+Dp8CpY=
|
||||||
|
github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
|
||||||
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 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||||
github.com/go-git/go-billy/v5 v5.4.1 h1:Uwp5tDRkPr+l/TnbHOQzp+tmJfLceOlbVucgpTz8ix4=
|
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||||
github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
|
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f h1:Pz0DHeFij3XFhoBRGUDPzSJ+w2UcK5/0JvF8DRI58r8=
|
||||||
github.com/go-git/go-git/v5 v5.8.1 h1:Zo79E4p7TRk0xoRgMq0RShiTHGKcKI4+DI6BfJc/Q+A=
|
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20230305113008-0c11038e723f/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
|
||||||
github.com/go-git/go-git/v5 v5.8.1/go.mod h1:FHFuoD6yGz5OSKEBK+aWN9Oah0q54Jxl0abmj6GnqAo=
|
github.com/go-git/go-git/v5 v5.9.0 h1:cD9SFA7sHVRdJ7AYck1ZaAa/yeuBvGPxwXDL8cxrObY=
|
||||||
|
github.com/go-git/go-git/v5 v5.9.0/go.mod h1:RKIqga24sWdMGZF+1Ekv9kylsDz6LzdTSI2s/OsZWE0=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||||
@ -261,6 +298,11 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
|||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
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.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||||
|
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
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=
|
github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSrdV7Nv3/gkvc=
|
||||||
github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
|
github.com/go-restruct/restruct v1.2.0-alpha/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
@ -268,6 +310,7 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
|
|||||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||||
@ -323,8 +366,9 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
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.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ=
|
github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ=
|
||||||
github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
|
github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
@ -355,8 +399,8 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S3
|
|||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||||
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
|
||||||
@ -406,6 +450,8 @@ github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq
|
|||||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk=
|
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 h1:i462o439ZjprVSFSZLZxcsoAe592sZB1rci2Z8j4wdk=
|
||||||
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
|
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0/go.mod h1:N0Wam8K1arqPXNWjMo21EXnBPOPp36vB07FNRdD2geA=
|
||||||
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
|
github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
|
||||||
|
github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
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-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-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||||
@ -419,8 +465,8 @@ 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/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 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jinzhu/copier v0.3.5 h1:GlvfUwHk62RokgqVNvYsku0TATCF7bAHVwEXoBh3iJg=
|
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||||
github.com/jinzhu/copier v0.3.5/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
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.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||||
@ -428,6 +474,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
|
|||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
|
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953 h1:WdAeg/imY2JFPc/9CST4bZ80nNJbiBFCAdSZCSgrS5Y=
|
||||||
|
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953/go.mod h1:6o+UrvuZWc4UTyBhQf0LGjW9Ld7qJxLz/OqvSOWWlEc=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||||
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
|
||||||
@ -486,9 +534,10 @@ github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75 h1:P8UmIzZ
|
|||||||
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
github.com/mattn/go-localereader v0.0.2-0.20220822084749-2491eb6c1c75/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
|
||||||
|
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||||
@ -516,31 +565,52 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
|
|||||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
|
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||||
|
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||||
|
github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
|
||||||
|
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
|
||||||
|
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||||
|
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||||
|
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||||
|
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
|
||||||
|
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
|
||||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
|
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
|
||||||
|
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
|
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||||
|
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA=
|
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 h1:kMlmsLSbjkikxQJ1IPwaM+7LJ9ltFu/fi8CRzvSnQmA=
|
||||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
||||||
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||||
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
|
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
|
||||||
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
|
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
|
||||||
github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7MQ=
|
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.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
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/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||||
|
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||||
|
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
|
github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8=
|
||||||
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
|
github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
|
||||||
|
github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs=
|
||||||
|
github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
|
||||||
|
github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
|
github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w=
|
||||||
|
github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||||
|
github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
|
||||||
|
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
||||||
|
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
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/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||||
github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
|
github.com/pborman/indent v1.2.1 h1:lFiviAbISHv3Rf0jcuh489bi06hj98JsVMtIDZQb9yM=
|
||||||
@ -564,6 +634,7 @@ github.com/pkg/profile v1.7.0 h1:hnbDkaNWPCLMO9wGLdBFTIZvzDrDfBM2072E1S9gJkA=
|
|||||||
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo=
|
||||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||||
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||||
|
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||||
@ -580,6 +651,9 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
|
|||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||||
|
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||||
|
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||||
|
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
@ -588,20 +662,27 @@ github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
|||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
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.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||||
github.com/saferwall/pe v1.4.4 h1:Ml++7/2/Z1iKwV4zCsd1nIqTEAdUQKAetwbbcCarhOg=
|
github.com/saferwall/pe v1.4.7 h1:A+G3DxX49paJ5OsxBfHKskhyDtmTjShlDmBd81IsHlQ=
|
||||||
github.com/saferwall/pe v1.4.4/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s=
|
github.com/saferwall/pe v1.4.7/go.mod h1:SNzv3cdgk8SBI0UwHfyTcdjawfdnN+nbydnEL7GZ25s=
|
||||||
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
|
||||||
|
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/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE=
|
github.com/sassoftware/go-rpmutils v0.2.0 h1:pKW0HDYMFWQ5b4JQPiI3WI12hGsVoW0V8+GMoZiI/JE=
|
||||||
github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI=
|
github.com/sassoftware/go-rpmutils v0.2.0/go.mod h1:TJJQYtLe/BeEmEjelI3b7xNZjzAukEkeWKmoakvaOoI=
|
||||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
|
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
|
||||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
|
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
|
||||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||||
github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
|
github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
|
||||||
|
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||||
|
github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||||
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
|
||||||
@ -612,6 +693,7 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
|||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
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.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
|
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
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 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||||
@ -623,8 +705,8 @@ github.com/spdx/tools-golang v0.5.3 h1:ialnHeEYUC4+hkm5vJm4qz2x+oEJbS0mAMFrNXdQr
|
|||||||
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
|
github.com/spdx/tools-golang v0.5.3/go.mod h1:/ETOahiAo96Ob0/RAIBmFZw6XN0yTnyr/uFZm2NTMhI=
|
||||||
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
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.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY=
|
||||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||||
@ -644,6 +726,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
@ -664,11 +747,14 @@ github.com/sylabs/sif/v2 v2.11.5 h1:7ssPH3epSonsTrzbS1YxeJ9KuqAN7ISlSM61a7j/mQM=
|
|||||||
github.com/sylabs/sif/v2 v2.11.5/go.mod h1:GBoZs9LU3e4yJH1dcZ3Akf/jsqYgy5SeguJQC+zd75Y=
|
github.com/sylabs/sif/v2 v2.11.5/go.mod h1:GBoZs9LU3e4yJH1dcZ3Akf/jsqYgy5SeguJQC+zd75Y=
|
||||||
github.com/sylabs/squashfs v0.6.1 h1:4hgvHnD9JGlYWwT0bPYNt9zaz23mAV3Js+VEgQoRGYQ=
|
github.com/sylabs/squashfs v0.6.1 h1:4hgvHnD9JGlYWwT0bPYNt9zaz23mAV3Js+VEgQoRGYQ=
|
||||||
github.com/sylabs/squashfs v0.6.1/go.mod h1:ZwpbPCj0ocIvMy2br6KZmix6Gzh6fsGQcCnydMF+Kx8=
|
github.com/sylabs/squashfs v0.6.1/go.mod h1:ZwpbPCj0ocIvMy2br6KZmix6Gzh6fsGQcCnydMF+Kx8=
|
||||||
|
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||||
|
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/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
github.com/therootcompany/xz v1.0.1 h1:CmOtsn1CbtmyYiusbfmhmkpAAETj0wBIH6kCYaX+xzw=
|
||||||
github.com/therootcompany/xz v1.0.1/go.mod h1:3K3UH1yCKgBneZYhuQUvJ9HPD19UEXEI0BWbMn8qNMY=
|
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.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
|
github.com/tidwall/gjson v1.16.0 h1:SyXa+dsSPpUlcwEDuKuEBJEz5vzTvOea+9rjyYodQFg=
|
||||||
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
github.com/tidwall/gjson v1.16.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
@ -681,6 +767,7 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
|
|||||||
github.com/ulikunitz/xz v0.5.9/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.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
github.com/ulikunitz/xz v0.5.10 h1:t92gobL9l3HE202wg3rlk19F6X+JOxl9BBrCCMYEYd8=
|
||||||
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
github.com/ulikunitz/xz v0.5.10/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
|
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
|
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/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
|
||||||
github.com/vbatts/go-mtree v0.5.3 h1:S/jYlfG8rZ+a0bhZd+RANXejy7M4Js8fq9U+XoWTd5w=
|
github.com/vbatts/go-mtree v0.5.3 h1:S/jYlfG8rZ+a0bhZd+RANXejy7M4Js8fq9U+XoWTd5w=
|
||||||
@ -689,10 +776,12 @@ github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RV
|
|||||||
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
|
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
|
||||||
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
|
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
|
||||||
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
|
github.com/vifraa/gopom v1.0.0/go.mod h1:oPa1dcrGrtlO37WPDBm5SqHAT+wTgF8An1Q71Z6Vv4o=
|
||||||
|
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
|
||||||
|
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
|
||||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
|
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIqV3d+DOxazTR9v+zgj8+VYuQBzPgBZvWBHA=
|
||||||
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20=
|
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20=
|
||||||
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 h1:lwgTsTy18nYqASnH58qyfRW/ldj7Gt2zzBvgYPzdA4s=
|
github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0 h1:0KGbf+0SMg+UFy4e1A/CPVvXn21f1qtWdeJwxZFoQG8=
|
||||||
github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
|
github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA=
|
||||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||||
@ -726,10 +815,17 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
|||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
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.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/otel v1.14.0 h1:/79Huy8wbf5DnIPhemGB+zEPVwnN6fuQybr/SRXa6hM=
|
||||||
|
go.opentelemetry.io/otel v1.14.0/go.mod h1:o4buv+dJzx8rohcUeRmWUZhqupFvzWis188WlggnNeU=
|
||||||
|
go.opentelemetry.io/otel/trace v1.14.0 h1:wp2Mmvj41tDsyAJXiWDWpfNsOiIyd38fy85pyKcFq/M=
|
||||||
|
go.opentelemetry.io/otel/trace v1.14.0/go.mod h1:8avnQLK+CG77yNLUae4ea2JDQ6iT+gozhnZjy/rw9G8=
|
||||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0=
|
|
||||||
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
|
||||||
|
go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA=
|
||||||
|
go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
@ -750,8 +846,8 @@ golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0
|
|||||||
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
|
||||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||||
@ -762,8 +858,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
|||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b h1:EqBVA+nNsObCwQoBEHy4wLU0pi7i8a4AL3pbItPdPkE=
|
|
||||||
golang.org/x/exp v0.0.0-20230202163644-54bba9f4231b/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
|
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||||
@ -777,7 +871,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
|
|||||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
|
|
||||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||||
@ -793,8 +886,8 @@ 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.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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY=
|
||||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -843,8 +936,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
|
|||||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
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-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
@ -875,8 +968,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
|||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
|
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||||
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
@ -890,6 +983,7 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -897,6 +991,7 @@ golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
@ -940,10 +1035,12 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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-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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
@ -958,16 +1055,16 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|||||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
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.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
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.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
@ -980,12 +1077,13 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|||||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
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-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.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
|
golang.org/x/time v0.1.0 h1:xYY+Bajn2a7VBmTM5GikTmnK8ZuX8YgnQCqZpbBNtmA=
|
||||||
|
golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||||
@ -1044,8 +1142,8 @@ 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.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||||
golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
|
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||||
golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
|
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -1222,13 +1320,13 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
|
||||||
|
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
@ -1243,7 +1341,9 @@ modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
|
|||||||
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
|
||||||
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
|
||||||
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk=
|
||||||
|
modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
|
||||||
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
|
||||||
|
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
|
||||||
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
|
modernc.org/libc v1.24.1 h1:uvJSeCKL/AgzBo2yYIPPTy82v21KgGnizcGYfBHaNuM=
|
||||||
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
|
modernc.org/libc v1.24.1/go.mod h1:FmfO1RLrU3MHJfyi9eYYmZBfi/R+tqZ6+hQ3yQQUkak=
|
||||||
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
|
||||||
@ -1252,14 +1352,16 @@ modernc.org/memory v1.6.0 h1:i6mzavxrE9a30whzMfwf7XWVODx2r5OYXvU46cirX7o=
|
|||||||
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
modernc.org/memory v1.6.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
|
||||||
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
|
||||||
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
|
||||||
modernc.org/sqlite v1.25.0 h1:AFweiwPNd/b3BoKnBOfFm+Y260guGMF+0UFk0savqeA=
|
modernc.org/sqlite v1.26.0 h1:SocQdLRSYlA8W99V8YH0NES75thx19d9sB/aFc4R8Lw=
|
||||||
modernc.org/sqlite v1.25.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
|
modernc.org/sqlite v1.26.0/go.mod h1:FL3pVXie73rg3Rii6V/u5BoHlSoyeZeIgKZEgHARyCU=
|
||||||
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
|
||||||
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
|
||||||
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
|
||||||
|
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
|
||||||
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
|
||||||
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
|
||||||
|
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
||||||
|
|||||||
@ -3,21 +3,24 @@ package bus
|
|||||||
import (
|
import (
|
||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/log"
|
"github.com/anchore/clio"
|
||||||
|
"github.com/anchore/syft/internal/redact"
|
||||||
"github.com/anchore/syft/syft/event"
|
"github.com/anchore/syft/syft/event"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Exit() {
|
func Exit() {
|
||||||
Publish(partybus.Event{
|
Publish(clio.ExitEvent(false))
|
||||||
Type: event.CLIExit,
|
}
|
||||||
})
|
|
||||||
|
func ExitWithInterrupt() {
|
||||||
|
Publish(clio.ExitEvent(true))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Report(report string) {
|
func Report(report string) {
|
||||||
if len(report) == 0 {
|
if len(report) == 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
report = log.Redactor.RedactString(report)
|
report = redact.Apply(report)
|
||||||
Publish(partybus.Event{
|
Publish(partybus.Event{
|
||||||
Type: event.CLIReport,
|
Type: event.CLIReport,
|
||||||
Value: report,
|
Value: report,
|
||||||
|
|||||||
@ -1,337 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"reflect"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/adrg/xdg"
|
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
|
|
||||||
"github.com/anchore/go-logger"
|
|
||||||
"github.com/anchore/syft/internal"
|
|
||||||
"github.com/anchore/syft/internal/log"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger"
|
|
||||||
golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang"
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
|
|
||||||
pythonCataloger "github.com/anchore/syft/syft/pkg/cataloger/python"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ErrApplicationConfigNotFound = fmt.Errorf("application config not found")
|
|
||||||
catalogerEnabledDefault = false
|
|
||||||
)
|
|
||||||
|
|
||||||
type defaultValueLoader interface {
|
|
||||||
loadDefaultValues(*viper.Viper)
|
|
||||||
}
|
|
||||||
|
|
||||||
type parser interface {
|
|
||||||
parseConfigValues() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Application is the main syft application configuration.
|
|
||||||
type Application struct {
|
|
||||||
// the location where the application config was read from (either from -c or discovered while loading); default .syft.yaml
|
|
||||||
ConfigPath string `yaml:"configPath,omitempty" json:"configPath" mapstructure:"config"`
|
|
||||||
Verbosity uint `yaml:"verbosity,omitempty" json:"verbosity" mapstructure:"verbosity"`
|
|
||||||
// -q, indicates to not show any status output to stderr (ETUI or logging UI)
|
|
||||||
Quiet bool `yaml:"quiet" json:"quiet" mapstructure:"quiet"`
|
|
||||||
Outputs []string `yaml:"output" json:"output" mapstructure:"output"` // -o, the format to use for output
|
|
||||||
OutputTemplatePath string `yaml:"output-template-path" json:"output-template-path" mapstructure:"output-template-path"` // -t template file to use for output
|
|
||||||
File string `yaml:"file" json:"file" mapstructure:"file"` // --file, the file to write report output to
|
|
||||||
CheckForAppUpdate bool `yaml:"check-for-app-update" json:"check-for-app-update" mapstructure:"check-for-app-update"` // whether to check for an application update on start up or not
|
|
||||||
Dev development `yaml:"dev" json:"dev" mapstructure:"dev"`
|
|
||||||
Log logging `yaml:"log" json:"log" mapstructure:"log"` // all logging-related options
|
|
||||||
Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"`
|
|
||||||
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
|
|
||||||
Golang golang `yaml:"golang" json:"golang" mapstructure:"golang"`
|
|
||||||
LinuxKernel linuxKernel `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"`
|
|
||||||
Python python `yaml:"python" json:"python" mapstructure:"python"`
|
|
||||||
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
|
|
||||||
FileMetadata FileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
|
|
||||||
FileClassification fileClassification `yaml:"file-classification" json:"file-classification" mapstructure:"file-classification"`
|
|
||||||
FileContents fileContents `yaml:"file-contents" json:"file-contents" mapstructure:"file-contents"`
|
|
||||||
Secrets secrets `yaml:"secrets" json:"secrets" mapstructure:"secrets"`
|
|
||||||
Registry registry `yaml:"registry" json:"registry" mapstructure:"registry"`
|
|
||||||
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
|
|
||||||
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
|
|
||||||
Name string `yaml:"name" json:"name" mapstructure:"name"`
|
|
||||||
Source sourceCfg `yaml:"source" json:"source" mapstructure:"source"`
|
|
||||||
Parallelism int `yaml:"parallelism" json:"parallelism" mapstructure:"parallelism"` // the number of catalog workers to run in parallel
|
|
||||||
DefaultImagePullSource string `yaml:"default-image-pull-source" json:"default-image-pull-source" mapstructure:"default-image-pull-source"` // specify default image pull source
|
|
||||||
BasePath string `yaml:"base-path" json:"base-path" mapstructure:"base-path"` // specify base path for all file paths
|
|
||||||
ExcludeBinaryOverlapByOwnership bool `yaml:"exclude-binary-overlap-by-ownership" json:"exclude-binary-overlap-by-ownership" mapstructure:"exclude-binary-overlap-by-ownership"` // exclude synthetic binary packages owned by os package files
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg Application) ToCatalogerConfig() cataloger.Config {
|
|
||||||
return cataloger.Config{
|
|
||||||
Search: cataloger.SearchConfig{
|
|
||||||
IncludeIndexedArchives: cfg.Package.SearchIndexedArchives,
|
|
||||||
IncludeUnindexedArchives: cfg.Package.SearchUnindexedArchives,
|
|
||||||
Scope: cfg.Package.Cataloger.ScopeOpt,
|
|
||||||
},
|
|
||||||
Catalogers: cfg.Catalogers,
|
|
||||||
Parallelism: cfg.Parallelism,
|
|
||||||
ExcludeBinaryOverlapByOwnership: cfg.ExcludeBinaryOverlapByOwnership,
|
|
||||||
Golang: golangCataloger.NewGoCatalogerOpts().
|
|
||||||
WithSearchLocalModCacheLicenses(cfg.Golang.SearchLocalModCacheLicenses).
|
|
||||||
WithLocalModCacheDir(cfg.Golang.LocalModCacheDir).
|
|
||||||
WithSearchRemoteLicenses(cfg.Golang.SearchRemoteLicenses).
|
|
||||||
WithProxy(cfg.Golang.Proxy).
|
|
||||||
WithNoProxy(cfg.Golang.NoProxy),
|
|
||||||
LinuxKernel: kernel.LinuxCatalogerConfig{
|
|
||||||
CatalogModules: cfg.LinuxKernel.CatalogModules,
|
|
||||||
},
|
|
||||||
Python: pythonCataloger.CatalogerConfig{
|
|
||||||
GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *Application) LoadAllValues(v *viper.Viper, configPath string) error {
|
|
||||||
// priority order: viper.Set, flag, env, config, kv, defaults
|
|
||||||
// flags have already been loaded into viper by command construction
|
|
||||||
|
|
||||||
// check if user specified config; otherwise read all possible paths
|
|
||||||
if err := loadConfig(v, configPath); err != nil {
|
|
||||||
var notFound *viper.ConfigFileNotFoundError
|
|
||||||
if errors.As(err, ¬Found) {
|
|
||||||
log.Debugf("no config file found, using defaults")
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("unable to load config: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// load default config values into viper
|
|
||||||
loadDefaultValues(v)
|
|
||||||
|
|
||||||
// load environment variables
|
|
||||||
v.SetEnvPrefix(internal.ApplicationName)
|
|
||||||
v.AllowEmptyEnv(true)
|
|
||||||
v.AutomaticEnv()
|
|
||||||
|
|
||||||
// unmarshal fully populated viper object onto config
|
|
||||||
err := v.Unmarshal(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert all populated config options to their internal application values ex: scope string => scopeOpt source.Scope
|
|
||||||
return cfg.parseConfigValues()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *Application) parseConfigValues() error {
|
|
||||||
// parse options on this struct
|
|
||||||
var catalogers []string
|
|
||||||
for _, c := range cfg.Catalogers {
|
|
||||||
for _, f := range strings.Split(c, ",") {
|
|
||||||
catalogers = append(catalogers, strings.TrimSpace(f))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Strings(catalogers)
|
|
||||||
cfg.Catalogers = catalogers
|
|
||||||
|
|
||||||
// parse application config options
|
|
||||||
for _, optionFn := range []func() error{
|
|
||||||
cfg.parseLogLevelOption,
|
|
||||||
cfg.parseFile,
|
|
||||||
} {
|
|
||||||
if err := optionFn(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := checkDefaultSourceValues(cfg.DefaultImagePullSource); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Name != "" {
|
|
||||||
log.Warnf("name parameter is deprecated. please use: source-name. name will be removed in a future version")
|
|
||||||
if cfg.Source.Name == "" {
|
|
||||||
cfg.Source.Name = cfg.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for valid default source options
|
|
||||||
// parse nested config options
|
|
||||||
// for each field in the configuration struct, see if the field implements the parser interface
|
|
||||||
// note: the app config is a pointer, so we need to grab the elements explicitly (to traverse the address)
|
|
||||||
value := reflect.ValueOf(cfg).Elem()
|
|
||||||
for i := 0; i < value.NumField(); i++ {
|
|
||||||
// note: since the interface method of parser is a pointer receiver we need to get the value of the field as a pointer.
|
|
||||||
if parsable, ok := value.Field(i).Addr().Interface().(parser); ok {
|
|
||||||
// the field implements parser, call it
|
|
||||||
if err := parsable.parseConfigValues(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *Application) parseLogLevelOption() error {
|
|
||||||
switch {
|
|
||||||
case cfg.Quiet:
|
|
||||||
// TODO: this is bad: quiet option trumps all other logging options (such as to a file on disk)
|
|
||||||
// we should be able to quiet the console logging and leave file logging alone...
|
|
||||||
// ... this will be an enhancement for later
|
|
||||||
cfg.Log.Level = logger.DisabledLevel
|
|
||||||
|
|
||||||
case cfg.Verbosity > 0:
|
|
||||||
cfg.Log.Level = logger.LevelFromVerbosity(int(cfg.Verbosity), logger.WarnLevel, logger.InfoLevel, logger.DebugLevel, logger.TraceLevel)
|
|
||||||
|
|
||||||
case cfg.Log.Level != "":
|
|
||||||
var err error
|
|
||||||
cfg.Log.Level, err = logger.LevelFromString(string(cfg.Log.Level))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if logger.IsVerbose(cfg.Log.Level) {
|
|
||||||
cfg.Verbosity = 1
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
cfg.Log.Level = logger.WarnLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *Application) parseFile() error {
|
|
||||||
if cfg.File != "" {
|
|
||||||
expandedPath, err := homedir.Expand(cfg.File)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to expand file path=%q: %w", cfg.File, err)
|
|
||||||
}
|
|
||||||
cfg.File = expandedPath
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// init loads the default configuration values into the viper instance (before the config values are read and parsed).
|
|
||||||
func loadDefaultValues(v *viper.Viper) {
|
|
||||||
// set the default values for primitive fields in this struct
|
|
||||||
v.SetDefault("quiet", false)
|
|
||||||
v.SetDefault("check-for-app-update", true)
|
|
||||||
v.SetDefault("catalogers", nil)
|
|
||||||
v.SetDefault("parallelism", 1)
|
|
||||||
v.SetDefault("default-image-pull-source", "")
|
|
||||||
v.SetDefault("exclude-binary-overlap-by-ownership", true)
|
|
||||||
|
|
||||||
// for each field in the configuration struct, see if the field implements the defaultValueLoader interface and invoke it if it does
|
|
||||||
value := reflect.ValueOf(Application{})
|
|
||||||
for i := 0; i < value.NumField(); i++ {
|
|
||||||
// note: the defaultValueLoader method receiver is NOT a pointer receiver.
|
|
||||||
if loadable, ok := value.Field(i).Interface().(defaultValueLoader); ok {
|
|
||||||
// the field implements defaultValueLoader, call it
|
|
||||||
loadable.loadDefaultValues(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg Application) String() string {
|
|
||||||
// yaml is pretty human friendly (at least when compared to json)
|
|
||||||
appaStr, err := yaml.Marshal(&cfg)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(appaStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// nolint:funlen
|
|
||||||
func loadConfig(v *viper.Viper, configPath string) error {
|
|
||||||
var err error
|
|
||||||
// use explicitly the given user config
|
|
||||||
if configPath != "" {
|
|
||||||
v.SetConfigFile(configPath)
|
|
||||||
if err := v.ReadInConfig(); err != nil {
|
|
||||||
return fmt.Errorf("unable to read application config=%q : %w", configPath, err)
|
|
||||||
}
|
|
||||||
v.Set("config", v.ConfigFileUsed())
|
|
||||||
// don't fall through to other options if the config path was explicitly provided
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// start searching for valid configs in order...
|
|
||||||
// 1. look for .<appname>.yaml (in the current directory)
|
|
||||||
confFilePath := "." + internal.ApplicationName
|
|
||||||
|
|
||||||
// TODO: Remove this before v1.0.0
|
|
||||||
// See syft #1634
|
|
||||||
v.AddConfigPath(".")
|
|
||||||
v.SetConfigName(confFilePath)
|
|
||||||
|
|
||||||
// check if config.yaml exists in the current directory
|
|
||||||
// DEPRECATED: this will be removed in v1.0.0
|
|
||||||
if _, err := os.Stat("config.yaml"); err == nil {
|
|
||||||
log.Warn("DEPRECATED: ./config.yaml as a configuration file is deprecated and will be removed as an option in v1.0.0, please rename to .syft.yaml")
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(confFilePath + ".yaml"); err == nil {
|
|
||||||
if err = v.ReadInConfig(); err == nil {
|
|
||||||
v.Set("config", v.ConfigFileUsed())
|
|
||||||
return nil
|
|
||||||
} else if !errors.As(err, &viper.ConfigFileNotFoundError{}) {
|
|
||||||
return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. look for .<appname>/config.yaml (in the current directory)
|
|
||||||
v.AddConfigPath("." + internal.ApplicationName)
|
|
||||||
v.SetConfigName("config")
|
|
||||||
if err = v.ReadInConfig(); err == nil {
|
|
||||||
v.Set("config", v.ConfigFileUsed())
|
|
||||||
return nil
|
|
||||||
} else if !errors.As(err, &viper.ConfigFileNotFoundError{}) {
|
|
||||||
return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. look for ~/.<appname>.yaml
|
|
||||||
home, err := homedir.Dir()
|
|
||||||
if err == nil {
|
|
||||||
v.AddConfigPath(home)
|
|
||||||
v.SetConfigName("." + internal.ApplicationName)
|
|
||||||
if err = v.ReadInConfig(); err == nil {
|
|
||||||
v.Set("config", v.ConfigFileUsed())
|
|
||||||
return nil
|
|
||||||
} else if !errors.As(err, &viper.ConfigFileNotFoundError{}) {
|
|
||||||
return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. look for <appname>/config.yaml in xdg locations (starting with xdg home config dir, then moving upwards)
|
|
||||||
v.SetConfigName("config")
|
|
||||||
configPath = path.Join(xdg.ConfigHome, internal.ApplicationName)
|
|
||||||
v.AddConfigPath(configPath)
|
|
||||||
for _, dir := range xdg.ConfigDirs {
|
|
||||||
v.AddConfigPath(path.Join(dir, internal.ApplicationName))
|
|
||||||
}
|
|
||||||
if err = v.ReadInConfig(); err == nil {
|
|
||||||
v.Set("config", v.ConfigFileUsed())
|
|
||||||
return nil
|
|
||||||
} else if !errors.As(err, &viper.ConfigFileNotFoundError{}) {
|
|
||||||
return fmt.Errorf("unable to parse config=%q: %w", v.ConfigFileUsed(), err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var validDefaultSourceValues = []string{"registry", "docker", "podman", ""}
|
|
||||||
|
|
||||||
func checkDefaultSourceValues(source string) error {
|
|
||||||
validValues := internal.NewStringSet(validDefaultSourceValues...)
|
|
||||||
if !validValues.Contains(source) {
|
|
||||||
validValuesString := strings.Join(validDefaultSourceValues, ", ")
|
|
||||||
return fmt.Errorf("%s is not a valid default source; please use one of the following: %s''", source, validValuesString)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,129 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/adrg/xdg"
|
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: set negative case when config.yaml is no longer a valid option
|
|
||||||
func TestApplicationConfig(t *testing.T) {
|
|
||||||
// disable homedir package cache for testing
|
|
||||||
originalCacheOpt := homedir.DisableCache
|
|
||||||
homedir.DisableCache = true
|
|
||||||
t.Cleanup(func() {
|
|
||||||
homedir.DisableCache = originalCacheOpt
|
|
||||||
})
|
|
||||||
|
|
||||||
// ensure we have no side effects for xdg package for future tests
|
|
||||||
originalXDG := os.Getenv("XDG_CONFIG_HOME")
|
|
||||||
t.Cleanup(func() {
|
|
||||||
// note: we're not using t.Setenv since the effect we're trying to eliminate is within the xdg package
|
|
||||||
require.NoError(t, os.Setenv("XDG_CONFIG_HOME", originalXDG))
|
|
||||||
xdg.Reload()
|
|
||||||
})
|
|
||||||
|
|
||||||
// config is picked up at desired configuration paths
|
|
||||||
// VALID: .syft.yaml, .syft/config.yaml, ~/.syft.yaml, <XDG_CONFIG_HOME>/syft/config.yaml
|
|
||||||
// DEPRECATED: config.yaml is currently supported by
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
setup func(t *testing.T) string
|
|
||||||
assertions func(t *testing.T, app *Application)
|
|
||||||
cleanup func()
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "explicit config",
|
|
||||||
setup: func(t *testing.T) string {
|
|
||||||
return "./test-fixtures/.syft.yaml"
|
|
||||||
}, // no-op for explicit config
|
|
||||||
assertions: func(t *testing.T, app *Application) {
|
|
||||||
assert.Equal(t, "test-explicit-config", app.File)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "current working directory named config",
|
|
||||||
setup: func(t *testing.T) string {
|
|
||||||
err := os.Chdir("./test-fixtures/config-wd-file") // change application cwd to test-fixtures
|
|
||||||
require.NoError(t, err)
|
|
||||||
return ""
|
|
||||||
},
|
|
||||||
assertions: func(t *testing.T, app *Application) {
|
|
||||||
assert.Equal(t, "test-wd-named-config", app.File)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "current working directory syft dir config",
|
|
||||||
setup: func(t *testing.T) string {
|
|
||||||
err := os.Chdir("./test-fixtures/config-dir-test") // change application cwd to test-fixtures
|
|
||||||
require.NoError(t, err)
|
|
||||||
return ""
|
|
||||||
},
|
|
||||||
assertions: func(t *testing.T, app *Application) {
|
|
||||||
assert.Equal(t, "test-dir-config", app.File)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "home directory file config",
|
|
||||||
setup: func(t *testing.T) string {
|
|
||||||
// Because Setenv affects the whole process, it cannot be used in parallel tests or
|
|
||||||
// tests with parallel ancestors: see separate XDG test for consequence of this
|
|
||||||
t.Setenv("HOME", "./test-fixtures/config-home-test/config-file")
|
|
||||||
return ""
|
|
||||||
},
|
|
||||||
assertions: func(t *testing.T, app *Application) {
|
|
||||||
assert.Equal(t, "test-home-config", app.File)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "XDG file config",
|
|
||||||
setup: func(t *testing.T) string {
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
require.NoError(t, err)
|
|
||||||
configDir := path.Join(wd, "./test-fixtures/config-home-test") // set HOME to testdata
|
|
||||||
// note: this explicitly has multiple XDG paths, make certain we use the first VALID one (not the first one)
|
|
||||||
t.Setenv("XDG_CONFIG_DIRS", fmt.Sprintf("/another/foo/bar:%s", configDir))
|
|
||||||
xdg.Reload()
|
|
||||||
return ""
|
|
||||||
},
|
|
||||||
assertions: func(t *testing.T, app *Application) {
|
|
||||||
assert.Equal(t, "test-home-XDG-config", app.File)
|
|
||||||
},
|
|
||||||
cleanup: func() {
|
|
||||||
require.NoError(t, os.Unsetenv("XDG_CONFIG_DIRS"))
|
|
||||||
xdg.Reload()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
if test.cleanup != nil {
|
|
||||||
t.Cleanup(test.cleanup)
|
|
||||||
}
|
|
||||||
wd, err := os.Getwd()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
defer os.Chdir(wd) // reset working directory after test
|
|
||||||
application := &Application{}
|
|
||||||
viperInstance := viper.New()
|
|
||||||
|
|
||||||
// this will override home in case you are running this test locally and DO have a syft config
|
|
||||||
// in your home directory... now it will be ignored. Same for XDG_CONFIG_DIRS.
|
|
||||||
t.Setenv("HOME", "/foo/bar")
|
|
||||||
t.Setenv("XDG_CONFIG_DIRS", "/foo/bar")
|
|
||||||
xdg.Reload()
|
|
||||||
|
|
||||||
configPath := test.setup(t)
|
|
||||||
err = application.LoadAllValues(viperInstance, configPath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
test.assertions(t, application)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import "github.com/spf13/viper"
|
|
||||||
|
|
||||||
type attest struct {
|
|
||||||
// IMPORTANT: do not show the attestation key/password in any YAML/JSON output (sensitive information)
|
|
||||||
Key string `yaml:"-" json:"-" mapstructure:"key"`
|
|
||||||
Password string `yaml:"-" json:"-" mapstructure:"password"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg attest) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("attest.key", "")
|
|
||||||
v.SetDefault("attest.password", "")
|
|
||||||
}
|
|
||||||
@ -1,29 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
type catalogerOptions struct {
|
|
||||||
Enabled bool `yaml:"enabled" json:"enabled" mapstructure:"enabled"`
|
|
||||||
Scope string `yaml:"scope" json:"scope" mapstructure:"scope"`
|
|
||||||
ScopeOpt source.Scope `yaml:"-" json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg catalogerOptions) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("package.cataloger.enabled", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *catalogerOptions) parseConfigValues() error {
|
|
||||||
scopeOption := source.ParseScope(cfg.Scope)
|
|
||||||
if scopeOption == source.UnknownScope {
|
|
||||||
return fmt.Errorf("bad scope value %q", cfg.Scope)
|
|
||||||
}
|
|
||||||
cfg.ScopeOpt = scopeOption
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import "github.com/spf13/viper"
|
|
||||||
|
|
||||||
type development struct {
|
|
||||||
ProfileCPU bool `yaml:"profile-cpu" json:"profile-cpu" mapstructure:"profile-cpu"`
|
|
||||||
ProfileMem bool `yaml:"profile-mem" json:"profile-mem" mapstructure:"profile-mem"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg development) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("dev.profile-cpu", false)
|
|
||||||
v.SetDefault("dev.profile-mem", false)
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
type fileClassification struct {
|
|
||||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg fileClassification) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("file-classification.cataloger.enabled", catalogerEnabledDefault)
|
|
||||||
v.SetDefault("file-classification.cataloger.scope", source.SquashedScope)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *fileClassification) parseConfigValues() error {
|
|
||||||
return cfg.Cataloger.parseConfigValues()
|
|
||||||
}
|
|
||||||
@ -1,25 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/internal/file"
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
type fileContents struct {
|
|
||||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
|
||||||
SkipFilesAboveSize int64 `yaml:"skip-files-above-size" json:"skip-files-above-size" mapstructure:"skip-files-above-size"`
|
|
||||||
Globs []string `yaml:"globs" json:"globs" mapstructure:"globs"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg fileContents) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("file-contents.cataloger.enabled", catalogerEnabledDefault)
|
|
||||||
v.SetDefault("file-contents.cataloger.scope", source.SquashedScope)
|
|
||||||
v.SetDefault("file-contents.skip-files-above-size", 1*file.MB)
|
|
||||||
v.SetDefault("file-contents.globs", []string{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *fileContents) parseConfigValues() error {
|
|
||||||
return cfg.Cataloger.parseConfigValues()
|
|
||||||
}
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/source"
|
|
||||||
)
|
|
||||||
|
|
||||||
type FileMetadata struct {
|
|
||||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
|
||||||
Digests []string `yaml:"digests" json:"digests" mapstructure:"digests"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg FileMetadata) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("file-metadata.cataloger.enabled", catalogerEnabledDefault)
|
|
||||||
v.SetDefault("file-metadata.cataloger.scope", source.SquashedScope)
|
|
||||||
v.SetDefault("file-metadata.digests", []string{"sha256"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *FileMetadata) parseConfigValues() error {
|
|
||||||
return cfg.Cataloger.parseConfigValues()
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import "github.com/spf13/viper"
|
|
||||||
|
|
||||||
type linuxKernel struct {
|
|
||||||
CatalogModules bool `json:"catalog-modules" yaml:"catalog-modules" mapstructure:"catalog-modules"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg linuxKernel) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("linux-kernel.catalog-modules", true)
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/mitchellh/go-homedir"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/go-logger"
|
|
||||||
)
|
|
||||||
|
|
||||||
// logging contains all logging-related configuration options available to the user via the application config.
|
|
||||||
type logging struct {
|
|
||||||
Structured bool `yaml:"structured" json:"structured" mapstructure:"structured"` // show all log entries as JSON formatted strings
|
|
||||||
Level logger.Level `yaml:"level" json:"level" mapstructure:"level"` // the log level string hint
|
|
||||||
FileLocation string `yaml:"file" json:"file-location" mapstructure:"file"` // the file path to write logs to
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *logging) parseConfigValues() error {
|
|
||||||
if cfg.FileLocation != "" {
|
|
||||||
expandedPath, err := homedir.Expand(cfg.FileLocation)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to expand log file path=%q: %w", cfg.FileLocation, err)
|
|
||||||
}
|
|
||||||
cfg.FileLocation = expandedPath
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg logging) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("log.structured", false)
|
|
||||||
v.SetDefault("log.file", "")
|
|
||||||
v.SetDefault("log.level", string(logger.WarnLevel))
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger"
|
|
||||||
)
|
|
||||||
|
|
||||||
type pkg struct {
|
|
||||||
Cataloger catalogerOptions `yaml:"cataloger" json:"cataloger" mapstructure:"cataloger"`
|
|
||||||
SearchUnindexedArchives bool `yaml:"search-unindexed-archives" json:"search-unindexed-archives" mapstructure:"search-unindexed-archives"`
|
|
||||||
SearchIndexedArchives bool `yaml:"search-indexed-archives" json:"search-indexed-archives" mapstructure:"search-indexed-archives"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg pkg) loadDefaultValues(v *viper.Viper) {
|
|
||||||
cfg.Cataloger.loadDefaultValues(v)
|
|
||||||
c := cataloger.DefaultSearchConfig()
|
|
||||||
v.SetDefault("package.search-unindexed-archives", c.IncludeUnindexedArchives)
|
|
||||||
v.SetDefault("package.search-indexed-archives", c.IncludeIndexedArchives)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *pkg) parseConfigValues() error {
|
|
||||||
return cfg.Cataloger.parseConfigValues()
|
|
||||||
}
|
|
||||||
@ -1,75 +0,0 @@
|
|||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
|
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RegistryCredentials struct {
|
|
||||||
Authority string `yaml:"authority" json:"authority" mapstructure:"authority"`
|
|
||||||
// IMPORTANT: do not show the username in any YAML/JSON output (sensitive information)
|
|
||||||
Username string `yaml:"-" json:"-" mapstructure:"username"`
|
|
||||||
// IMPORTANT: do not show the password in any YAML/JSON output (sensitive information)
|
|
||||||
Password string `yaml:"-" json:"-" mapstructure:"password"`
|
|
||||||
// IMPORTANT: do not show the token in any YAML/JSON output (sensitive information)
|
|
||||||
Token string `yaml:"-" json:"-" mapstructure:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type registry struct {
|
|
||||||
InsecureSkipTLSVerify bool `yaml:"insecure-skip-tls-verify" json:"insecure-skip-tls-verify" mapstructure:"insecure-skip-tls-verify"`
|
|
||||||
InsecureUseHTTP bool `yaml:"insecure-use-http" json:"insecure-use-http" mapstructure:"insecure-use-http"`
|
|
||||||
Auth []RegistryCredentials `yaml:"auth" json:"auth" mapstructure:"auth"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg registry) loadDefaultValues(v *viper.Viper) {
|
|
||||||
v.SetDefault("registry.insecure-skip-tls-verify", false)
|
|
||||||
v.SetDefault("registry.insecure-use-http", false)
|
|
||||||
v.SetDefault("registry.auth", []RegistryCredentials{})
|
|
||||||
}
|
|
||||||
|
|
||||||
//nolint:unparam
|
|
||||||
func (cfg *registry) parseConfigValues() error {
|
|
||||||
// there may be additional credentials provided by env var that should be appended to the set of credentials
|
|
||||||
authority, username, password, token :=
|
|
||||||
os.Getenv("SYFT_REGISTRY_AUTH_AUTHORITY"),
|
|
||||||
os.Getenv("SYFT_REGISTRY_AUTH_USERNAME"),
|
|
||||||
os.Getenv("SYFT_REGISTRY_AUTH_PASSWORD"),
|
|
||||||
os.Getenv("SYFT_REGISTRY_AUTH_TOKEN")
|
|
||||||
|
|
||||||
if hasNonEmptyCredentials(username, password, token) {
|
|
||||||
// note: we prepend the credentials such that the environment variables take precedence over on-disk configuration.
|
|
||||||
cfg.Auth = append([]RegistryCredentials{
|
|
||||||
{
|
|
||||||
Authority: authority,
|
|
||||||
Username: username,
|
|
||||||
Password: password,
|
|
||||||
Token: token,
|
|
||||||
},
|
|
||||||
}, cfg.Auth...)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func hasNonEmptyCredentials(username, password, token string) bool {
|
|
||||||
return password != "" && username != "" || token != ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *registry) ToOptions() *image.RegistryOptions {
|
|
||||||
var auth = make([]image.RegistryCredentials, len(cfg.Auth))
|
|
||||||
for i, a := range cfg.Auth {
|
|
||||||
auth[i] = image.RegistryCredentials{
|
|
||||||
Authority: a.Authority,
|
|
||||||
Username: a.Username,
|
|
||||||
Password: a.Password,
|
|
||||||
Token: a.Token,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &image.RegistryOptions{
|
|
||||||
InsecureSkipTLSVerify: cfg.InsecureSkipTLSVerify,
|
|
||||||
InsecureUseHTTP: cfg.InsecureUseHTTP,
|
|
||||||
Credentials: auth,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
# same as --file; write output report to a file (default is to write to stdout)
|
|
||||||
file: "test-explicit-config"
|
|
||||||
package:
|
|
||||||
cataloger:
|
|
||||||
scope: "squashed"
|
|
||||||
|
|
||||||
# same as --scope; limit the scope of the cataloger to only the specified types
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# same as --file; write output report to a file (default is to write to stdout)
|
|
||||||
file: "test-dir-config"
|
|
||||||
package:
|
|
||||||
cataloger:
|
|
||||||
scope: "squashed"
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# same as --file; write output report to a file (default is to write to stdout)
|
|
||||||
file: "test-home-config"
|
|
||||||
package:
|
|
||||||
cataloger:
|
|
||||||
scope: "squashed"
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# same as --file; write output report to a file (default is to write to stdout)
|
|
||||||
file: "test-home-XDG-config"
|
|
||||||
package:
|
|
||||||
cataloger:
|
|
||||||
scope: "squashed"
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
# same as --file; write output report to a file (default is to write to stdout)
|
|
||||||
file: "test-wd-named-config"
|
|
||||||
package:
|
|
||||||
cataloger:
|
|
||||||
scope: "squashed"
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user