Compare commits

..
Author SHA1 Message Date
Keith Zantow 674558adbd feat: detect name/version from directory scans
Signed-off-by: Keith Zantow <kzantow@gmail.com>
2024-05-29 21:14:41 -04:00
264 changed files with 3426 additions and 37796 deletions
+7 -7
View File
@@ -2,7 +2,7 @@ tools:
# we want to use a pinned version of binny to manage the toolchain (so binny manages itself!)
- name: binny
version:
want: v0.8.0
want: v0.7.0
method: github-release
with:
repo: anchore/binny
@@ -18,7 +18,7 @@ tools:
# used to sign mac binaries at release
- name: quill
version:
want: v0.4.2
want: v0.4.1
method: github-release
with:
repo: anchore/quill
@@ -26,7 +26,7 @@ tools:
# used for linting
- name: golangci-lint
version:
want: v1.59.1
want: v1.59.0
method: github-release
with:
repo: golangci/golangci-lint
@@ -42,7 +42,7 @@ tools:
# used for signing the checksums file at release
- name: cosign
version:
want: v2.3.0
want: v2.2.4
method: github-release
with:
repo: sigstore/cosign
@@ -58,7 +58,7 @@ tools:
# used to release all artifacts
- name: goreleaser
version:
want: v2.1.0
want: v1.26.2
method: github-release
with:
repo: goreleaser/goreleaser
@@ -103,7 +103,7 @@ tools:
# used for running all local and CI tasks
- name: task
version:
want: v3.38.0
want: v3.37.2
method: github-release
with:
repo: go-task/task
@@ -111,7 +111,7 @@ tools:
# used for triggering a release
- name: gh
version:
want: v2.54.0
want: v2.49.2
method: github-release
with:
repo: cli/cli
-1
View File
@@ -6,7 +6,6 @@ permit:
- MPL.*
- ISC
- WTFPL
- Unlicense
ignore-packages:
# packageurl-go is released under the MIT license located in the root of the repo at /mit.LICENSE
+3 -3
View File
@@ -5,7 +5,7 @@ inputs:
go-version:
description: "Go version to install"
required: true
default: "1.22.x"
default: "1.21.x"
go-dependencies:
description: "Download go dependencies"
required: true
@@ -27,14 +27,14 @@ runs:
using: "composite"
steps:
# note: go mod and build is automatically cached on default with v4+
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 # v5.0.1
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 #v5.0.1
if: inputs.go-version != ''
with:
go-version: ${{ inputs.go-version }}
- name: Restore tool cache
id: tool-cache
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
with:
path: ${{ github.workspace }}/.tool
key: ${{ inputs.cache-key-prefix }}-${{ runner.os }}-tool-${{ hashFiles('.binny.yaml') }}
-8
View File
@@ -17,11 +17,3 @@ updates:
open-pull-requests-limit: 10
labels:
- "dependencies"
- package-ecosystem: "github-actions"
directory: "/.github/actions/boostrap"
schedule:
interval: "daily"
open-pull-requests-limit: 10
labels:
- "dependencies"
+7 -14
View File
@@ -4,19 +4,12 @@ set -eu
bold=$(tput bold)
normal=$(tput sgr0)
GH_CLI=.tool/gh
if ! [ -x "$(command -v $GH_CLI)" ]; then
echo "The GitHub CLI could not be found. run: make bootstrap"
if ! [ -x "$(command -v gh)" ]; then
echo "The GitHub CLI could not be found. To continue follow the instructions at https://github.com/cli/cli#installation"
exit 1
fi
$GH_CLI auth status
# set the default repo in cases where multiple remotes are defined
$GH_CLI repo set-default anchore/syft
export GITHUB_TOKEN="${GITHUB_TOKEN-"$($GH_CLI auth token)"}"
gh auth status
# we need all of the git state to determine the next version. Since tagging is done by
# the release pipeline it is possible to not have all of the tags from previous releases.
@@ -44,7 +37,7 @@ done
echo "${bold}Kicking off release for ${NEXT_VERSION}${normal}..."
echo
$GH_CLI workflow run release.yaml -f version=${NEXT_VERSION}
gh workflow run release.yaml -f version=${NEXT_VERSION}
echo
echo "${bold}Waiting for release to start...${normal}"
@@ -52,6 +45,6 @@ sleep 10
set +e
echo "${bold}Head to the release workflow to monitor the release:${normal} $($GH_CLI run list --workflow=release.yaml --limit=1 --json url --jq '.[].url')"
id=$($GH_CLI run list --workflow=release.yaml --limit=1 --json databaseId --jq '.[].databaseId')
$GH_CLI run watch $id --exit-status || (echo ; echo "${bold}Logs of failed step:${normal}" && GH_PAGER="" $GH_CLI run view $id --log-failed)
echo "${bold}Head to the release workflow to monitor the release:${normal} $(gh run list --workflow=release.yaml --limit=1 --json url --jq '.[].url')"
id=$(gh run list --workflow=release.yaml --limit=1 --json databaseId --jq '.[].databaseId')
gh run watch $id --exit-status || (echo ; echo "${bold}Logs of failed step:${normal}" && GH_PAGER="" gh run view $id --log-failed)
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
# the job by event.
steps:
- name: Checkout code
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -39,7 +39,7 @@ jobs:
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
- uses: actions/upload-artifact@89ef406dd8d7e03cfd12d9e0a4a378f454709029 # v4.3.5
- uses: actions/upload-artifact@65462800fd760344b1a7b4382951275a0abb4808 # v4.3.3
with:
name: benchmark-test-results
path: test/results/**/*
+5 -5
View File
@@ -36,16 +36,16 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Install Go
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 #v5.0.2
uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 #v5.0.1
with:
go-version-file: go.mod
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a #v3.25.15
uses: github/codeql-action/init@9fdb3e49720b44c48891d036bb502feb25684276 #v3.25.6
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
@@ -56,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a #v3.25.15
uses: github/codeql-action/autobuild@9fdb3e49720b44c48891d036bb502feb25684276 #v3.25.6
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -70,4 +70,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a #v3.25.15
uses: github/codeql-action/analyze@9fdb3e49720b44c48891d036bb502feb25684276 #v3.25.6
+1 -1
View File
@@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- run: python .github/scripts/labeler.py
env:
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
release:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Update version file
run: make ci-release-version-file
+5 -13
View File
@@ -15,15 +15,7 @@ jobs:
environment: release
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- name: Check if running on main
if: github.ref != 'refs/heads/main'
# we are using the following flag when running `cosign blob-verify` for checksum signature verification:
# --certificate-identity-regexp "https://github.com/anchore/.github/workflows/release.yaml@refs/heads/main"
# if we are not on the main branch, the signature will not be verifiable since the suffix requires the main branch
# at the time of when the OIDC token was issued on the Github Actions runner.
run: echo "This can only be run on the main branch otherwise releases produced will not be verifiable with cosign" && exit 1
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Check if tag already exists
# note: this will fail if the tag already exists
@@ -105,7 +97,7 @@ jobs:
# required for goreleaser signs section with cosign
id-token: write
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
with:
fetch-depth: 0
@@ -113,13 +105,13 @@ jobs:
uses: ./.github/actions/bootstrap
- name: Login to Docker Hub
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 #v3.3.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 #v3.2.0
with:
username: ${{ secrets.ANCHOREOSSWRITE_DH_USERNAME }}
password: ${{ secrets.ANCHOREOSSWRITE_DH_PAT }}
- name: Login to GitHub Container Registry
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 #v3.3.0
uses: docker/login-action@0d4c9c5ea7693da7b068278f7b52bda2a190a446 #v3.2.0
with:
registry: ghcr.io
username: ${{ github.actor }}
@@ -148,7 +140,7 @@ jobs:
# for updating brew formula in anchore/homebrew-syft
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
- uses: anchore/sbom-action@d94f46e13c6c62f59525ac9a1e147a99dc0b9bf5 #v0.17.0
- uses: anchore/sbom-action@e8d2a6937ecead383dfe75190d104edd1f9c5751 #v0.16.0
continue-on-error: true
with:
artifact-name: sbom.spdx.json
+2 -2
View File
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -50,7 +50,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c #v6.1.0
- uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
with:
signoff: true
delete-branch: true
@@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -33,7 +33,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c #v6.1.0
- uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
with:
signoff: true
delete-branch: true
@@ -17,9 +17,9 @@ jobs:
runs-on: ubuntu-latest
if: github.repository == 'anchore/syft' # only run for main repo
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 #v5.0.2
- uses: actions/setup-go@cdcb36043654635271a94b9a6d1392de5bb323a7 #v5.0.1
with:
go-version: ${{ env.GO_VERSION }}
stable: ${{ env.GO_STABLE_VERSION }}
@@ -44,7 +44,7 @@ jobs:
app_id: ${{ secrets.TOKEN_APP_ID }}
private_key: ${{ secrets.TOKEN_APP_PRIVATE_KEY }}
- uses: peter-evans/create-pull-request@c5a7806660adbe173f04e3e038b0ccdcd758773c #v6.1.0
- uses: peter-evans/create-pull-request@6d6857d36972b65feb161a90e484f2984215f83e #v6.0.5
with:
signoff: true
delete-branch: true
+7 -10
View File
@@ -17,7 +17,7 @@ jobs:
name: "Static analysis"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -31,7 +31,7 @@ jobs:
name: "Unit tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -87,7 +87,7 @@ jobs:
name: "Integration tests"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -109,7 +109,7 @@ jobs:
name: "Build snapshot artifacts"
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -139,7 +139,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -187,10 +187,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: macos-latest
steps:
- name: Install Cosign
uses: sigstore/cosign-installer@v3.5.0
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
@@ -234,7 +231,7 @@ jobs:
needs: [Build-Snapshot-Artifacts]
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 #v4.1.6
- name: Bootstrap environment
uses: ./.github/actions/bootstrap
+1 -3
View File
@@ -1,5 +1,3 @@
version: 2
release:
prerelease: auto
draft: false
@@ -82,7 +80,7 @@ nfpms:
- deb
brews:
- repository:
- tap:
owner: anchore
name: homebrew-syft
token: "{{.Env.GITHUB_BREW_TOKEN}}"
-128
View File
@@ -1,128 +0,0 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[opensource@anchore.com](mailto:opensource@anchore.com).
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.
+8 -17
View File
@@ -11,7 +11,7 @@
&nbsp;<a href="https://goreportcard.com/report/github.com/anchore/syft" target="_blank"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/anchore/syft"></a>&nbsp;
&nbsp;<a href="https://github.com/anchore/syft/releases/latest" target="_blank"><img alt="GitHub release" src="https://img.shields.io/github/release/anchore/syft.svg"></a>&nbsp;
&nbsp;<a href="https://github.com/anchore/syft" target="_blank"><img alt="GitHub go.mod Go version" src="https://img.shields.io/github/go-mod/go-version/anchore/syft.svg"></a>&nbsp;
&nbsp;<a href="" target="_blank"><img alt="License: Apache-2.0" src="https://img.shields.io/badge/License-Apache%202.0-blue.svg"></a>&nbsp;
&nbsp;<a href"" target="_blank"><img alt="License: Apache-2.0" src="https://img.shields.io/badge/License-Apache%202.0-blue.svg"></a>&nbsp;
&nbsp;<a href="https://anchore.com/slack" target="_blank"><img alt="Slack" src="https://img.shields.io/badge/Slack-Join-blue?logo=slack"></a>&nbsp;
</p>
@@ -40,10 +40,11 @@ Syft binaries are provided for Linux, macOS and Windows.
> curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
> ```
Install script options:
- `-b`: Specify a custom installation directory (defaults to `./bin`)
- `-d`: More verbose logging levels (`-d` for debug, `-dd` for trace)
- `-v`: Verify the signature of the downloaded artifact before installation (requires [`cosign`](https://github.com/sigstore/cosign) to be installed)
... or, you can specify a release version and destination directory for the installation:
> ```bash
> curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b <DESTINATION_DIR> <RELEASE_VERSION>
> ```
### Homebrew
```bash
@@ -105,10 +106,8 @@ syft <image> -o <format>
Where the `formats` available are:
- `syft-json`: Use this to get as much information out of Syft as possible!
- `syft-text`: A row-oriented, human-and-machine-friendly output.
- `cyclonedx-xml`: A XML report conforming to the [CycloneDX 1.6 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-xml@1.5`: A XML report conforming to the [CycloneDX 1.5 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-json`: A JSON report conforming to the [CycloneDX 1.6 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-json@1.5`: A JSON report conforming to the [CycloneDX 1.5 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-xml`: A XML report conforming to the [CycloneDX 1.4 specification](https://cyclonedx.org/specification/overview/).
- `cyclonedx-json`: A JSON report conforming to the [CycloneDX 1.4 specification](https://cyclonedx.org/specification/overview/).
- `spdx-tag-value`: A tag-value formatted report conforming to the [SPDX 2.3 specification](https://spdx.github.io/spdx-spec/v2.3/).
- `spdx-tag-value@2.2`: A tag-value formatted report conforming to the [SPDX 2.2 specification](https://spdx.github.io/spdx-spec/v2.2.2/).
- `spdx-json`: A JSON report conforming to the [SPDX 2.3 JSON Schema](https://github.com/spdx/spdx-spec/blob/v2.3/schemas/spdx-schema.json).
@@ -117,8 +116,6 @@ Where the `formats` available are:
- `syft-table`: A columnar summary (default).
- `template`: Lets the user specify the output format. See ["Using templates"](#using-templates) below.
Note that flags using the @<version> can be used for earlier versions of each specification as well.
### Supported Ecosystems
- Alpine (apk)
@@ -168,15 +165,9 @@ Our [wiki](https://github.com/anchore/syft/wiki) contains further details on the
* [Adding an SBOM to an image as an attestation using Syft](https://github.com/anchore/syft/wiki/attestation#adding-an-sbom-to-an-image-as-an-attestation-using-syft)
* [Configuration](https://github.com/anchore/syft/wiki/configuration)
## Contributing
Check out our [contributing](/CONTRIBUTING.md) guide and [developer](/DEVELOPING.md) docs.
## Syft Team Meetings
The Syft Team hold regular community meetings online. All are welcome to join to bring topics for discussion.
- Check the [calendar](https://calendar.google.com/calendar/u/0/r?cid=Y182OTM4dGt0MjRtajI0NnNzOThiaGtnM29qNEBncm91cC5jYWxlbmRhci5nb29nbGUuY29t) for the next meeting date.
- Add items to the [agenda](https://docs.google.com/document/d/1ZtSAa6fj2a6KRWviTn3WoJm09edvrNUp4Iz_dOjjyY8/edit?usp=sharing) (join [this group](https://groups.google.com/g/anchore-oss-community) for write access to the [agenda](https://docs.google.com/document/d/1ZtSAa6fj2a6KRWviTn3WoJm09edvrNUp4Iz_dOjjyY8/edit?usp=sharing))
- See you there!
<!-- KJIc1D4zXJbRybKEJrWd -->
-2
View File
@@ -43,7 +43,6 @@ type attestOptions struct {
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
options.Catalog `yaml:",inline" mapstructure:",squash"`
Attest options.Attest `yaml:"attest" mapstructure:"attest"`
Cache options.Cache `json:"-" yaml:"cache" mapstructure:"cache"`
}
func Attest(app clio.Application) *cobra.Command {
@@ -78,7 +77,6 @@ func defaultAttestOptions() attestOptions {
Output: defaultAttestOutputOptions(),
UpdateCheck: options.DefaultUpdateCheck(),
Catalog: options.DefaultCatalog(),
Cache: options.DefaultCache(),
}
}
-2
View File
@@ -68,7 +68,6 @@ type scanOptions struct {
options.Output `yaml:",inline" mapstructure:",squash"`
options.UpdateCheck `yaml:",inline" mapstructure:",squash"`
options.Catalog `yaml:",inline" mapstructure:",squash"`
Cache options.Cache `json:"-" yaml:"cache" mapstructure:"cache"`
}
func defaultScanOptions() *scanOptions {
@@ -76,7 +75,6 @@ func defaultScanOptions() *scanOptions {
Output: options.DefaultOutput(),
UpdateCheck: options.DefaultUpdateCheck(),
Catalog: options.DefaultCatalog(),
Cache: options.DefaultCache(),
}
}
-131
View File
@@ -1,131 +0,0 @@
package options
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"github.com/adrg/xdg"
"github.com/mitchellh/go-homedir"
"github.com/anchore/clio"
"github.com/anchore/syft/internal/cache"
"github.com/anchore/syft/internal/log"
)
// Cache provides configuration for the Syft caching behavior
type Cache struct {
Dir string `yaml:"dir" mapstructure:"dir"`
TTL string `yaml:"ttl" mapstructure:"ttl"`
}
func (c *Cache) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&c.Dir, "root directory to cache any downloaded content; empty string will use an in-memory cache")
descriptions.Add(&c.TTL, "time to live for cached data; setting this to 0 will disable caching entirely")
}
func (c *Cache) PostLoad() error {
ttl, err := parseDuration(c.TTL)
if err != nil {
log.Warnf("unable to parse duration '%v', using default (%s) due to: %v", c.TTL, durationToString(defaultTTL()), err)
ttl = defaultTTL()
}
// if TTL is <= 0, disable caching entirely
if ttl <= 0 {
cache.SetManager(nil)
return nil
}
// if dir == "" but we have a TTL, use an in-memory cache
if c.Dir == "" {
cache.SetManager(cache.NewInMemory(ttl))
return nil
}
dir, err := homedir.Expand(c.Dir)
if err != nil {
log.Warnf("unable to expand cache directory %s: %v", c.Dir, err)
cache.SetManager(cache.NewInMemory(ttl))
} else {
m, err := cache.NewFromDir(dir, ttl)
if err != nil {
log.Warnf("unable to get filesystem cache at %s: %v", c.Dir, err)
cache.SetManager(cache.NewInMemory(ttl))
} else {
cache.SetManager(m)
}
}
return nil
}
var _ interface {
clio.PostLoader
clio.FieldDescriber
} = (*Cache)(nil)
func DefaultCache() Cache {
return Cache{
Dir: defaultDir(),
TTL: durationToString(defaultTTL()),
}
}
func defaultTTL() time.Duration {
return 7 * 24 * time.Hour
}
func defaultDir() string {
var err error
cacheRoot := xdg.CacheHome
if cacheRoot == "" {
cacheRoot, err = homedir.Dir()
if err != nil {
cacheRoot = os.TempDir()
log.Debugf("unable to get stable cache directory due to: %v, defaulting cache to temp dir: %s", err, cacheRoot)
} else {
cacheRoot = filepath.Join(cacheRoot, ".cache")
}
}
return filepath.Join(cacheRoot, "syft")
}
func durationToString(duration time.Duration) string {
days := int64(duration / (24 * time.Hour))
remain := duration % (24 * time.Hour)
out := ""
if days > 0 {
out = fmt.Sprintf("%vd", days)
}
if remain != 0 {
out += remain.String()
}
if out == "" {
return "0"
}
return out
}
func parseDuration(duration string) (time.Duration, error) {
whitespace := regexp.MustCompile(`\s+`)
duration = strings.ToLower(whitespace.ReplaceAllString(duration, ""))
parts := strings.SplitN(duration, "d", 2)
var days time.Duration
var remain time.Duration
var err error
if len(parts) > 1 {
numDays, daysErr := strconv.Atoi(parts[0])
if daysErr != nil {
return 0, daysErr
}
days = time.Duration(numDays) * 24 * time.Hour
if len(parts[1]) > 0 {
remain, err = time.ParseDuration(parts[1])
}
} else {
remain, err = time.ParseDuration(duration)
}
return days + remain, err
}
-313
View File
@@ -1,313 +0,0 @@
package options
import (
"io"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/adrg/xdg"
"github.com/mitchellh/go-homedir"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/cache"
)
func Test_defaultDir(t *testing.T) {
tmpDir := filepath.Join(t.TempDir(), "cache-temp")
xdgCacheDir := filepath.Join(tmpDir, "fake-xdg-cache")
homeDir := filepath.Join(tmpDir, "fake-home")
tests := []struct {
name string
env map[string]string
expected string
}{
{
name: "no-xdg",
env: map[string]string{
"HOME": homeDir,
},
expected: homeDir,
},
{
name: "xdg-cache",
env: map[string]string{
"XDG_CACHE_HOME": xdgCacheDir,
},
expected: xdgCacheDir,
},
}
// capture all the initial environment variables to reset them before we reset library caches
env := map[string]string{
"HOME": "",
"XDG_DATA_HOME": "",
"XDG_DATA_DIRS": "",
"XDG_CONFIG_HOME": "",
"XDG_CONFIG_DIRS": "",
"XDG_STATE_HOME": "",
"XDG_CACHE_HOME": "",
"XDG_RUNTIME_DIR": "",
}
for k := range env {
env[k] = os.Getenv(k)
}
unsetEnv := func(t *testing.T) {
for k := range env {
t.Setenv(k, "")
}
}
resetEnv := func() {
for k, v := range env {
if v == "" {
_ = os.Unsetenv(k)
} else {
_ = os.Setenv(k, v)
}
}
homedir.Reset()
xdg.Reload()
}
t.Cleanup(resetEnv)
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
defer resetEnv()
unsetEnv(t)
for k, v := range test.env {
t.Setenv(k, v)
}
homedir.Reset()
xdg.Reload()
got := defaultDir()
require.True(t, strings.HasPrefix(got, test.expected))
})
}
}
func Test_cacheOptions(t *testing.T) {
tmp := t.TempDir()
tests := []struct {
name string
opts Cache
test func(t *testing.T)
}{
{
name: "disable-1",
opts: Cache{
Dir: "some-dir",
TTL: "0",
},
test: func(t *testing.T) {
c := cache.GetManager().GetCache("test-disable-1", "v-disable-1")
err := c.Write("key-disable-1", strings.NewReader("some-value-disable-1"))
require.NoError(t, err)
rdr, err := c.Read("key-disable-1")
require.Nil(t, rdr)
require.Error(t, err)
},
},
{
name: "disable-2",
opts: Cache{
Dir: "some-dir",
TTL: "0s",
},
test: func(t *testing.T) {
c := cache.GetManager().GetCache("test-disable-2", "v-disable-2")
err := c.Write("key-disable-2", strings.NewReader("some-value-disable-2"))
require.NoError(t, err)
rdr, err := c.Read("key-disable-2")
require.Nil(t, rdr)
require.Error(t, err)
},
},
{
name: "disable-3",
opts: Cache{
Dir: "some-dir",
TTL: "0d",
},
test: func(t *testing.T) {
c := cache.GetManager().GetCache("test-disable-3", "v-disable-3")
err := c.Write("key-disable-3", strings.NewReader("some-value-disable-3"))
require.NoError(t, err)
rdr, err := c.Read("key-disable-3")
require.Nil(t, rdr)
require.Error(t, err)
},
},
{
name: "in-memory",
opts: Cache{
Dir: "",
TTL: "10m",
},
test: func(t *testing.T) {
c := cache.GetManager().GetCache("test-mem", "v-mem")
err := c.Write("key-mem", strings.NewReader("some-value-mem"))
require.NoError(t, err)
rdr, err := c.Read("key-mem")
require.NotNil(t, rdr)
defer internal.CloseAndLogError(rdr, "")
require.NoError(t, err)
data, err := io.ReadAll(rdr)
require.NoError(t, err)
require.Equal(t, "some-value-mem", string(data))
require.NoDirExists(t, filepath.Join("test-mem", "v-mem"))
},
},
{
name: "on disk",
opts: Cache{
Dir: tmp,
TTL: "10m",
},
test: func(t *testing.T) {
c := cache.GetManager().GetCache("test-disk", "v-disk")
err := c.Write("key-disk", strings.NewReader("some-value-disk"))
require.NoError(t, err)
rdr, err := c.Read("key-disk")
require.NotNil(t, rdr)
defer internal.CloseAndLogError(rdr, "")
require.NoError(t, err)
data, err := io.ReadAll(rdr)
require.NoError(t, err)
require.Equal(t, "some-value-disk", string(data))
require.DirExists(t, filepath.Join(tmp, "test-disk", "v-disk"))
},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
original := cache.GetManager()
defer cache.SetManager(original)
err := test.opts.PostLoad()
require.NoError(t, err)
test.test(t)
})
}
}
func Test_parseDuration(t *testing.T) {
tests := []struct {
duration string
expect time.Duration
err require.ErrorAssertionFunc
}{
{
duration: "0d",
expect: 0,
},
{
duration: "0m",
expect: 0,
},
{
duration: "0s",
expect: 0,
},
{
duration: "0",
expect: 0,
},
{
duration: "1d",
expect: 24 * time.Hour,
},
{
duration: "7d",
expect: 7 * 24 * time.Hour,
},
{
duration: "365D",
expect: 365 * 24 * time.Hour,
},
{
duration: "7d1h1m1s",
expect: 7*24*time.Hour + time.Hour + time.Minute + time.Second,
},
{
duration: "7d 1h 1m 1s",
expect: 7*24*time.Hour + time.Hour + time.Minute + time.Second,
},
{
duration: "2h",
expect: 2 * time.Hour,
},
{
duration: "2h5m",
expect: 2*time.Hour + 5*time.Minute,
},
{
duration: "2h 5m",
expect: 2*time.Hour + 5*time.Minute,
},
{
duration: "d24h",
err: require.Error,
},
}
for _, test := range tests {
t.Run(test.duration, func(t *testing.T) {
got, err := parseDuration(test.duration)
if test.err != nil {
test.err(t, err)
return
} else {
require.NoError(t, err)
}
require.Equal(t, test.expect, got)
})
}
}
func Test_durationToString(t *testing.T) {
tests := []struct {
duration time.Duration
expect string
err require.ErrorAssertionFunc
}{
{
expect: "1d",
duration: 24 * time.Hour,
},
{
expect: "7d",
duration: 7 * 24 * time.Hour,
},
{
expect: "7d1h1m1s",
duration: 7*24*time.Hour + time.Hour + time.Minute + time.Second,
},
{
expect: "2h0m0s",
duration: 2 * time.Hour,
},
{
expect: "2h5m0s",
duration: 2*time.Hour + 5*time.Minute,
},
}
for _, test := range tests {
t.Run(test.expect, func(t *testing.T) {
got := durationToString(test.duration)
require.Equal(t, test.expect, got)
})
}
}
-3
View File
@@ -64,7 +64,6 @@ func DefaultCatalog() Catalog {
Package: defaultPackageConfig(),
LinuxKernel: defaultLinuxKernelConfig(),
Golang: defaultGolangConfig(),
Java: defaultJavaConfig(),
File: defaultFileConfig(),
Relationships: defaultRelationshipsConfig(),
Source: defaultSourceConfig(),
@@ -151,8 +150,6 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
},
JavaArchive: java.DefaultArchiveCatalogerConfig().
WithUseMavenLocalRepository(cfg.Java.UseMavenLocalRepository).
WithMavenLocalRepositoryDir(cfg.Java.MavenLocalRepositoryDir).
WithUseNetwork(cfg.Java.UseNetwork).
WithMavenBaseURL(cfg.Java.MavenURL).
WithArchiveTraversal(archiveSearch, cfg.Java.MaxParentRecursiveDepth),
+8 -30
View File
@@ -1,46 +1,24 @@
package options
import (
"github.com/anchore/clio"
"github.com/anchore/syft/syft/pkg/cataloger/java"
)
import "github.com/anchore/clio"
type javaConfig struct {
UseNetwork bool `yaml:"use-network" json:"use-network" mapstructure:"use-network"`
UseMavenLocalRepository bool `yaml:"use-maven-local-repository" json:"use-maven-local-repository" mapstructure:"use-maven-local-repository"`
MavenLocalRepositoryDir string `yaml:"maven-local-repository-dir" json:"maven-local-repository-dir" mapstructure:"maven-local-repository-dir"`
MavenURL string `yaml:"maven-url" json:"maven-url" mapstructure:"maven-url"`
MaxParentRecursiveDepth int `yaml:"max-parent-recursive-depth" json:"max-parent-recursive-depth" mapstructure:"max-parent-recursive-depth"`
}
func defaultJavaConfig() javaConfig {
def := java.DefaultArchiveCatalogerConfig()
return javaConfig{
UseNetwork: def.UseNetwork,
MaxParentRecursiveDepth: def.MaxParentRecursiveDepth,
UseMavenLocalRepository: def.UseMavenLocalRepository,
MavenLocalRepositoryDir: def.MavenLocalRepositoryDir,
MavenURL: def.MavenBaseURL,
}
}
var _ interface {
clio.FieldDescriber
} = (*javaConfig)(nil)
func (o *javaConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
descriptions.Add(&o.UseNetwork, `enables Syft to use the network to fetch version and license information for packages when
a parent or imported pom file is not found in the local maven repository.
the pom files are downloaded from the remote Maven repository at 'maven-url'`)
descriptions.Add(&o.UseNetwork, `enables Syft to use the network to fill in more detailed information about artifacts
currently this enables searching maven-url for license data
when running across pom.xml files that could have more information, syft will
explicitly search maven for license information by querying the online pom when this is true
this option is helpful for when the parent pom has more data,
that is not accessible from within the final built artifact`)
descriptions.Add(&o.MavenURL, `maven repository to use, defaults to Maven central`)
descriptions.Add(&o.MaxParentRecursiveDepth, `depth to recursively resolve parent POMs, no limit if <= 0`)
descriptions.Add(&o.UseMavenLocalRepository, `use the local Maven repository to retrieve pom files. When Maven is installed and was previously used
for building the software that is being scanned, then most pom files will be available in this
repository on the local file system. this greatly speeds up scans. when all pom files are available
in the local repository, then 'use-network' is not needed.
TIP: If you want to download all required pom files to the local repository without running a full
build, run 'mvn help:effective-pom' before performing the scan with syft.`)
descriptions.Add(&o.MavenLocalRepositoryDir, `override the default location of the local Maven repository.
the default is the subdirectory '.m2/repository' in your home directory`)
descriptions.Add(&o.MaxParentRecursiveDepth, `depth to recursively resolve parent POMs`)
}
@@ -35,7 +35,7 @@ var imageOnlyTestCases = []testCase{
pkgType: pkg.PythonPkg,
pkgLanguage: pkg.Python,
pkgInfo: map[string]string{
"pygments": "2.6.1",
"Pygments": "2.6.1",
"requests": "2.22.0",
"somerequests": "3.22.0",
"someotherpkg": "3.19.0",
@@ -172,7 +172,7 @@ var dirOnlyTestCases = []testCase{
"passlib": "1.7.2",
"mypy": "v0.770",
// common to image and directory
"pygments": "2.6.1",
"Pygments": "2.6.1",
"requests": "2.22.0",
"somerequests": "3.22.0",
"someotherpkg": "3.19.0",
@@ -388,14 +388,6 @@ var dirOnlyTestCases = []testCase{
"swift-numerics": "1.0.2",
},
},
{
name: "find swipl pack package manager packages",
pkgType: pkg.SwiplPackPkg,
pkgLanguage: pkg.Swipl,
pkgInfo: map[string]string{
"hdt": "0.5.2",
},
},
{
name: "find github action packages (from usage in workflow files and composite actions)",
pkgType: pkg.GithubActionPkg,
@@ -51,7 +51,6 @@ func TestPkgCoverageImage(t *testing.T) {
definedLanguages.Remove(pkg.Rust.String())
definedLanguages.Remove(pkg.Dart.String())
definedLanguages.Remove(pkg.Swift.String())
definedLanguages.Remove(pkg.Swipl.String())
definedLanguages.Remove(pkg.CPP.String())
definedLanguages.Remove(pkg.Haskell.String())
definedLanguages.Remove(pkg.Elixir.String())
@@ -77,7 +76,6 @@ func TestPkgCoverageImage(t *testing.T) {
definedPkgs.Remove(string(pkg.LinuxKernelPkg))
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
definedPkgs.Remove(string(pkg.SwiftPkg))
definedPkgs.Remove(string(pkg.SwiplPackPkg))
definedPkgs.Remove(string(pkg.GithubActionPkg))
definedPkgs.Remove(string(pkg.GithubActionWorkflowPkg))
@@ -10,8 +10,7 @@ import (
func TestMarinerDistroless(t *testing.T) {
sbom, _ := catalogFixtureImage(t, "image-mariner-distroless", source.SquashedScope)
// 12 RPMs + 2 binaries with ELF package notes claiming to be RPMs
expectedPkgs := 14
expectedPkgs := 12
actualPkgs := 0
for range sbom.Artifacts.Packages.Enumerate(pkg.RpmPkg) {
actualPkgs += 1
@@ -5,7 +5,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/relationship"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/source"
)
@@ -45,13 +44,12 @@ func TestBinaryElfRelationships(t *testing.T) {
}
}
relationshipIndex := relationship.NewIndex(sbom.Relationships...)
for name, expectedDepNames := range expectedGraph {
pkgId := nameToId[name]
p := sbom.Artifacts.Packages.Package(pkgId)
require.NotNil(t, p, "expected package %q to be present in the SBOM", name)
rels := relationshipIndex.References(*p, artifact.DependencyOfRelationship)
rels := sbom.RelationshipsForPackage(*p, artifact.DependencyOfRelationship)
require.NotEmpty(t, rels, "expected package %q to have relationships", name)
toIds := map[artifact.ID]struct{}{}
@@ -1,10 +0,0 @@
name(hdt).
version('0.5.2').
% TODO: swipl_version([90121]).
title('Access RDF HDT files').
keywords(['RDF']).
author( 'Jan Wielemaker', 'J.Wielemaker@vu.nl' ).
packager( 'Jan Wielemaker', 'J.Wielemaker@vu.nl' ).
maintainer( 'Jan Wielemaker', 'J.Wielemaker@vu.nl' ).
home( 'https://github.com/JanWielemaker/hdt' ).
download( 'https://github.com/JanWielemaker/hdt/archive/V*.zip' ).
+35 -38
View File
@@ -1,9 +1,9 @@
module github.com/anchore/syft
go 1.22.0
go 1.21.0
require (
github.com/CycloneDX/cyclonedx-go v0.9.0
github.com/CycloneDX/cyclonedx-go v0.8.0
github.com/Masterminds/semver v1.5.0
github.com/Masterminds/sprig/v3 v3.2.3
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
@@ -17,29 +17,29 @@ require (
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f
github.com/anchore/stereoscope v0.0.3-0.20240725180315-50ce3be7aa1f
github.com/anchore/stereoscope v0.0.3-0.20240501181043-2e9894674185
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
// we are hinting brotli to latest due to warning when installing archiver v3:
// go: warning: github.com/andybalholm/brotli@v1.0.1: retracted by module author: occasional panics and data corruption
github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
github.com/bmatcuk/doublestar/v4 v4.6.1
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.26.6
github.com/charmbracelet/lipgloss v0.12.1
github.com/charmbracelet/bubbletea v0.26.3
github.com/charmbracelet/lipgloss v0.11.0
github.com/dave/jennifer v1.7.0
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
github.com/distribution/reference v0.6.0
github.com/docker/docker v27.1.1+incompatible
github.com/docker/docker v26.1.3+incompatible
github.com/dustin/go-humanize v1.0.1
github.com/elliotchance/phpserialize v1.4.0
github.com/facebookincubator/nvdtools v0.1.5
github.com/github/go-spdx/v2 v2.3.1
github.com/gkampitakis/go-snaps v0.5.7
github.com/github/go-spdx/v2 v2.2.0
github.com/gkampitakis/go-snaps v0.5.4
github.com/go-git/go-billy/v5 v5.5.0
github.com/go-git/go-git/v5 v5.12.0
github.com/go-test/deep v1.1.1
github.com/go-test/deep v1.1.0
github.com/google/go-cmp v0.6.0
github.com/google/go-containerregistry v0.20.1
github.com/google/go-containerregistry v0.19.1
github.com/google/licensecheck v0.3.1
github.com/google/uuid v1.6.0
github.com/gookit/color v1.5.4
@@ -55,43 +55,38 @@ require (
github.com/mitchellh/go-homedir v1.1.0
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/mitchellh/mapstructure v1.5.0
github.com/moby/sys/mountinfo v0.7.2
github.com/moby/sys/mountinfo v0.7.1
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/quasilyte/go-ruleguard/dsl v0.3.22
github.com/saferwall/pe v1.5.4
github.com/saferwall/pe v1.5.3
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d
github.com/sanity-io/litter v1.5.5
github.com/sassoftware/go-rpmutils v0.4.0
// pinned to pull in 386 arch fix: https://github.com/scylladb/go-set/commit/cc7b2070d91ebf40d233207b633e28f5bd8f03a5
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3
github.com/spdx/tools-golang v0.5.5
github.com/spdx/tools-golang v0.5.4
github.com/spf13/afero v1.11.0
github.com/spf13/cobra v1.8.1
github.com/spf13/cobra v1.8.0
github.com/stretchr/testify v1.9.0
github.com/vbatts/go-mtree v0.5.4
github.com/vbatts/go-mtree v0.5.3
github.com/vifraa/gopom v1.0.0
github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651
github.com/wagoodman/go-progress v0.0.0-20230925121702-07e42b3cdba0
github.com/xeipuuv/gojsonschema v1.2.0
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
go.uber.org/goleak v1.3.0
golang.org/x/mod v0.20.0
golang.org/x/net v0.27.0
golang.org/x/mod v0.17.0
golang.org/x/net v0.25.0
gopkg.in/yaml.v3 v3.0.1
modernc.org/sqlite v1.31.1
modernc.org/sqlite v1.29.10
)
require google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
require (
github.com/BurntSushi/toml v1.4.0
github.com/adrg/xdg v0.5.0
github.com/magiconair/properties v1.8.7
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678
)
require github.com/magiconair/properties v1.8.7
require (
dario.cat/mergo v1.0.0 // indirect
@@ -103,6 +98,7 @@ require (
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.11.4 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/adrg/xdg v0.4.0 // 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
@@ -110,7 +106,7 @@ require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/becheran/wildmatch-go v1.0.0 // indirect
github.com/charmbracelet/harmonica v0.2.0 // indirect
github.com/charmbracelet/x/ansi v0.1.4 // indirect
github.com/charmbracelet/x/ansi v0.1.1 // indirect
github.com/charmbracelet/x/input v0.1.0 // indirect
github.com/charmbracelet/x/term v0.1.1 // indirect
github.com/charmbracelet/x/windows v0.1.0 // indirect
@@ -125,7 +121,7 @@ require (
github.com/containerd/typeurl/v2 v2.1.1 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/cli v27.0.3+incompatible // indirect
github.com/docker/cli v24.0.0+incompatible // indirect
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/docker/go-connections v0.4.0 // indirect
@@ -138,7 +134,7 @@ require (
github.com/felixge/fgprof v0.9.3 // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.0 // indirect
github.com/gkampitakis/ciinfo v0.3.0 // indirect
github.com/gkampitakis/go-diff v1.3.2 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
@@ -163,7 +159,7 @@ require (
github.com/klauspost/pgzip v1.2.5 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/maruel/natural v1.1.1 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@@ -183,13 +179,13 @@ require (
github.com/muesli/termenv v0.15.2 // indirect
github.com/ncruces/go-strftime v0.1.9 // indirect
github.com/nwaples/rardecode v1.1.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/image-spec v1.1.0-rc3 // indirect
github.com/opencontainers/runc v1.1.12 // 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/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/pierrec/lz4/v4 v4.1.19 // indirect
github.com/pierrec/lz4/v4 v4.1.15 // 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
@@ -209,8 +205,8 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.18.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/sylabs/sif/v2 v2.17.1 // indirect
github.com/sylabs/squashfs v1.0.0 // indirect
github.com/sylabs/sif/v2 v2.11.5 // indirect
github.com/sylabs/squashfs v0.6.1 // indirect
github.com/therootcompany/xz v1.0.1 // indirect
github.com/tidwall/gjson v1.17.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
@@ -230,12 +226,13 @@ require (
go.opentelemetry.io/otel/trace v1.19.0 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
golang.org/x/crypto v0.25.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.22.0 // indirect
golang.org/x/term v0.22.0 // indirect
golang.org/x/text v0.16.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/term v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
golang.org/x/tools v0.19.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
google.golang.org/grpc v1.59.0 // indirect
@@ -243,7 +240,7 @@ require (
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
modernc.org/libc v1.55.3 // indirect
modernc.org/libc v1.49.3 // indirect
modernc.org/mathutil v1.6.0 // indirect
modernc.org/memory v1.8.0 // indirect
modernc.org/strutil v1.2.0 // indirect
+69 -71
View File
@@ -57,11 +57,9 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg6
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CycloneDX/cyclonedx-go v0.9.0 h1:inaif7qD8bivyxp7XLgxUYtOXWtDez7+j72qKTMQTb8=
github.com/CycloneDX/cyclonedx-go v0.9.0/go.mod h1:NE/EWvzELOFlG6+ljX/QeMlVt9VKcTwu8u0ccsACEsw=
github.com/CycloneDX/cyclonedx-go v0.8.0 h1:FyWVj6x6hoJrui5uRQdYZcSievw3Z32Z88uYzG/0D6M=
github.com/CycloneDX/cyclonedx-go v0.8.0/go.mod h1:K2bA+324+Og0X84fA8HhN2X066K7Bxz4rpMQ4ZhjtSk=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
@@ -85,8 +83,8 @@ github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpH
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4=
github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls=
github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -113,8 +111,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f h1:B/E9ixKNCasntpoch61NDaQyGPDXLEJlL+B9B/PbdbA=
github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
github.com/anchore/stereoscope v0.0.3-0.20240725180315-50ce3be7aa1f h1:xuBvotcht1Ns8IdaC4UuYV1U8MFln9c5ELeo5bzDEO8=
github.com/anchore/stereoscope v0.0.3-0.20240725180315-50ce3be7aa1f/go.mod h1:DcQdMes8SwpFli3rDH0v+Vd9qU9Jariq7JSHNJV5X/A=
github.com/anchore/stereoscope v0.0.3-0.20240501181043-2e9894674185 h1:SuViDJ27nZ+joGdKbAkxAlm7tYMt9NTxTZZ05po4hls=
github.com/anchore/stereoscope v0.0.3-0.20240501181043-2e9894674185/go.mod h1:ckIamHiRMp8iBwWoTtE5Xkt9VQ5QC+6+O4VzwqyZr5Q=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@@ -157,14 +155,14 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
github.com/charmbracelet/bubbletea v0.26.6 h1:zTCWSuST+3yZYZnVSvbXwKOPRSNZceVeqpzOLN2zq1s=
github.com/charmbracelet/bubbletea v0.26.6/go.mod h1:dz8CWPlfCCGLFbBlTY4N7bjLiyOGDJEnd2Muu7pOWhk=
github.com/charmbracelet/bubbletea v0.26.3 h1:iXyGvI+FfOWqkB2V07m1DF3xxQijxjY2j8PqiXYqasg=
github.com/charmbracelet/bubbletea v0.26.3/go.mod h1:bpZHfDHTYJC5g+FBK+ptJRCQotRC+Dhh3AoMxa/2+3Q=
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
github.com/charmbracelet/lipgloss v0.12.1 h1:/gmzszl+pedQpjCOH+wFkZr/N90Snz40J/NR7A0zQcs=
github.com/charmbracelet/lipgloss v0.12.1/go.mod h1:V2CiwIuhx9S1S1ZlADfOj9HmxeMAORuz5izHb0zGbB8=
github.com/charmbracelet/x/ansi v0.1.4 h1:IEU3D6+dWwPSgZ6HBH+v6oUuZ/nVawMiWj5831KfiLM=
github.com/charmbracelet/x/ansi v0.1.4/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/lipgloss v0.11.0 h1:UoAcbQ6Qml8hDwSWs0Y1cB5TEQuZkDPH/ZqwWWYTG4g=
github.com/charmbracelet/lipgloss v0.11.0/go.mod h1:1UdRTH9gYgpcdNN5oBtjbu/IzNKtzVtb7sqN1t9LNn8=
github.com/charmbracelet/x/ansi v0.1.1 h1:CGAduulr6egay/YVbGc8Hsu8deMg1xZ/bkaXTPi1JDk=
github.com/charmbracelet/x/ansi v0.1.1/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/charmbracelet/x/input v0.1.0 h1:TEsGSfZYQyOtp+STIjyBq6tpRaorH0qpwZUj8DavAhQ=
github.com/charmbracelet/x/input v0.1.0/go.mod h1:ZZwaBxPF7IG8gWWzPUVqHEtWhc1+HXJPNuerJGRGZ28=
github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI=
@@ -211,7 +209,7 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
@@ -227,12 +225,12 @@ github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4i
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/cli v27.0.3+incompatible h1:usGs0/BoBW8MWxGeEtqPMkzOY56jZ6kYlSN5BLDioCQ=
github.com/docker/cli v27.0.3+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
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/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
github.com/docker/docker v27.1.1+incompatible h1:hO/M4MtV36kzKldqnA37IWhebRA+LnqqcqDja6kVaKY=
github.com/docker/docker v27.1.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v26.1.3+incompatible h1:lLCzRbrVZrljpVNobJu1J2FHk8V0s4BawoZippkc+xo=
github.com/docker/docker v26.1.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -284,17 +282,17 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gabriel-vasile/mimetype v1.4.4 h1:QjV6pZ7/XZ7ryI2KuyeEDE8wnh7fHP9YnQy+R0LnH8I=
github.com/gabriel-vasile/mimetype v1.4.4/go.mod h1:JwLei5XPtWdGiMFB5Pjle1oEeoSeEuJfJE+TtfvdB/s=
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/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/github/go-spdx/v2 v2.3.1 h1:ffGuHTbHuHzWPt53n8f9o8clGutuLPObo3zB4JAjxU8=
github.com/github/go-spdx/v2 v2.3.1/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
github.com/github/go-spdx/v2 v2.2.0 h1:yBBLMasHA70Ujd35OpL/OjJOWWVNXcJGbars0GinGRI=
github.com/github/go-spdx/v2 v2.2.0/go.mod h1:hMCrsFgT0QnCwn7G8gxy/MxMpy67WgZrwFeISTn0o6w=
github.com/gkampitakis/ciinfo v0.3.0 h1:gWZlOC2+RYYttL0hBqcoQhM7h1qNkVqvRCV1fOvpAv8=
github.com/gkampitakis/ciinfo v0.3.0/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
github.com/gkampitakis/go-snaps v0.5.7 h1:uVGjHR4t4pPHU944udMx7VKHpwepZXmvDMF+yDmI0rg=
github.com/gkampitakis/go-snaps v0.5.7/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
github.com/gkampitakis/go-snaps v0.5.4 h1:GX+dkKmVsRenz7SoTbdIEL4KQARZctkMiZ8ZKprRwT8=
github.com/gkampitakis/go-snaps v0.5.4/go.mod h1:ZABkO14uCuVxBHAXAfKG+bqNz+aa1bGPAg8jkI0Nk8Y=
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0=
github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
@@ -323,8 +321,8 @@ github.com/go-restruct/restruct v1.2.0-alpha h1:2Lp474S/9660+SJjpVxoKuWX09JsXHSr
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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
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/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@@ -384,8 +382,8 @@ github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-containerregistry v0.20.1 h1:eTgx9QNYugV4DN5mz4U8hiAGTi1ybXn0TPi4Smd8du0=
github.com/google/go-containerregistry v0.20.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
github.com/google/go-containerregistry v0.19.1 h1:yMQ62Al6/V0Z7CqIrrS1iYoA5/oQCm88DeNujc7C1KY=
github.com/google/go-containerregistry v0.19.1/go.mod h1:YCMFNQeeXeLF+dnhhWkqDItx/JSkH01j1Kis4PsjzFI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
@@ -522,9 +520,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs=
github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w=
@@ -584,8 +581,8 @@ github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3N
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg=
github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4=
github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g=
github.com/moby/sys/mountinfo v0.7.1/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=
@@ -618,8 +615,8 @@ 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/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
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/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss=
github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8=
github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w=
@@ -636,8 +633,8 @@ github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCko
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0=
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
@@ -685,8 +682,8 @@ github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99
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/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/saferwall/pe v1.5.4 h1:tLmMggEMUfeqrpJ25zS/okUQmyFdD5xWKL2+z9njCqg=
github.com/saferwall/pe v1.5.4/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ=
github.com/saferwall/pe v1.5.3 h1:tZ2KJWeJK6N2jjJx+B9hm+wq6qe0o56pql2PXUaQ9eI=
github.com/saferwall/pe v1.5.3/go.mod h1:mJx+PuptmNpoPFBNhWs/uDMFL/kTHVZIkg0d4OUJFbQ=
github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
@@ -727,8 +724,8 @@ github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9yS
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spdx/gordf v0.0.0-20201111095634-7098f93598fb/go.mod h1:uKWaldnbMnjsSAXRurWqqrdyZen1R7kxl8TkmWk2OyM=
github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk=
github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
github.com/spdx/tools-golang v0.5.4 h1:fRW4iz16P1ZCUtWStFqS6YiMgnK7WgfTFU/lrsYlvqY=
github.com/spdx/tools-golang v0.5.4/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE=
github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
@@ -738,8 +735,8 @@ github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.3.0/go.mod h1:BrRVncBjOJa/eUcVVm9CE+oC6as8k+VYr4NY7WCi9V4=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -769,10 +766,10 @@ github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/sylabs/sif/v2 v2.17.1 h1:p6Sl0LWyShXBj2SBsS1dMOMIMrZHe8pwBnBrYt6uo4M=
github.com/sylabs/sif/v2 v2.17.1/go.mod h1:XUGB6AQUXGkms3qPOPdevctT3lBLRLWZNWHVnt5HMKE=
github.com/sylabs/squashfs v1.0.0 h1:xAyMS21ogglkuR5HaY55PCfqY3H32ma9GkasTYo28Zg=
github.com/sylabs/squashfs v1.0.0/go.mod h1:rhWzvgefq1X+R+LZdts10hfMsTg3g74OfGunW8tvg/4=
github.com/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/squashfs v0.6.1 h1:4hgvHnD9JGlYWwT0bPYNt9zaz23mAV3Js+VEgQoRGYQ=
github.com/sylabs/squashfs v0.6.1/go.mod h1:ZwpbPCj0ocIvMy2br6KZmix6Gzh6fsGQcCnydMF+Kx8=
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=
@@ -794,8 +791,8 @@ github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/vbatts/go-mtree v0.5.4 h1:OMAb8jaCyiFA7zXj0Zc/oARcxBDBoeu2LizjB8BVJl0=
github.com/vbatts/go-mtree v0.5.4/go.mod h1:5GqJbVhm9BBiCc4K5uc/c42FPgXulHaQs4sFUEfIWMo=
github.com/vbatts/go-mtree v0.5.3 h1:S/jYlfG8rZ+a0bhZd+RANXejy7M4Js8fq9U+XoWTd5w=
github.com/vbatts/go-mtree v0.5.3/go.mod h1:eXsdoPMdL2jcJx6HweWi9lYQxBsTp4lNhqqAjgkZUg8=
github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck=
github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY=
github.com/vifraa/gopom v1.0.0 h1:L9XlKbyvid8PAIK8nr0lihMApJQg/12OBvMA28BcWh0=
@@ -878,8 +875,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
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.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -920,8 +917,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.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.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
golang.org/x/mod v0.17.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-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -962,14 +959,15 @@ golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLd
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210505024714-0287a6fb4125/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.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.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1081,15 +1079,15 @@ 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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
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.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1102,8 +1100,8 @@ 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.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.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1165,8 +1163,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.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.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1359,18 +1357,18 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
modernc.org/cc/v4 v4.21.4 h1:3Be/Rdo1fpr8GrQ7IVw9OHtplU4gWbb+wNgeoBMmGLQ=
modernc.org/cc/v4 v4.21.4/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.19.2 h1:lwQZgvboKD0jBwdaeVCTouxhxAyN6iawF3STraAal8Y=
modernc.org/ccgo/v4 v4.19.2/go.mod h1:ysS3mxiMV38XGRTTcgo0DQTeTmAO4oCmJl1nX9VFI3s=
modernc.org/cc/v4 v4.20.0 h1:45Or8mQfbUqJOG9WaxvlFYOAQO0lQ5RvqBcFCXngjxk=
modernc.org/cc/v4 v4.20.0/go.mod h1:HM7VJTZbUCR3rV8EYBi9wxnJ0ZBRiGE5OeGXNA0IsLQ=
modernc.org/ccgo/v4 v4.16.0 h1:ofwORa6vx2FMm0916/CkZjpFPSR70VwTjUCe2Eg5BnA=
modernc.org/ccgo/v4 v4.16.0/go.mod h1:dkNyWIjFrVIZ68DTo36vHK+6/ShBn4ysU61So6PIqCI=
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
modernc.org/gc/v2 v2.4.1 h1:9cNzOqPyMJBvrUipmynX0ZohMhcxPtMccYgGOJdOiBw=
modernc.org/gc/v2 v2.4.1/go.mod h1:wzN5dK1AzVGoH6XOzc3YZ+ey/jPgYHLuVckd62P0GYU=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI=
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4=
modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U=
modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w=
modernc.org/libc v1.49.3 h1:j2MRCRdwJI2ls/sGbeSk0t2bypOG/uvPZUsGQFDulqg=
modernc.org/libc v1.49.3/go.mod h1:yMZuGkn7pXbKfoT/M35gFJOAEdSKdxL0q64sF7KqCDo=
modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E=
@@ -1379,8 +1377,8 @@ modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/sortutil v1.2.0 h1:jQiD3PfS2REGJNzNCMMaLSp/wdMNieTbKX920Cqdgqc=
modernc.org/sortutil v1.2.0/go.mod h1:TKU2s7kJMf1AE84OoiGppNHJwvB753OYfNl2WRb++Ss=
modernc.org/sqlite v1.31.1 h1:XVU0VyzxrYHlBhIs1DiEgSl0ZtdnPtbLVy8hSkzxGrs=
modernc.org/sqlite v1.31.1/go.mod h1:UqoylwmTb9F+IqXERT8bW9zzOWN8qwAIcLdzeBZs4hA=
modernc.org/sqlite v1.29.10 h1:3u93dz83myFnMilBGCOLbr+HjklS6+5rJLx4q86RDAg=
modernc.org/sqlite v1.29.10/go.mod h1:ItX2a1OVGgNsFh6Dv60JQvGfJfTPHPVpV6DF59akYOA=
modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA=
modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0=
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
+53 -220
View File
@@ -2,26 +2,34 @@
# note: we require errors to propagate (don't set -e)
set -u
PROJECT_NAME=syft
PROJECT_NAME="syft"
OWNER=anchore
REPO="${PROJECT_NAME}"
GITHUB_DOWNLOAD_PREFIX=https://github.com/${OWNER}/${REPO}/releases/download
INSTALL_SH_BASE_URL=https://raw.githubusercontent.com/${OWNER}/${PROJECT_NAME}
PROGRAM_ARGS=$@
# signature verification options
# the location to the cosign binary (allowed to be overridden by the user)
COSIGN_BINARY=${COSIGN_BINARY:-cosign}
VERIFY_SIGN=false
# this is the earliest tag in the repo where cosign sign-blob was introduced in the release process (see the goreleaser config)
VERIFY_SIGN_SUPPORTED_VERSION=v0.104.0
# this is the earliest tag in the repo where the -v flag was introduced to this install.sh script
VERIFY_SIGN_FLAG_VERSION=v1.6.0
# do not change the name of this parameter (this must always be backwards compatible)
DOWNLOAD_TAG_INSTALL_SCRIPT=${DOWNLOAD_TAG_INSTALL_SCRIPT:-true}
#
# usage [script-name]
#
usage() (
this=$1
cat <<EOF
$this: download go binaries for anchore/syft
Usage: $this [-b] dir [-d] [tag]
-b the installation directory (dDefaults to ./bin)
-d turns on debug logging
-dd turns on trace logging
[tag] the specific release to use (if missing, then the latest will be used)
EOF
exit 2
)
# ------------------------------------------------------------------------
# https://github.com/client9/shlib - portable posix shell functions
# Public domain - http://unlicense.org
@@ -262,21 +270,21 @@ hash_sha256() (
)
hash_sha256_verify() (
target=$1
TARGET=$1
checksums=$2
if [ -z "$checksums" ]; then
log_err "hash_sha256_verify checksum file not specified as argument"
log_err "hash_sha256_verify checksum file not specified in arg2"
return 1
fi
target_basename=${target##*/}
want=$(grep "${target_basename}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
BASENAME=${TARGET##*/}
want=$(grep "${BASENAME}" "${checksums}" 2>/dev/null | tr '\t' ' ' | cut -d ' ' -f 1)
if [ -z "$want" ]; then
log_err "hash_sha256_verify unable to find checksum for '${target}' in '${checksums}'"
log_err "hash_sha256_verify unable to find checksum for '${TARGET}' in '${checksums}'"
return 1
fi
got=$(hash_sha256 "$target")
got=$(hash_sha256 "$TARGET")
if [ "$want" != "$got" ]; then
log_err "hash_sha256_verify checksum for '$target' did not verify ${want} vs $got"
log_err "hash_sha256_verify checksum for '$TARGET' did not verify ${want} vs $got"
return 1
fi
)
@@ -350,69 +358,28 @@ github_release_tag() (
echo "$tag"
)
# github_release_asset_url [release-url-prefix] [name] [version] [output-dir] [filename]
#
# outputs the url to the release asset
#
github_release_asset_url() (
download_url="$1"
name="$2"
version="$3"
filename="$4"
complete_filename="${name}_${version}_${filename}"
complete_url="${download_url}/${complete_filename}"
echo "${complete_url}"
)
# download_github_release_checksums_files [release-url-prefix] [name] [version] [output-dir] [filename]
#
# outputs path to the downloaded checksums related file
#
download_github_release_checksums_files() (
download_url="$1"
name="$2"
version="$3"
output_dir="$4"
filename="$5"
log_trace "download_github_release_checksums_files(url=${download_url}, name=${name}, version=${version}, output_dir=${output_dir}, filename=${filename})"
complete_filename="${name}_${version}_${filename}"
complete_url=$(github_release_asset_url "${download_url}" "${name}" "${version}" "${filename}")
output_path="${output_dir}/${complete_filename}"
http_download "${output_path}" "${complete_url}" ""
asset_file_exists "${output_path}"
log_trace "download_github_release_checksums_files() returned '${output_path}' for file '${complete_filename}'"
echo "${output_path}"
)
# download_github_release_checksums [release-url-prefix] [name] [version] [output-dir]
#
# outputs path to the downloaded checksums file
#
download_github_release_checksums() (
download_github_release_checksums_files "$@" "checksums.txt"
)
download_url="$1"
name="$2"
version="$3"
output_dir="$4"
# github_release_checksums_sig_url [release-url-prefix] [name] [version]
#
# outputs the url to the release checksums signature file
#
github_release_checksums_sig_url() (
github_release_asset_url "$@" "checksums.txt.sig"
)
log_trace "download_github_release_checksums(url=${download_url}, name=${name}, version=${version}, output_dir=${output_dir})"
# github_release_checksums_cert_url [release-url-prefix] [name] [version]
#
# outputs the url to the release checksums certificate file
#
github_release_checksums_cert_url() (
github_release_asset_url "$@" "checksums.txt.pem"
checksum_filename=${name}_${version}_checksums.txt
checksum_url=${download_url}/${checksum_filename}
output_path="${output_dir}/${checksum_filename}"
http_download "${output_path}" "${checksum_url}" ""
asset_file_exists "${output_path}"
log_trace "download_github_release_checksums() returned '${output_path}'"
echo "${output_path}"
)
# search_for_asset [checksums-file-path] [name] [os] [arch] [format]
@@ -568,10 +535,7 @@ download_and_install_asset() (
format="$8"
binary="$9"
if ! asset_filepath=$(download_asset "${download_url}" "${download_path}" "${name}" "${os}" "${arch}" "${version}" "${format}"); then
log_err "could not download asset for os='${os}' arch='${arch}' format='${format}'"
return 1
fi
asset_filepath=$(download_asset "${download_url}" "${download_path}" "${name}" "${os}" "${arch}" "${version}" "${format}")
# don't continue if we couldn't download an asset
if [ -z "${asset_filepath}" ]; then
@@ -582,36 +546,6 @@ download_and_install_asset() (
install_asset "${asset_filepath}" "${install_path}" "${binary}"
)
# verify_sign [checksums-file-path] [certificate-reference] [signature-reference] [version]
#
# attempts verify the signature of the checksums file from the release workflow in Github Actions run against the main branch.
#
verify_sign() {
checksums_file=$1
cert_reference=$2
sig_reference=$3
log_trace "verifying artifact $1"
log_file=$(mktemp)
${COSIGN_BINARY} \
verify-blob "$checksums_file" \
--certificate "$cert_reference" \
--signature "$sig_reference" \
--certificate-identity "https://github.com/${OWNER}/${REPO}/.github/workflows/release.yaml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" > "${log_file}" 2>&1
if [ $? -ne 0 ]; then
log_err "$(cat "${log_file}")"
rm -f "${log_file}"
return 1
fi
rm -f "${log_file}"
}
# download_asset [release-url-prefix] [download-path] [name] [os] [arch] [version] [format] [binary]
#
# outputs the path to the downloaded asset asset_filepath
@@ -638,20 +572,6 @@ download_asset() (
return 1
fi
if [ "$VERIFY_SIGN" = true ]; then
checksum_sig_file_url=$(github_release_checksums_sig_url "${download_url}" "${name}" "${version}")
log_trace "checksums signature url: ${checksum_sig_file_url}"
checksums_cert_file_url=$(github_release_checksums_cert_url "${download_url}" "${name}" "${version}")
log_trace "checksums certificate url: ${checksums_cert_file_url}"
if ! verify_sign "${checksums_filepath}" "${checksums_cert_file_url}" "${checksum_sig_file_url}"; then
log_err "signature verification failed"
return 1
fi
log_info "signature verification succeeded"
fi
asset_url="${download_url}/${asset_filename}"
asset_filepath="${destination}/${asset_filename}"
http_download "${asset_filepath}" "${asset_url}" ""
@@ -689,79 +609,6 @@ install_asset() (
install "${archive_dir}/${binary}" "${destination}/"
)
# compare two semver strings. Returns 0 if version1 >= version2, 1 otherwise.
# Note: pre-release (-) and metadata (+) are not supported.
compare_semver() {
# remove leading 'v' if present
version1=${1#v}
version2=${2#v}
IFS=. read -r major1 minor1 patch1 <<EOF
$version1
EOF
IFS=. read -r major2 minor2 patch2 <<EOF
$version2
EOF
if [ "$major1" -gt "$major2" ]; then
return 0
elif [ "$major1" -lt "$major2" ]; then
return 1
fi
if [ "$minor1" -gt "$minor2" ]; then
return 0
elif [ "$minor1" -lt "$minor2" ]; then
return 1
fi
if [ "$patch1" -gt "$patch2" ]; then
return 0
elif [ "$patch1" -lt "$patch2" ]; then
return 1
fi
# versions are equal
return 0
}
prep_signature_verification() {
version="$1"
if [ "$VERIFY_SIGN" != true ]; then
return 0
fi
# is there any cryptographic material produced at release that we can use for signature verification?
if ! compare_semver "$version" "$VERIFY_SIGN_SUPPORTED_VERSION"; then
log_err "${PROJECT_NAME} release '$version' does not support signature verification"
log_err "you can still install ${PROJECT_NAME} by removing the -v flag or using a release that supports signature verification (>= '$VERIFY_SIGN_SUPPORTED_VERSION')"
log_err "aborting installation"
return 1
else
log_trace "${PROJECT_NAME} release '$version' supports signature verification (>= '$VERIFY_SIGN_SUPPORTED_VERSION')"
fi
# will invoking an earlier version of this script work (considering the -v flag)?
if ! compare_semver "$version" "$VERIFY_SIGN_FLAG_VERSION"; then
# the -v argument did not always exist, so we cannot be guaranteed that invoking an earlier version of this script
# will work (error with "illegal option -v"). However, the user requested signature verification, so we will
# attempt to install the application with this version of the script (keeping signature verification).
DOWNLOAD_TAG_INSTALL_SCRIPT=false
log_debug "provided version install script does not support -v flag (>= '$VERIFY_SIGN_FLAG_VERSION'), using current script for installation"
else
log_trace "provided version install script supports -v flag (>= '$VERIFY_SIGN_FLAG_VERSION')"
fi
# check to see if the cosign binary is installed
if is_command "${COSIGN_BINARY}"; then
log_trace "${COSIGN_BINARY} binary is installed"
else
log_err "signature verification is requested but ${COSIGN_BINARY} binary is not installed (see https://docs.sigstore.dev/system_config/installation/ to install it)"
return 1
fi
}
main() (
# parse arguments
@@ -769,7 +616,7 @@ main() (
install_dir=${install_dir:-./bin}
# note: never change the program flags or arguments (this must always be backwards compatible)
while getopts "b:dvh?x" arg; do
while getopts "b:dh?x" arg; do
case "$arg" in
b) install_dir="$OPTARG" ;;
d)
@@ -781,37 +628,25 @@ main() (
log_set_priority $log_trace_priority
fi
;;
v) VERIFY_SIGN=true;;
h | \?)
cat <<EOF
Download and install a released binary for ${OWNER}/${REPO} from the github releases page
Usage: $0 [-v] [-b DIR] [-d] [TAG]
-b DIR the installation directory (defaults to ./bin)
-d turns on debug logging
-dd turns on trace logging
-v verify checksum signature (requires cosign binary to be installed).
TAG the specific release to use (if missing, then the latest will be used)
EOF
exit 0
;;
h | \?) usage "$0" ;;
x) set -x ;;
esac
done
shift $((OPTIND - 1))
set +u
tag=$1
if [ -z "${tag}" ]; then
log_info "checking github for the current release tag"
log_debug "checking github for the current release tag"
tag=""
else
log_info "checking github for release tag='${tag}'"
log_debug "checking github for release tag='${tag}'"
fi
set -u
if ! tag=$(get_release_tag "${OWNER}" "${REPO}" "${tag}"); then
tag=$(get_release_tag "${OWNER}" "${REPO}" "${tag}")
if [ "$?" != "0" ]; then
log_err "unable to find tag='${tag}'"
log_err "do not specify a version or select a valid version from https://github.com/${OWNER}/${REPO}/releases"
return 1
@@ -826,10 +661,6 @@ EOF
binary=$(get_binary_name "${os}" "${arch}" "${PROJECT_NAME}")
download_url="${GITHUB_DOWNLOAD_PREFIX}/${tag}"
if ! prep_signature_verification "$version"; then
return 1
fi
# we always use the install.sh script that is associated with the tagged release. Why? the latest install.sh is not
# guaranteed to be able to install every version of the application. We use the DOWNLOAD_TAG_INSTALL_SCRIPT env var
# to indicate if we should continue processing with the existing script or to download the script from the given tag.
@@ -847,8 +678,10 @@ EOF
log_debug "downloading files into ${download_dir}"
download_and_install_asset "${download_url}" "${download_dir}" "${install_dir}" "${PROJECT_NAME}" "${os}" "${arch}" "${version}" "${format}" "${binary}"
# don't continue if we couldn't install the asset
if ! download_and_install_asset "${download_url}" "${download_dir}" "${install_dir}" "${PROJECT_NAME}" "${os}" "${arch}" "${version}" "${format}" "${binary}"; then
if [ "$?" != "0" ]; then
log_err "failed to install ${PROJECT_NAME}"
return 1
fi
@@ -864,4 +697,4 @@ if [ -z "${TEST_INSTALL_SH}" ]; then
main "$@"
exit $?
fi
set -u
set -u
-51
View File
@@ -1,51 +0,0 @@
# Caching
All caches are created from a global `manager`. By defaut this is a `bypassedCache`, which performs no caching.
One benefit of this is that tests don't need to worry about caching causing issues unless they explicitly need
to test the cache and can opt-in using the `cache.TestCache(t)` helper.
Syft sets a `filesystemCache` when the [cache options](../../cmd/syft/internal/options/cache.go) are loaded.
When using the `filesystemCache` all items are stored on disk under a root directory, generally in the form of:
```
<rootDir>/<named-cache>/<data-version>/path/to/data
```
# Using the cache
The easiest and preferred method to use the cache is a `cache.Resolver`, which automatically creates a `<data-version>`
based on the _structure_ of the provided type.
If the structure changes in any way it will end up with a new version key and all will re populate this new key,
ignoring cached values from older, different versions.
The resolver will store items using the `json` package to serialize/deserialize values, so to save space
it is encouraged to use `omitempty`. For example:
```go
type myCacheItem struct {
Name string `json:"name",omitempty`
}
```
It is possible to use core types such as `pkg.Package` as long as they support the standard `json` serialization,
but this is discouraged in order to decouple changes to them from affecting the information stored in the cache.
To get a cache for this type:
```go
resolver := cache.GetResolver[myCacheItem]("myCacheName", "v1")
```
Using the `resolver` is a single call, which manages checking for items in the cache, expiry times,
and if not found invoking the callback to populate the cache and return a value:
```go
data := resolver.Resolve("some/cache/key", func() (myCacheItem, error) {
// do things to return a myCacheItem or error
})
```
If it is common that checking for an item will result in errors, and you do not want to re-run the resolve function
when errors are encountered, instead of using `GetResolver`, you can use `GetResolverCachingErrors`, which is useful
for things such as resolving artifacts over a network, where a number of them will not be resolved, and you do not want
to continue to have the expense of running the network resolution. This should be used when it is acceptable a network
outage and cached errors is an acceptable risk.
An example can be seen in the [golang cataloger](../../syft/pkg/cataloger/golang/licenses.go) fetching remote licenses.
-24
View File
@@ -1,24 +0,0 @@
package cache
import "io"
type bypassedCache struct{}
func (b *bypassedCache) Read(_ string) (ReaderAtCloser, error) {
return nil, errNotFound
}
func (b *bypassedCache) Write(_ string, contents io.Reader) error {
if closer, ok := contents.(io.Closer); ok {
_ = closer.Close()
}
return nil
}
func (b *bypassedCache) GetCache(_, _ string) Cache {
return b
}
func (b *bypassedCache) RootDirs() []string {
return nil
}
-18
View File
@@ -1,18 +0,0 @@
package cache
import (
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func Test_bypassedCache(t *testing.T) {
m := bypassedCache{}
cache := m.GetCache("name", "version")
err := cache.Write("test", strings.NewReader("value"))
require.NoError(t, err)
rdr, err := cache.Read("test")
require.Nil(t, rdr)
require.ErrorIs(t, err, errNotFound)
}
-49
View File
@@ -1,49 +0,0 @@
package cache
import (
"io"
)
// Manager is responsible for managing cache data and instantiating all caches
type Manager interface {
// GetCache returns a cache scoped to the given named, versioned data
GetCache(name, version string) Cache
// RootDirs returns any root directories this cache manager uses
RootDirs() []string
}
// ReaderAtCloser is an amalgamation of: io.Reader, io.ReaderAt, and io.Closer
type ReaderAtCloser interface {
io.Reader
io.ReaderAt
io.Closer
}
// Cache is what the application interacts with to get and set cached data
type Cache interface {
// Read returns a reader for the cache value, if found and not expired
// or errors when unable to find / expired
Read(key string) (ReaderAtCloser, error)
// Write writes the contents of the reader to the cache
// and closes it, if the reader implements io.Closer
Write(key string, contents io.Reader) error
}
// GetManager returns the global cache manager, which is used to instantiate all caches
func GetManager() Manager {
return manager
}
// SetManager sets the global cache manager, which is used to instantiate all caches.
// Setting this to nil disables caching.
func SetManager(m Manager) {
if m == nil {
manager = &bypassedCache{}
} else {
manager = m
}
}
var manager Manager = &bypassedCache{}
-32
View File
@@ -1,32 +0,0 @@
package cache
import (
"testing"
"time"
"github.com/stretchr/testify/require"
)
func Test_SetManager(t *testing.T) {
original := GetManager()
defer SetManager(original)
SetManager(nil)
require.NotNil(t, GetManager())
require.IsType(t, &bypassedCache{}, GetManager())
SetManager(NewInMemory(0))
require.NotNil(t, GetManager())
require.IsType(t, &bypassedCache{}, GetManager())
SetManager(NewInMemory(1 * time.Hour))
require.NotNil(t, GetManager())
require.IsType(t, &filesystemCache{}, GetManager())
SetManager(nil)
require.NotNil(t, GetManager())
require.IsType(t, &bypassedCache{}, GetManager())
}
-40
View File
@@ -1,40 +0,0 @@
package cache
import "fmt"
// GetResolverCachingErrors returns a Resolver that caches errors and will return them
// instead of continuing to call the provided resolve functions
func GetResolverCachingErrors[T any](name, version string) Resolver[T] {
return &errorResolver[T]{
resolver: GetResolver[errResponse[T]](name, version),
}
}
type errResponse[T any] struct {
Error string `json:"err,omitempty"`
Value T `json:"val,omitempty"`
}
type errorResolver[T any] struct {
resolver Resolver[errResponse[T]]
}
func (r *errorResolver[T]) Resolve(key string, resolver resolverFunc[T]) (T, error) {
v, err := r.resolver.Resolve(key, func() (errResponse[T], error) {
v, err := resolver()
out := errResponse[T]{
Value: v,
}
if err != nil {
out.Error = err.Error()
}
return out, nil
})
if err != nil {
return v.Value, err
}
if v.Error != "" {
return v.Value, fmt.Errorf(v.Error)
}
return v.Value, nil
}
-47
View File
@@ -1,47 +0,0 @@
package cache
import (
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func Test_errorResolver(t *testing.T) {
original := GetManager()
defer SetManager(original)
SetManager(NewInMemory(time.Hour))
resolver := GetResolverCachingErrors[string]("theCache", "theVersion")
resolveCount := 0
resolveFunc := func() (string, error) {
resolveCount++
return "theValue", nil
}
val, err := resolver.Resolve("theKey", resolveFunc)
require.NoError(t, err)
require.Equal(t, 1, resolveCount)
require.Equal(t, "theValue", val)
val, err = resolver.Resolve("theKey", resolveFunc)
require.NoError(t, err)
require.Equal(t, 1, resolveCount)
require.Equal(t, "theValue", val)
errorCount := 0
errorFunc := func() (string, error) {
errorCount++
return "", fmt.Errorf("an error")
}
_, err = resolver.Resolve("errorValue", errorFunc)
require.ErrorContains(t, err, "an error")
require.Equal(t, 1, errorCount)
_, err = resolver.Resolve("errorValue", errorFunc)
require.ErrorContains(t, err, "an error")
require.Equal(t, 1, errorCount)
}
-120
View File
@@ -1,120 +0,0 @@
package cache
import (
"errors"
"fmt"
"io"
"net/url"
"os"
"path/filepath"
"regexp"
"strings"
"time"
"github.com/spf13/afero"
"github.com/anchore/syft/internal/log"
)
// NewFromDir creates a new cache manager which returns caches stored on disk, rooted at the given directory
func NewFromDir(dir string, ttl time.Duration) (Manager, error) {
dir = filepath.Clean(dir)
fsys, err := subFs(afero.NewOsFs(), dir)
if err != nil {
return nil, err
}
return &filesystemCache{
dir: dir,
fs: fsys,
ttl: ttl,
}, nil
}
const filePermissions = 0700
const dirPermissions = os.ModeDir | filePermissions
type filesystemCache struct {
dir string
fs afero.Fs
ttl time.Duration
}
func (d *filesystemCache) GetCache(name, version string) Cache {
fsys, err := subFs(d.fs, name, version)
if err != nil {
log.Warnf("error getting cache for: %s/%s: %v", name, version, err)
return &bypassedCache{}
}
return &filesystemCache{
dir: filepath.Join(d.dir, name, version),
fs: fsys,
ttl: d.ttl,
}
}
func (d *filesystemCache) RootDirs() []string {
if d.dir == "" {
return nil
}
return []string{d.dir}
}
func (d *filesystemCache) Read(key string) (ReaderAtCloser, error) {
path := makeDiskKey(key)
f, err := d.fs.Open(path)
if err != nil {
log.Tracef("no cache entry for %s %s: %v", d.dir, key, err)
return nil, errNotFound
} else if stat, err := f.Stat(); err != nil || stat == nil || time.Since(stat.ModTime()) > d.ttl {
log.Tracef("cache entry is too old for %s %s", d.dir, key)
return nil, errExpired
}
log.Tracef("using cache for %s %s", d.dir, key)
return f, nil
}
func (d *filesystemCache) Write(key string, contents io.Reader) error {
path := makeDiskKey(key)
return afero.WriteReader(d.fs, path, contents)
}
// subFs returns a writable directory with the given name under the root cache directory returned from findRoot,
// the directory will be created if it does not exist
func subFs(fsys afero.Fs, subDirs ...string) (afero.Fs, error) {
dir := filepath.Join(subDirs...)
dir = filepath.Clean(dir)
stat, err := fsys.Stat(dir)
if errors.Is(err, afero.ErrFileNotFound) {
err = fsys.MkdirAll(dir, dirPermissions)
if err != nil {
return nil, fmt.Errorf("unable to create directory at '%s': %v", dir, err)
}
stat, err = fsys.Stat(dir)
if err != nil {
return nil, err
}
}
if err != nil || stat == nil || !stat.IsDir() {
return nil, fmt.Errorf("unable to verify directory '%s': %v", dir, err)
}
fsys = afero.NewBasePathFs(fsys, dir)
return fsys, err
}
var keyReplacer = regexp.MustCompile("[^-._/a-zA-Z0-9]")
// makeDiskKey makes a safe sub-path but not escape forward slashes, this allows for logical partitioning on disk
func makeDiskKey(key string) string {
// encode single dot directory
if key == "." {
return "%2E"
}
// replace any disallowed chars with encoded form
key = keyReplacer.ReplaceAllStringFunc(key, url.QueryEscape)
// allow . in names but not ..
key = strings.ReplaceAll(key, "..", "%2E%2E")
return key
}
var errNotFound = fmt.Errorf("not found")
var errExpired = fmt.Errorf("expired")
-106
View File
@@ -1,106 +0,0 @@
package cache
import (
"fmt"
"io"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal"
)
func Test_filesystemCache(t *testing.T) {
dir := t.TempDir()
man, err := NewFromDir(dir, 1*time.Minute)
require.NoError(t, err)
cacheName := "test"
cacheVersion := "v1"
cache := man.GetCache(cacheName, cacheVersion)
cacheKey := "test-key"
contentsValue := "some contents to cache"
err = cache.Write(cacheKey, strings.NewReader(contentsValue))
require.NoError(t, err)
rdr, err := cache.Read(cacheKey)
require.NoError(t, err)
defer internal.CloseAndLogError(rdr, cacheKey)
contents, err := io.ReadAll(rdr)
require.NoError(t, err)
require.Equal(t, contentsValue, string(contents))
// check the contents were actually written to disk as expected
contents, err = os.ReadFile(filepath.Join(dir, cacheName, cacheVersion, cacheKey))
require.NoError(t, err)
require.Equal(t, contentsValue, string(contents))
_, err = cache.Read("otherKey")
require.ErrorIs(t, err, errNotFound)
}
func Test_makeDiskKey(t *testing.T) {
tests := []struct {
in string
expected string
}{
{
in: "",
expected: "",
},
{
in: ".",
expected: "%2E",
},
{
in: "..",
expected: "%2E%2E",
},
{
in: "github.com",
expected: "github.com",
},
{
in: "../github.com",
expected: "%2E%2E/github.com",
},
{
in: "github.com/../..",
expected: "github.com/%2E%2E/%2E%2E",
},
{
in: "github.com/%2E../..",
expected: "github.com/%252E%2E%2E/%2E%2E",
},
}
for _, test := range tests {
t.Run(test.in, func(t *testing.T) {
got := makeDiskKey(test.in)
// validate appropriate escaping
require.Equal(t, test.expected, got)
// also validate that unescaped string matches original
unescaped, err := url.QueryUnescape(got)
require.NoError(t, err)
require.Equal(t, test.in, unescaped)
})
}
}
func Test_errors(t *testing.T) {
tmp := t.TempDir()
cache := filepath.Join(tmp, "cache")
// make a non-writable directory
require.NoError(t, os.MkdirAll(cache, 0500|os.ModeDir))
// attempt to make cache in non-writable directory
dir := filepath.Join(cache, "dir")
_, err := NewFromDir(dir, time.Hour)
require.ErrorContains(t, err, fmt.Sprintf("unable to create directory at '%s':", dir))
}
-71
View File
@@ -1,71 +0,0 @@
package cache
import (
"fmt"
"reflect"
"github.com/mitchellh/hashstructure/v2"
)
// hashType returns a stable hash based on the structure of the type
func hashType[T any]() string {
// get the base type and hash an empty instance
var t T
empty := emptyValue(reflect.TypeOf(t)).Interface()
hash, err := hashstructure.Hash(empty, hashstructure.FormatV2, &hashstructure.HashOptions{
ZeroNil: false,
IgnoreZeroValue: false,
SlicesAsSets: false,
UseStringer: false,
})
if err != nil {
panic(fmt.Errorf("unable to use type as cache key: %w", err))
}
return fmt.Sprintf("%x", hash)
}
func emptyValue(t reflect.Type) reflect.Value {
switch t.Kind() {
case reflect.Pointer:
e := t.Elem()
v := emptyValue(e)
if v.CanAddr() {
return v.Addr()
}
ptrv := reflect.New(e)
ptrv.Elem().Set(v)
return ptrv
case reflect.Slice:
v := emptyValue(t.Elem())
s := reflect.MakeSlice(t, 1, 1)
s.Index(0).Set(v)
return s
case reflect.Struct:
v := reflect.New(t).Elem()
// get all empty field values, too
for i := 0; i < v.NumField(); i++ {
f := t.Field(i)
if isIgnored(f) {
continue
}
fv := v.Field(i)
if fv.CanSet() {
fv.Set(emptyValue(f.Type))
}
}
return v
default:
return reflect.New(t).Elem()
}
}
func isIgnored(f reflect.StructField) bool {
if !f.IsExported() {
return true
}
tag := f.Tag.Get("hash")
if tag == "-" || tag == "ignore" {
return true
}
return false
}
-123
View File
@@ -1,123 +0,0 @@
package cache
import (
"fmt"
"testing"
"github.com/mitchellh/hashstructure/v2"
"github.com/stretchr/testify/require"
)
func Test_hashType(t *testing.T) {
type t1 struct {
Name string
}
type t2 struct {
Name string
}
type generic[T any] struct {
Val T
}
tests := []struct {
name string
hash func() string
expected string
}{
{
name: "struct 1",
hash: func() string { return hashType[t1]() },
expected: "d106c3ffbf98a0b1",
},
{
name: "slice of struct 1",
hash: func() string { return hashType[[]t1]() },
expected: "8122ace4ee1af0b4",
},
{
name: "slice of struct 2",
hash: func() string { return hashType[[]t2]() },
expected: "8cc04b5808be5bf9",
},
{
name: "ptr 1",
hash: func() string { return hashType[*t1]() },
expected: "d106c3ffbf98a0b1", // same hash as t1, which is ok since the structs are the same
},
{
name: "slice of ptr 1",
hash: func() string { return hashType[[]*t1]() },
expected: "8122ace4ee1af0b4", // same hash as []t1, again underlying serialization is the same
},
{
name: "slice of ptr 2",
hash: func() string { return hashType[[]*t2]() },
expected: "8cc04b5808be5bf9", // same hash as []t2, underlying serialization is the same
},
{
name: "slice of ptr of slice of ptr",
hash: func() string { return hashType[[]*[]*t1]() },
expected: "500d9f5b3a5977ce",
},
{
name: "generic 1",
hash: func() string { return hashType[generic[t1]]() },
expected: "b5fbb30e24400e81",
},
{
name: "generic 2",
hash: func() string { return hashType[generic[t2]]() },
expected: "becdb767c6b22bfa",
},
{
name: "generic with ptr 1",
hash: func() string { return hashType[generic[*t1]]() },
expected: "30c8855bf290fd83",
},
{
name: "generic with ptr 2",
hash: func() string { return hashType[generic[*t2]]() },
expected: "b66366b6ce9e6361",
},
{
name: "generic with slice 1",
hash: func() string { return hashType[generic[[]t1]]() },
expected: "d2ed158942fa6c29",
},
{
name: "generic with slice 2",
hash: func() string { return hashType[generic[[]t2]]() },
expected: "7a7bec575871c179",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.expected, test.hash())
})
}
}
func Test_hashIgnores(t *testing.T) {
hash := func(v any) string {
v, err := hashstructure.Hash(v, hashstructure.FormatV2, &hashstructure.HashOptions{})
require.NoError(t, err)
return fmt.Sprintf("%x", v)
}
type t1 struct {
Name string
notExported string
}
require.Equal(t, hash(t1{notExported: "a value"}), hashType[t1]())
type t2 struct {
Name string
Exported string `hash:"ignore"`
}
require.Equal(t, hash(t2{Exported: "another value"}), hashType[t2]())
type t3 struct {
Name string
Exported string `hash:"-"`
}
require.Equal(t, hash(t3{Exported: "still valued"}), hashType[t3]())
}
-19
View File
@@ -1,19 +0,0 @@
package cache
import (
"time"
"github.com/spf13/afero"
)
// NewInMemory returns an in-memory only cache manager
func NewInMemory(ttl time.Duration) Manager {
if ttl <= 0 {
return &bypassedCache{}
}
return &filesystemCache{
dir: "",
fs: afero.NewMemMapFs(),
ttl: ttl,
}
}
-37
View File
@@ -1,37 +0,0 @@
package cache
import (
"io"
"strings"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal"
)
func Test_NewInMemory(t *testing.T) {
man := NewInMemory(time.Hour)
cacheName := "test"
cacheVersion := "v1"
cache := man.GetCache(cacheName, cacheVersion)
cacheKey := "test-key"
contentsValue := "some contents to cache"
err := cache.Write(cacheKey, strings.NewReader(contentsValue))
require.NoError(t, err)
rdr, err := cache.Read(cacheKey)
require.NoError(t, err)
defer internal.CloseAndLogError(rdr, cacheKey)
contents, err := io.ReadAll(rdr)
require.NoError(t, err)
require.Equal(t, contentsValue, string(contents))
_, err = cache.Read("otherKey")
require.ErrorIs(t, err, errNotFound)
}
-83
View File
@@ -1,83 +0,0 @@
package cache
import (
"bytes"
"encoding/json"
"fmt"
"path"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/log"
)
// Resolver interface provides a single Resolve method, which will return from cache
// or call the provided resolve function to get the value if not available in cache
type Resolver[T any] interface {
// Resolve attempts to resolve the given key from cache and convert it to the type of the cache,
// or calls the resolver function if unable to resolve a cached value
Resolve(key string, resolver resolverFunc[T]) (T, error)
}
// GetResolver returns a cache resolver for persistent cached data across Syft runs, stored in a unique
// location based on the provided name and versioned by the type
func GetResolver[T any](name, version string) Resolver[T] {
typeHash := hashType[T]()
versionKey := path.Join(version, typeHash)
return &cacheResolver[T]{
name: fmt.Sprintf("%s/%s", name, versionKey),
cache: manager.GetCache(name, versionKey),
}
}
const resolverKeySuffix = ".json"
type resolverFunc[T any] func() (T, error)
type cacheResolver[T any] struct {
name string
cache Cache
}
var _ interface {
Resolver[int]
} = (*cacheResolver[int])(nil)
func (r *cacheResolver[T]) Resolve(key string, resolver resolverFunc[T]) (T, error) {
key += resolverKeySuffix
rdr, err := r.cache.Read(key)
if rdr == nil || err != nil {
return r.resolveAndCache(key, resolver)
}
defer internal.CloseAndLogError(rdr, key)
dec := json.NewDecoder(rdr)
if dec == nil {
log.Tracef("error getting cache json decoder for %s %v: %v", r.name, key, err)
return r.resolveAndCache(key, resolver)
}
var t T
err = dec.Decode(&t)
if err != nil {
log.Tracef("error decoding cached entry for %s %v: %v", r.name, key, err)
return r.resolveAndCache(key, resolver)
}
// no error, able to resolve from cache
return t, nil
}
func (r *cacheResolver[T]) resolveAndCache(key string, resolver func() (T, error)) (T, error) {
t, err := resolver()
if err != nil {
return t, err
}
var data bytes.Buffer
enc := json.NewEncoder(&data)
enc.SetEscapeHTML(false)
err = enc.Encode(t)
if err != nil {
return t, err
}
err = r.cache.Write(key, &data)
return t, err
}
-92
View File
@@ -1,92 +0,0 @@
package cache
import (
"encoding/json"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func Test_Resolver(t *testing.T) {
original := GetManager()
defer SetManager(original)
SetManager(NewInMemory(time.Hour))
type sub struct {
Name string
Value bool
}
type thing struct {
Value string
Values []int
Subs []*sub
}
versionHash := hashType[thing]()
cache := GetManager().GetCache("test", "v7/"+versionHash)
resolver := GetResolver[thing]("test", "v7")
require.NotNil(t, resolver)
require.IsType(t, &cacheResolver[thing]{}, resolver)
cr := resolver.(*cacheResolver[thing])
require.IsType(t, cache, cr.cache)
resolveErrCount := 0
resolveThingErr := func() (thing, error) {
resolveErrCount++
return thing{}, fmt.Errorf("an error")
}
_, err := resolver.Resolve("err", resolveThingErr)
require.ErrorContains(t, err, "an error")
require.Equal(t, 1, resolveErrCount)
_, err = resolver.Resolve("err", resolveThingErr)
require.ErrorContains(t, err, "an error")
require.Equal(t, 2, resolveErrCount)
aThing := thing{
Value: "a value",
Values: []int{7, 8, 9},
Subs: []*sub{
{
Name: "sub1",
Value: true,
},
{
Name: "sub2",
Value: false,
},
},
}
resolveThingCount := 0
resolveThing := func() (thing, error) {
resolveThingCount++
return aThing, nil
}
val, err := resolver.Resolve("thing", resolveThing)
require.NoError(t, err)
require.Equal(t, 1, resolveThingCount)
require.Equal(t, aThing, val)
val, err = resolver.Resolve("thing", resolveThing)
require.NoError(t, err)
require.Equal(t, 1, resolveThingCount)
require.Equal(t, aThing, val)
rdr, err := cache.Read("thing" + resolverKeySuffix)
require.NoError(t, err)
decoder := json.NewDecoder(rdr)
var val2 thing
err = decoder.Decode(&val2)
require.NoError(t, err)
require.Equal(t, aThing, val2)
}
+1 -1
View File
@@ -3,5 +3,5 @@ package internal
const (
// JSONSchemaVersion is the current schema version output by the JSON encoder
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
JSONSchemaVersion = "16.0.15"
JSONSchemaVersion = "16.0.12"
)
@@ -21,22 +21,27 @@ func NewDependencyRelationships(resolver file.Resolver, accessor sbomsync.Access
// 3. craft package-to-package relationships for each binary that represent shared library dependencies
//note: we only care about package-to-package relationships
return generateRelationships(resolver, accessor, index)
var relIndex *relationship.Index
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
relIndex = relationship.NewIndex(s.Relationships...)
})
return generateRelationships(resolver, accessor, index, relIndex)
}
func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, index *sharedLibraryIndex) []artifact.Relationship {
newRelationships := relationship.NewIndex()
func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, index *sharedLibraryIndex, relIndex *relationship.Index) []artifact.Relationship {
// read all existing dependencyOf relationships
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
for _, r := range s.Relationships {
if r.Type != artifact.DependencyOfRelationship {
continue
}
relIndex.Track(r)
}
})
// find all package-to-package relationships for shared library dependencies
accessor.ReadFromSBOM(func(s *sbom.SBOM) {
relIndex := relationship.NewIndex(s.Relationships...)
addRelationship := func(r artifact.Relationship) {
if !relIndex.Contains(r) {
newRelationships.Add(r)
}
}
for _, parentPkg := range s.Artifacts.Packages.Sorted(pkg.BinaryPkg) {
for _, evidentLocation := range parentPkg.Locations.ToSlice() {
if evidentLocation.Annotations[pkg.EvidenceAnnotationKey] != pkg.PrimaryEvidenceAnnotation {
@@ -49,12 +54,12 @@ func generateRelationships(resolver file.Resolver, accessor sbomsync.Accessor, i
continue
}
populateRelationships(exec, parentPkg, resolver, addRelationship, index)
populateRelationships(exec, parentPkg, resolver, relIndex, index)
}
}
})
return newRelationships.All()
return relIndex.NewRelationships()
}
// PackagesToRemove returns a list of binary packages (resolved by the ELF cataloger) that should be removed from the SBOM
@@ -142,7 +147,7 @@ func getBinaryPackagesToDelete(resolver file.Resolver, s *sbom.SBOM) []artifact.
return pkgsToDelete
}
func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver file.Resolver, addRelationship func(artifact.Relationship), index *sharedLibraryIndex) {
func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver file.Resolver, relIndex *relationship.Index, index *sharedLibraryIndex) {
for _, libReference := range exec.ImportedLibraries {
// for each library reference, check s.Artifacts.Packages.Sorted(pkg.BinaryPkg) for a binary package that represents that library
// if found, create a relationship between the parent package and the library package
@@ -162,7 +167,7 @@ func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver
realBaseName := path.Base(loc.RealPath)
pkgCollection := index.owningLibraryPackage(realBaseName)
if pkgCollection.PackageCount() < 1 {
addRelationship(
relIndex.Add(
artifact.Relationship{
From: loc.Coordinates,
To: parentPkg,
@@ -171,7 +176,7 @@ func populateRelationships(exec file.Executable, parentPkg pkg.Package, resolver
)
}
for _, p := range pkgCollection.Sorted() {
addRelationship(
relIndex.Add(
artifact.Relationship{
From: p,
To: parentPkg,
@@ -2,7 +2,6 @@ package binary
import (
"path"
"strings"
"testing"
"github.com/google/go-cmp/cmp"
@@ -329,20 +328,7 @@ func relationshipComparer(x, y []artifact.Relationship) string {
artifact.Relationship{},
file.LocationSet{},
pkg.LicenseSet{},
), cmpopts.SortSlices(lessRelationships))
}
func lessRelationships(r1, r2 artifact.Relationship) bool {
c := strings.Compare(string(r1.Type), string(r2.Type))
if c != 0 {
return c < 0
}
c = strings.Compare(string(r1.From.ID()), string(r2.From.ID()))
if c != 0 {
return c < 0
}
c = strings.Compare(string(r1.To.ID()), string(r2.To.ID()))
return c < 0
))
}
func newAccessor(pkgs []pkg.Package, coordinateIndex map[file.Coordinates]file.Executable, preexistingRelationships []artifact.Relationship) sbomsync.Accessor {
@@ -1,7 +1,6 @@
package relationship
import (
"reflect"
"slices"
"github.com/anchore/syft/internal/sbomsync"
@@ -22,10 +21,6 @@ var (
binaryCatalogerTypes = []pkg.Type{
pkg.BinaryPkg,
}
binaryMetadataTypes = []string{
reflect.TypeOf(pkg.ELFBinaryPackageNoteJSONPayload{}).Name(),
reflect.TypeOf(pkg.BinarySignature{}).Name(),
}
)
func ExcludeBinariesByFileOwnershipOverlap(accessor sbomsync.Accessor) {
@@ -65,15 +60,5 @@ func excludeBinaryByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collect
return false
}
if slices.Contains(binaryCatalogerTypes, child.Type) {
return true
}
if child.Metadata == nil {
return false
}
childMetadataType := reflect.TypeOf(child.Metadata)
return slices.Contains(binaryMetadataTypes, childMetadataType.Name())
return slices.Contains(binaryCatalogerTypes, child.Type)
}
@@ -12,9 +12,7 @@ func TestExclude(t *testing.T) {
packageB := pkg.Package{Name: "package-a", Type: pkg.PythonPkg}
packageC := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg}
packageD := pkg.Package{Name: "package-d", Type: pkg.BinaryPkg}
packageE := pkg.Package{Name: "package-e", Type: pkg.RpmPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{Type: "rpm"}}
packageF := pkg.Package{Name: "package-f", Type: pkg.RpmPkg, Metadata: pkg.BinarySignature{}}
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE, &packageF} {
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD} {
p := p
p.SetID()
}
@@ -45,26 +43,6 @@ func TestExclude(t *testing.T) {
packages: pkg.NewCollection(packageA, packageC),
shouldExclude: true,
},
{
name: "exclusions from os -> elf binary (as RPM)",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageE,
},
packages: pkg.NewCollection(packageA, packageE),
shouldExclude: true,
},
{
name: "exclusions from os -> binary (masquerading as RPM)",
relationship: artifact.Relationship{
Type: artifact.OwnershipByFileOverlapRelationship,
From: packageA,
To: packageF,
},
packages: pkg.NewCollection(packageA, packageF),
shouldExclude: true,
},
{
name: "no exclusions from python -> binary",
relationship: artifact.Relationship{
+56 -149
View File
@@ -1,181 +1,88 @@
package relationship
import (
"slices"
"strings"
"github.com/scylladb/go-set/strset"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
)
// Index indexes relationships, preventing duplicates
type Index struct {
all []*sortableRelationship
fromID map[artifact.ID]*mappedRelationships
toID map[artifact.ID]*mappedRelationships
typesByFromTo map[artifact.ID]map[artifact.ID]*strset.Set
existing []artifact.Relationship
additional []artifact.Relationship
}
// NewIndex returns a new relationship Index
func NewIndex(relationships ...artifact.Relationship) *Index {
out := Index{}
out.Add(relationships...)
return &out
}
// Add adds all the given relationships to the index, without adding duplicates
func (i *Index) Add(relationships ...artifact.Relationship) {
if i.fromID == nil {
i.fromID = map[artifact.ID]*mappedRelationships{}
func NewIndex(existing ...artifact.Relationship) *Index {
r := &Index{
typesByFromTo: make(map[artifact.ID]map[artifact.ID]*strset.Set),
}
if i.toID == nil {
i.toID = map[artifact.ID]*mappedRelationships{}
r.TrackAll(existing...)
return r
}
func (i *Index) track(r artifact.Relationship) bool {
fromID := r.From.ID()
if _, ok := i.typesByFromTo[fromID]; !ok {
i.typesByFromTo[fromID] = make(map[artifact.ID]*strset.Set)
}
// store appropriate indexes for stable ordering to minimize ID() calls
for _, r := range relationships {
// prevent duplicates
if i.Contains(r) {
continue
}
fromID := r.From.ID()
toID := r.To.ID()
relationship := &sortableRelationship{
from: fromID,
to: toID,
relationship: r,
}
// add to all relationships
i.all = append(i.all, relationship)
// add from -> to mapping
mapped := i.fromID[fromID]
if mapped == nil {
mapped = &mappedRelationships{}
i.fromID[fromID] = mapped
}
mapped.add(toID, relationship)
// add to -> from mapping
mapped = i.toID[toID]
if mapped == nil {
mapped = &mappedRelationships{}
i.toID[toID] = mapped
}
mapped.add(fromID, relationship)
toID := r.To.ID()
if _, ok := i.typesByFromTo[fromID][toID]; !ok {
i.typesByFromTo[fromID][toID] = strset.New()
}
}
// From returns all relationships from the given identifiable, with specified types
func (i *Index) From(identifiable artifact.Identifiable, types ...artifact.RelationshipType) []artifact.Relationship {
return toSortedSlice(fromMapped(i.fromID, identifiable), types)
}
// To returns all relationships to the given identifiable, with specified types
func (i *Index) To(identifiable artifact.Identifiable, types ...artifact.RelationshipType) []artifact.Relationship {
return toSortedSlice(fromMapped(i.toID, identifiable), types)
}
// References returns all relationships that reference to or from the given identifiable
func (i *Index) References(identifiable artifact.Identifiable, types ...artifact.RelationshipType) []artifact.Relationship {
return toSortedSlice(append(fromMapped(i.fromID, identifiable), fromMapped(i.toID, identifiable)...), types)
}
// Coordinates returns all coordinates for the provided identifiable for provided relationship types
// If no types are provided, all relationship types are considered.
func (i *Index) Coordinates(identifiable artifact.Identifiable, types ...artifact.RelationshipType) []file.Coordinates {
var coordinates []file.Coordinates
for _, relationship := range i.References(identifiable, types...) {
cords := extractCoordinates(relationship)
coordinates = append(coordinates, cords...)
var exists bool
if i.typesByFromTo[fromID][toID].Has(string(r.Type)) {
exists = true
}
return coordinates
i.typesByFromTo[fromID][toID].Add(string(r.Type))
return !exists
}
// Contains indicates the relationship is present in this index
func (i *Index) Contains(r artifact.Relationship) bool {
if mapped := i.fromID[r.From.ID()]; mapped != nil {
if ids := mapped.typeMap[r.Type]; ids != nil {
return ids[r.To.ID()] != nil
}
// Track this relationship as "exists" in the index (this is used to prevent duplicate relationships from being added).
// returns true if the relationship is new to the index, false otherwise.
func (i *Index) Track(r artifact.Relationship) bool {
unique := i.track(r)
if unique {
i.existing = append(i.existing, r)
}
return unique
}
// Add a new relationship to the index, returning true if the relationship is new to the index, false otherwise (thus is a duplicate).
func (i *Index) Add(r artifact.Relationship) bool {
if i.track(r) {
i.additional = append(i.additional, r)
return true
}
return false
}
// All returns a sorted set of relationships matching all types, or all relationships if no types specified
func (i *Index) All(types ...artifact.RelationshipType) []artifact.Relationship {
return toSortedSlice(i.all, types)
func (i *Index) TrackAll(rs ...artifact.Relationship) {
for _, r := range rs {
i.Track(r)
}
}
func fromMapped(idMap map[artifact.ID]*mappedRelationships, identifiable artifact.Identifiable) []*sortableRelationship {
if identifiable == nil || idMap == nil {
return nil
func (i *Index) AddAll(rs ...artifact.Relationship) {
for _, r := range rs {
i.Add(r)
}
mapped := idMap[identifiable.ID()]
if mapped == nil {
return nil
}
return mapped.allRelated
}
func toSortedSlice(relationships []*sortableRelationship, types []artifact.RelationshipType) []artifact.Relationship {
// always return sorted for SBOM stability
slices.SortFunc(relationships, sortFunc)
var out []artifact.Relationship
for _, r := range relationships {
if len(types) == 0 || slices.Contains(types, r.relationship.Type) {
out = append(out, r.relationship)
}
}
return out
func (i *Index) NewRelationships() []artifact.Relationship {
return i.additional
}
func extractCoordinates(relationship artifact.Relationship) (results []file.Coordinates) {
if coordinates, exists := relationship.From.(file.Coordinates); exists {
results = append(results, coordinates)
}
if coordinates, exists := relationship.To.(file.Coordinates); exists {
results = append(results, coordinates)
}
return results
func (i *Index) ExistingRelationships() []artifact.Relationship {
return i.existing
}
type mappedRelationships struct {
typeMap map[artifact.RelationshipType]map[artifact.ID]*sortableRelationship
allRelated []*sortableRelationship
}
func (m *mappedRelationships) add(id artifact.ID, newRelationship *sortableRelationship) {
m.allRelated = append(m.allRelated, newRelationship)
if m.typeMap == nil {
m.typeMap = map[artifact.RelationshipType]map[artifact.ID]*sortableRelationship{}
}
typeMap := m.typeMap[newRelationship.relationship.Type]
if typeMap == nil {
typeMap = map[artifact.ID]*sortableRelationship{}
m.typeMap[newRelationship.relationship.Type] = typeMap
}
typeMap[id] = newRelationship
}
type sortableRelationship struct {
from artifact.ID
to artifact.ID
relationship artifact.Relationship
}
func sortFunc(a, b *sortableRelationship) int {
cmp := strings.Compare(string(a.relationship.Type), string(b.relationship.Type))
if cmp != 0 {
return cmp
}
cmp = strings.Compare(string(a.from), string(b.from))
if cmp != 0 {
return cmp
}
return strings.Compare(string(a.to), string(b.to))
func (i *Index) AllUniqueRelationships() []artifact.Relationship {
var all []artifact.Relationship
all = append(all, i.existing...)
all = append(all, i.additional...)
return all
}
+202 -210
View File
@@ -3,231 +3,223 @@ package relationship
import (
"testing"
"github.com/stretchr/testify/require"
"github.com/google/go-cmp/cmp"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
func Test_Index(t *testing.T) {
p1 := pkg.Package{
Name: "pkg-1",
func Test_newRelationshipIndex(t *testing.T) {
from := fakeIdentifiable{id: "from"}
to := fakeIdentifiable{id: "to"}
tests := []struct {
name string
given []artifact.Relationship
track []artifact.Relationship
add []artifact.Relationship
wantExisting []string
wantAdditional []string
}{
{
name: "empty",
},
{
name: "tracks existing relationships",
given: []artifact.Relationship{
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
},
wantExisting: []string{"from [evident-by] to"},
},
{
name: "deduplicate tracked relationships",
given: []artifact.Relationship{
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
},
track: []artifact.Relationship{
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
},
wantExisting: []string{"from [evident-by] to"},
},
{
name: "deduplicate any input relationships",
given: []artifact.Relationship{
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
},
track: []artifact.Relationship{
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
},
add: []artifact.Relationship{
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
},
wantExisting: []string{"from [evident-by] to"},
},
{
name: "deduplicate any added relationships",
add: []artifact.Relationship{
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
{
From: from,
To: to,
Type: artifact.EvidentByRelationship,
},
},
wantAdditional: []string{"from [evident-by] to"},
},
}
p2 := pkg.Package{
Name: "pkg-2",
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
idx := NewIndex(tt.given...)
idx.TrackAll(tt.track...)
idx.AddAll(tt.add...)
diffRelationships(t, tt.wantExisting, idx.existing)
diffRelationships(t, tt.wantAdditional, idx.additional)
})
}
p3 := pkg.Package{
Name: "pkg-3",
}
c1 := file.Coordinates{
RealPath: "/coords/1",
}
c2 := file.Coordinates{
RealPath: "/coords/2",
}
for _, p := range []*pkg.Package{&p1, &p2, &p3} {
p.SetID()
}
r1 := artifact.Relationship{
From: p1,
To: p2,
Type: artifact.DependencyOfRelationship,
}
r2 := artifact.Relationship{
From: p1,
To: p3,
Type: artifact.DependencyOfRelationship,
}
r3 := artifact.Relationship{
From: p1,
To: c1,
Type: artifact.ContainsRelationship,
}
r4 := artifact.Relationship{
From: p2,
To: c2,
Type: artifact.ContainsRelationship,
}
r5 := artifact.Relationship{
From: p3,
To: c2,
Type: artifact.ContainsRelationship,
}
dup := artifact.Relationship{
From: p3,
To: c2,
Type: artifact.ContainsRelationship,
}
idx := NewIndex(r1, r2, r3, r4, r5, dup)
require.ElementsMatch(t, slice(r1, r2, r3, r4, r5), idx.All())
require.ElementsMatch(t, slice(r1, r4), idx.References(p2))
require.ElementsMatch(t, slice(r4), idx.References(p2, artifact.ContainsRelationship))
require.ElementsMatch(t, slice(r1), idx.To(p2))
require.ElementsMatch(t, []artifact.Relationship(nil), idx.To(p2, artifact.ContainsRelationship))
require.ElementsMatch(t, slice(r4), idx.From(p2))
require.ElementsMatch(t, slice(r4), idx.From(p2, artifact.ContainsRelationship))
}
func Test_sortOrder(t *testing.T) {
r1 := artifact.Relationship{
From: id("1"),
To: id("2"),
Type: "1",
func diffRelationships(t *testing.T, expected []string, actual []artifact.Relationship) {
if d := cmp.Diff(expected, stringRelationships(actual)); d != "" {
t.Errorf("unexpected relationships (-want, +got): %s", d)
}
r2 := artifact.Relationship{
From: id("2"),
To: id("3"),
Type: "1",
}
r3 := artifact.Relationship{
From: id("3"),
To: id("4"),
Type: "1",
}
r4 := artifact.Relationship{
From: id("1"),
To: id("2"),
Type: "2",
}
r5 := artifact.Relationship{
From: id("2"),
To: id("3"),
Type: "2",
}
dup := artifact.Relationship{
From: id("2"),
To: id("3"),
Type: "2",
}
r6 := artifact.Relationship{
From: id("2"),
To: id("3"),
Type: "3",
}
idx := NewIndex(r5, r2, r6, r4, r1, r3, dup)
require.EqualValues(t, slice(r1, r2, r3, r4, r5, r6), idx.All())
require.EqualValues(t, slice(r1, r4), idx.From(id("1")))
require.EqualValues(t, slice(r2, r5, r6), idx.To(id("3")))
rLast := artifact.Relationship{
From: id("0"),
To: id("3"),
Type: "9999",
}
rFirst := artifact.Relationship{
From: id("0"),
To: id("3"),
Type: "1",
}
rMid := artifact.Relationship{
From: id("0"),
To: id("1"),
Type: "2",
}
idx.Add(rLast, rFirst, rMid)
require.EqualValues(t, slice(rFirst, r1, r2, r3, rMid, r4, r5, r6, rLast), idx.All())
require.EqualValues(t, slice(rFirst, r2, r5, r6, rLast), idx.To(id("3")))
}
func Test_Coordinates(t *testing.T) {
p1 := pkg.Package{
Name: "pkg-1",
}
p2 := pkg.Package{
Name: "pkg-2",
}
p3 := pkg.Package{
Name: "pkg-3",
}
c1 := file.Coordinates{
RealPath: "/coords/1",
}
c2 := file.Coordinates{
RealPath: "/coords/2",
}
c3 := file.Coordinates{
RealPath: "/coords/3",
}
c4 := file.Coordinates{
RealPath: "/coords/4",
func stringRelationships(relationships []artifact.Relationship) []string {
var result []string
for _, r := range relationships {
result = append(result, string(r.From.ID())+" ["+string(r.Type)+"] "+string(r.To.ID()))
}
return result
for _, p := range []*pkg.Package{&p1, &p2, &p3} {
p.SetID()
}
r1 := artifact.Relationship{
From: p1,
To: p2,
Type: artifact.DependencyOfRelationship,
}
r2 := artifact.Relationship{
From: p1,
To: p3,
Type: artifact.DependencyOfRelationship,
}
r3 := artifact.Relationship{
From: p1,
To: c1,
Type: artifact.ContainsRelationship,
}
r4 := artifact.Relationship{
From: p2,
To: c2,
Type: artifact.ContainsRelationship,
}
r5 := artifact.Relationship{
From: p3,
To: c1,
Type: artifact.ContainsRelationship,
}
r6 := artifact.Relationship{
From: p3,
To: c2,
Type: artifact.ContainsRelationship,
}
r7 := artifact.Relationship{
From: c1,
To: c3,
Type: artifact.ContainsRelationship,
}
r8 := artifact.Relationship{
From: c3,
To: c4,
Type: artifact.ContainsRelationship,
}
idx := NewIndex(r1, r2, r3, r4, r5, r6, r7, r8)
got := idx.Coordinates(p1)
require.ElementsMatch(t, slice(c1), got)
got = idx.Coordinates(p3)
require.ElementsMatch(t, slice(c1, c2), got)
}
type id string
func (i id) ID() artifact.ID {
return artifact.ID(i)
func Test_relationshipIndex_track(t *testing.T) {
from := fakeIdentifiable{id: "from"}
to := fakeIdentifiable{id: "to"}
relationship := artifact.Relationship{From: from, To: to, Type: artifact.EvidentByRelationship}
tests := []struct {
name string
existing []artifact.Relationship
given artifact.Relationship
want bool
}{
{
name: "track returns true for a new relationship",
existing: []artifact.Relationship{},
given: relationship,
want: true,
},
{
name: "track returns false for an existing relationship",
existing: []artifact.Relationship{relationship},
given: relationship,
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := NewIndex(tt.existing...)
if got := i.Track(tt.given); got != tt.want {
t.Errorf("track() = %v, want %v", got, tt.want)
}
})
}
}
func slice[T any](values ...T) []T {
return values
func Test_relationshipIndex_add(t *testing.T) {
from := fakeIdentifiable{id: "from"}
to := fakeIdentifiable{id: "to"}
relationship := artifact.Relationship{From: from, To: to, Type: artifact.EvidentByRelationship}
tests := []struct {
name string
existing []artifact.Relationship
given artifact.Relationship
want bool
}{
{
name: "add returns true for a new relationship",
existing: []artifact.Relationship{},
given: relationship,
want: true,
},
{
name: "add returns false for an existing relationship",
existing: []artifact.Relationship{relationship},
given: relationship,
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
i := NewIndex(tt.existing...)
if got := i.Add(tt.given); got != tt.want {
t.Errorf("add() = %v, want %v", got, tt.want)
}
})
}
}
type fakeIdentifiable struct {
id string
}
func (f fakeIdentifiable) ID() artifact.ID {
return artifact.ID(f.id)
}
-68
View File
@@ -1,68 +0,0 @@
package internal
import (
"fmt"
"sort"
)
// Set represents a generic set type.
type Set[T comparable] map[T]struct{}
// NewSet creates a new empty Set.
func NewSet[T comparable](start ...T) Set[T] {
ret := make(Set[T])
for _, v := range start {
ret.Add(v)
}
return ret
}
// Add adds elements to the set.
func (s Set[T]) Add(elements ...T) {
for _, e := range elements {
s[e] = struct{}{}
}
}
// Remove removes an element from the set.
func (s Set[T]) Remove(element T) {
delete(s, element)
}
// Contains checks if an element is in the set.
func (s Set[T]) Contains(element T) bool {
_, ok := s[element]
return ok
}
// ToSlice returns a sorted slice of elements in the set.
func (s Set[T]) ToSlice() []T {
ret := make([]T, len(s))
idx := 0
for v := range s {
ret[idx] = v
idx++
}
sort.Slice(ret, func(i, j int) bool {
return fmt.Sprintf("%v", ret[i]) < fmt.Sprintf("%v", ret[j])
})
return ret
}
// Equals checks if two sets are equal.
func (s Set[T]) Equals(o Set[T]) bool {
if len(s) != len(o) {
return false
}
for k := range s {
if !o.Contains(k) {
return false
}
}
return true
}
// Empty checks if the set is empty.
func (s Set[T]) Empty() bool {
return len(s) == 0
}
-146
View File
@@ -1,146 +0,0 @@
package internal
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewSet(t *testing.T) {
tests := []struct {
name string
start []int
result Set[int]
}{
{"empty set", []int{}, NewSet[int]()},
{"non-empty set", []int{1, 2, 3}, NewSet(1, 2, 3)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := NewSet(tt.start...)
require.Equal(t, tt.result, s)
})
}
}
func TestAdd(t *testing.T) {
tests := []struct {
name string
input []int
add []int
result Set[int]
}{
{"add to empty set", []int{}, []int{1, 2, 3}, NewSet(1, 2, 3)},
{"add to non-empty set", []int{1}, []int{2, 3}, NewSet(1, 2, 3)},
{"add existing elements", []int{1, 2}, []int{2, 3}, NewSet(1, 2, 3)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := NewSet(tt.input...)
s.Add(tt.add...)
assert.Equal(t, tt.result, s)
})
}
}
func TestRemove(t *testing.T) {
tests := []struct {
name string
input []int
remove int
result Set[int]
}{
{"remove from non-empty set", []int{1, 2, 3}, 2, NewSet(1, 3)},
{"remove non-existent element", []int{1, 2}, 3, NewSet(1, 2)},
{"remove from single-element set", []int{1}, 1, NewSet[int]()},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := NewSet(tt.input...)
s.Remove(tt.remove)
assert.Equal(t, tt.result, s)
})
}
}
func TestContains(t *testing.T) {
tests := []struct {
name string
input []int
contains int
result bool
}{
{"element in set", []int{1, 2, 3}, 2, true},
{"element not in set", []int{1, 2}, 3, false},
{"empty set", []int{}, 1, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := NewSet(tt.input...)
assert.Equal(t, tt.result, s.Contains(tt.contains))
})
}
}
func TestToSlice(t *testing.T) {
tests := []struct {
name string
input []int
result []int
}{
{"non-empty set", []int{3, 1, 2}, []int{1, 2, 3}},
{"empty set", []int{}, []int{}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := NewSet(tt.input...)
assert.Equal(t, tt.result, s.ToSlice())
})
}
}
func TestEquals(t *testing.T) {
tests := []struct {
name string
set1 []int
set2 []int
result bool
}{
{"equal sets", []int{1, 2, 3}, []int{3, 2, 1}, true},
{"different sets", []int{1, 2}, []int{2, 3}, false},
{"empty sets", []int{}, []int{}, true},
{"one empty set", []int{1, 2}, []int{}, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s1 := NewSet(tt.set1...)
s2 := NewSet(tt.set2...)
assert.Equal(t, tt.result, s1.Equals(s2))
})
}
}
func TestEmpty(t *testing.T) {
tests := []struct {
name string
input []int
result bool
}{
{"non-empty set", []int{1}, false},
{"empty set", []int{}, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := NewSet(tt.input...)
assert.Equal(t, tt.result, s.Empty())
})
}
}
+61
View File
@@ -0,0 +1,61 @@
package internal
import "sort"
// StringSet represents a set of string types.
type StringSet map[string]struct{}
// NewStringSet creates a new empty StringSet.
func NewStringSet(start ...string) StringSet {
ret := make(StringSet)
for _, s := range start {
ret.Add(s)
}
return ret
}
// Add a string to the set.
func (s StringSet) Add(i ...string) {
for _, str := range i {
s[str] = struct{}{}
}
}
// Remove a string from the set.
func (s StringSet) Remove(i string) {
delete(s, i)
}
// Contains indicates if the given string is contained within the set.
func (s StringSet) Contains(i string) bool {
_, ok := s[i]
return ok
}
// ToSlice returns a sorted slice of strings that are contained within the set.
func (s StringSet) ToSlice() []string {
ret := make([]string, len(s))
idx := 0
for v := range s {
ret[idx] = v
idx++
}
sort.Strings(ret)
return ret
}
func (s StringSet) Equals(o StringSet) bool {
if len(s) != len(o) {
return false
}
for k := range s {
if !o.Contains(k) {
return false
}
}
return true
}
func (s StringSet) Empty() bool {
return len(s) < 1
}
-2
View File
@@ -29,7 +29,6 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/rust"
sbomCataloger "github.com/anchore/syft/syft/pkg/cataloger/sbom"
"github.com/anchore/syft/syft/pkg/cataloger/swift"
"github.com/anchore/syft/syft/pkg/cataloger/swipl"
"github.com/anchore/syft/syft/pkg/cataloger/wordpress"
)
@@ -94,7 +93,6 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
newSimplePackageTaskFactory(rust.NewCargoLockCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "rust", "cargo"),
newSimplePackageTaskFactory(swift.NewCocoapodsCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swift", "cocoapods"),
newSimplePackageTaskFactory(swift.NewSwiftPackageManagerCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swift", "spm"),
newSimplePackageTaskFactory(swipl.NewSwiplPackCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "swipl", "pack"),
// language-specific package for both image and directory scans (but not necessarily declared) ////////////////////////////////////////
newSimplePackageTaskFactory(dotnet.NewDotnetPortableExecutableCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "dotnet", "c#", "binary"),
-9
View File
@@ -7,12 +7,3 @@ For this reason we've included a copy of all schemas needed to validate `syft` o
to reference local copies of dependent schemas.
You can get the latest schemas from the [CycloneDX specifications repo](https://github.com/CycloneDX/specification/tree/master/schema).
When the spec version is bumped an approach to determining prior modifications is to compare the
prior spec version (e.g. if updating to 1.7, compare the files in this directory against the 1.6
equivalents).
One can also update the schemas and observe the errors in order to make the necessary updates.
At the time of writing, the cyclonedx.xsd needed modifications to link to the local spdx.xsd,
and also to changes the minOccurs for a license tag to 0. (The json schema does not require
modification for the generated file to lint properly, but can simply be copy/pasted).
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
+1 -126
View File
@@ -2,7 +2,7 @@
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified"
targetNamespace="http://cyclonedx.org/schema/spdx"
version="1.0-3.24.0">
version="1.0-3.23">
<xs:simpleType name="licenseId">
<xs:restriction base="xs:string">
@@ -12,11 +12,6 @@
<xs:documentation>BSD Zero Clause License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="3D-Slicer-1.0">
<xs:annotation>
<xs:documentation>3D Slicer License v1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="AAL">
<xs:annotation>
<xs:documentation>Attribution Assurance License</xs:documentation>
@@ -122,11 +117,6 @@
<xs:documentation>Aladdin Free Public License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="AMD-newlib">
<xs:annotation>
<xs:documentation>AMD newlib License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="AMDPLPA">
<xs:annotation>
<xs:documentation>AMD&apos;s plpa_map.c License</xs:documentation>
@@ -157,11 +147,6 @@
<xs:documentation>ANTLR Software Rights Notice with license fallback</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="any-OSI">
<xs:annotation>
<xs:documentation>Any OSI License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Apache-1.0">
<xs:annotation>
<xs:documentation>Apache License 1.0</xs:documentation>
@@ -337,11 +322,6 @@
<xs:documentation>BSD 2-Clause - Ian Darwin variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-2-Clause-first-lines">
<xs:annotation>
<xs:documentation>BSD 2-Clause - first lines requirement</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="BSD-2-Clause-FreeBSD">
<xs:annotation>
<xs:documentation>BSD 2-Clause FreeBSD License</xs:documentation>
@@ -542,11 +522,6 @@
<xs:documentation>Caldera License (without preamble)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Catharon">
<xs:annotation>
<xs:documentation>Catharon License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="CATOSL-1.1">
<xs:annotation>
<xs:documentation>Computer Associates Trusted Open Source License 1.1</xs:documentation>
@@ -1027,11 +1002,6 @@
<xs:documentation>curl License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="cve-tou">
<xs:annotation>
<xs:documentation>Common Vulnerability Enumeration ToU License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="D-FSL-1.0">
<xs:annotation>
<xs:documentation>Deutsche Freie Software Lizenz</xs:documentation>
@@ -1512,11 +1482,6 @@
<xs:documentation>gtkbook License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Gutmann">
<xs:annotation>
<xs:documentation>Gutmann License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HaskellReport">
<xs:annotation>
<xs:documentation>Haskell Language Report License</xs:documentation>
@@ -1567,21 +1532,11 @@
<xs:documentation>HPND with US Government export control warning</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-export-US-acknowledgement">
<xs:annotation>
<xs:documentation>HPND with US Government export control warning and acknowledgment</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-export-US-modify">
<xs:annotation>
<xs:documentation>HPND with US Government export control warning and modification rqmt</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-export2-US">
<xs:annotation>
<xs:documentation>HPND with US Government export control and 2 disclaimers</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-Fenneberg-Livingston">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - Fenneberg-Livingston variant</xs:documentation>
@@ -1592,11 +1547,6 @@
<xs:documentation>Historical Permission Notice and Disclaimer - INRIA-IMAG variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-Intel">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - Intel variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-Kevlin-Henney">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - Kevlin Henney variant</xs:documentation>
@@ -1607,11 +1557,6 @@
<xs:documentation>Historical Permission Notice and Disclaimer - Markus Kuhn variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-merchantability-variant">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - merchantability variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-MIT-disclaimer">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer with MIT disclaimer</xs:documentation>
@@ -1642,21 +1587,11 @@
<xs:documentation>HPND sell variant with MIT disclaimer</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-sell-variant-MIT-disclaimer-rev">
<xs:annotation>
<xs:documentation>HPND sell variant with MIT disclaimer - reverse</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-UC">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - University of California variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HPND-UC-export-US">
<xs:annotation>
<xs:documentation>Historical Permission Notice and Disclaimer - University of California, US export warning</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="HTMLTIDY">
<xs:annotation>
<xs:documentation>HTML Tidy License</xs:documentation>
@@ -2092,11 +2027,6 @@
<xs:documentation>MIT Festival Variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="MIT-Khronos-old">
<xs:annotation>
<xs:documentation>MIT Khronos - old variant</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="MIT-Modern-Variant">
<xs:annotation>
<xs:documentation>MIT License Modern Variant</xs:documentation>
@@ -2232,21 +2162,11 @@
<xs:documentation>Net Boolean Public License v1</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="NCBI-PD">
<xs:annotation>
<xs:documentation>NCBI Public Domain Notice</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="NCGL-UK-2.0">
<xs:annotation>
<xs:documentation>Non-Commercial Government Licence</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="NCL">
<xs:annotation>
<xs:documentation>NCL Source Code License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="NCSA">
<xs:annotation>
<xs:documentation>University of Illinois/NCSA Open Source License</xs:documentation>
@@ -2362,11 +2282,6 @@
<xs:documentation>Open Use of Data Agreement v1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OAR">
<xs:annotation>
<xs:documentation>OAR License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="OCCT-PL">
<xs:annotation>
<xs:documentation>Open CASCADE Technology Public License</xs:documentation>
@@ -2647,11 +2562,6 @@
<xs:documentation>Pixar License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="pkgconf">
<xs:annotation>
<xs:documentation>pkgconf License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Plexus">
<xs:annotation>
<xs:documentation>Plexus Classworlds License</xs:documentation>
@@ -2677,11 +2587,6 @@
<xs:documentation>PostgreSQL License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="PPL">
<xs:annotation>
<xs:documentation>Peer Production License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="PSF-2.0">
<xs:annotation>
<xs:documentation>Python Software Foundation License 2.0</xs:documentation>
@@ -2957,11 +2862,6 @@
<xs:documentation>Sun PPP License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Sun-PPP-2000">
<xs:annotation>
<xs:documentation>Sun PPP License (2000)</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="SunPro">
<xs:annotation>
<xs:documentation>SunPro License</xs:documentation>
@@ -3007,11 +2907,6 @@
<xs:documentation>Transitive Grace Period Public Licence 1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="threeparttable">
<xs:annotation>
<xs:documentation>threeparttable License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="TMate">
<xs:annotation>
<xs:documentation>TMate Open Source License</xs:documentation>
@@ -3237,11 +3132,6 @@
<xs:documentation>XSkat License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="xzoom">
<xs:annotation>
<xs:documentation>xzoom License</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="YPL-1.0">
<xs:annotation>
<xs:documentation>Yahoo! Public License v1.0</xs:documentation>
@@ -3313,11 +3203,6 @@
<xs:documentation>Asterisk exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Asterisk-linking-protocols-exception">
<xs:annotation>
<xs:documentation>Asterisk linking protocols exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="Autoconf-exception-2.0">
<xs:annotation>
<xs:documentation>Autoconf exception 2.0</xs:documentation>
@@ -3553,11 +3438,6 @@
<xs:documentation>OpenVPN OpenSSL Exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="PCRE2-exception">
<xs:annotation>
<xs:documentation>PCRE2 exception</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="PS-or-PDF-font-exception-20170817">
<xs:annotation>
<xs:documentation>PS/PDF font exception (2017-08-17)</xs:documentation>
@@ -3583,11 +3463,6 @@
<xs:documentation>Qwt exception 1.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="RRDtool-FLOSS-exception-2.0">
<xs:annotation>
<xs:documentation>RRDtool FLOSS exception 2.0</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value="SANE-exception">
<xs:annotation>
<xs:documentation>SANE Exception</xs:documentation>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -119
View File
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "anchore.io/schema/syft/json/16.0.15/document",
"$id": "anchore.io/schema/syft/json/16.0.12/document",
"$ref": "#/$defs/Document",
"$defs": {
"AlpmDbEntry": {
@@ -647,24 +647,12 @@
"type": {
"type": "string"
},
"architecture": {
"type": "string"
},
"osCPE": {
"type": "string"
},
"os": {
"type": "string"
},
"osVersion": {
"vendor": {
"type": "string"
},
"system": {
"type": "string"
},
"vendor": {
"type": "string"
},
"sourceRepo": {
"type": "string"
},
@@ -1608,9 +1596,6 @@
{
"$ref": "#/$defs/SwiftPackageManagerLockEntry"
},
{
"$ref": "#/$defs/SwiplpackPackage"
},
{
"$ref": "#/$defs/WordpressPluginEntry"
}
@@ -2066,74 +2051,15 @@
"index"
]
},
"PythonPoetryLockDependencyEntry": {
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
},
"optional": {
"type": "boolean"
},
"markers": {
"type": "string"
},
"extras": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
"required": [
"name",
"version",
"optional"
]
},
"PythonPoetryLockEntry": {
"properties": {
"index": {
"type": "string"
},
"dependencies": {
"items": {
"$ref": "#/$defs/PythonPoetryLockDependencyEntry"
},
"type": "array"
},
"extras": {
"items": {
"$ref": "#/$defs/PythonPoetryLockExtraEntry"
},
"type": "array"
}
},
"type": "object",
"required": [
"index",
"dependencies"
]
},
"PythonPoetryLockExtraEntry": {
"properties": {
"name": {
"type": "string"
},
"dependencies": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
"required": [
"name",
"dependencies"
"index"
]
},
"RDescription": {
@@ -2507,48 +2433,6 @@
"revision"
]
},
"SwiplpackPackage": {
"properties": {
"name": {
"type": "string"
},
"version": {
"type": "string"
},
"author": {
"type": "string"
},
"authorEmail": {
"type": "string"
},
"packager": {
"type": "string"
},
"packagerEmail": {
"type": "string"
},
"homepage": {
"type": "string"
},
"dependencies": {
"items": {
"type": "string"
},
"type": "array"
}
},
"type": "object",
"required": [
"name",
"version",
"author",
"authorEmail",
"packager",
"packagerEmail",
"homepage",
"dependencies"
]
},
"WordpressPluginEntry": {
"properties": {
"pluginInstallDirectory": {
@@ -64,28 +64,6 @@ func TestBySourceThenSpecificity(t *testing.T) {
Must("cpe:2.3:a:some:package:*:*:*:*:*:*:*:*", "some-unknown-source"),
},
},
{
name: "lexical sorting on equal sources puts escaped characters later",
input: []CPE{
Must("cpe:2.3:a:jenkins:pipeline\\\\:_supporting_apis:865.v43e78cc44e0d:*:*:*:*:jenkins:*:*", "nvd-cpe-dictionary"),
Must("cpe:2.3:a:jenkins:pipeline_supporting_apis:865.v43e78cc44e0d:*:*:*:*:jenkins:*:*", "nvd-cpe-dictionary"),
},
want: []CPE{
Must("cpe:2.3:a:jenkins:pipeline_supporting_apis:865.v43e78cc44e0d:*:*:*:*:jenkins:*:*", "nvd-cpe-dictionary"),
Must("cpe:2.3:a:jenkins:pipeline\\\\:_supporting_apis:865.v43e78cc44e0d:*:*:*:*:jenkins:*:*", "nvd-cpe-dictionary"),
},
},
{
name: "lexical sorting on equal sources puts more specific attributes earlier",
input: []CPE{
Must("cpe:2.3:a:jenkins:mailer:472.vf7c289a_4b_420:*:*:*:*:*:*:*", "nvd-cpe-dictionary"),
Must("cpe:2.3:a:jenkins:mailer:472.vf7c289a_4b_420:*:*:*:*:jenkins:*:*", "nvd-cpe-dictionary"),
},
want: []CPE{
Must("cpe:2.3:a:jenkins:mailer:472.vf7c289a_4b_420:*:*:*:*:jenkins:*:*", "nvd-cpe-dictionary"),
Must("cpe:2.3:a:jenkins:mailer:472.vf7c289a_4b_420:*:*:*:*:*:*:*", "nvd-cpe-dictionary"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
+1 -1
View File
@@ -21,7 +21,7 @@ type LicenseEvidence struct {
func NewLicense(value string) License {
spdxExpression, err := license.ParseExpression(value)
if err != nil {
log.WithFields("error", err, "value", value).Trace("unable to parse license expression")
log.Trace("unable to parse license expression: %s, %w", value, err)
}
return License{
@@ -16,7 +16,6 @@ import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/internal/mimetype"
"github.com/anchore/syft/internal/relationship"
"github.com/anchore/syft/internal/spdxlicense"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
@@ -46,10 +45,9 @@ const (
func ToFormatModel(s sbom.SBOM) *spdx.Document {
name, namespace := helpers.DocumentNameAndNamespace(s.Source, s.Descriptor)
rels := relationship.NewIndex(s.Relationships...)
packages := toPackages(rels, s.Artifacts.Packages, s)
packages := toPackages(s.Artifacts.Packages, s)
allRelationships := toRelationships(rels.All())
relationships := toRelationships(s.RelationshipsSorted())
// for valid SPDX we need a document describes relationship
describesID := spdx.ElementID("DOCUMENT")
@@ -59,7 +57,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
describesID = rootPackage.PackageSPDXIdentifier
// add all relationships from the document root to all other packages
allRelationships = append(allRelationships, toRootRelationships(rootPackage, packages)...)
relationships = append(relationships, toRootRelationships(rootPackage, packages)...)
// append the root package
packages = append(packages, rootPackage)
@@ -77,7 +75,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
}
// add the root document relationship
allRelationships = append(allRelationships, documentDescribesRelationship)
relationships = append(relationships, documentDescribesRelationship)
return &spdx.Document{
// 6.1: SPDX Version; should be in the format "SPDX-x.x"
@@ -152,7 +150,7 @@ func ToFormatModel(s sbom.SBOM) *spdx.Document {
},
Packages: packages,
Files: toFiles(s),
Relationships: allRelationships,
Relationships: relationships,
OtherLicenses: toOtherLicenses(s.Artifacts.Packages),
}
}
@@ -304,7 +302,7 @@ func toSPDXID(identifiable artifact.Identifiable) spdx.ElementID {
// packages populates all Package Information from the package Collection (see https://spdx.github.io/spdx-spec/3-package-information/)
//
//nolint:funlen
func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBOM) (results []*spdx.Package) {
func toPackages(catalog *pkg.Collection, sbom sbom.SBOM) (results []*spdx.Package) {
for _, p := range catalog.Sorted() {
// name should be guaranteed to be unique, but semantically useful and stable
id := toSPDXID(p)
@@ -320,7 +318,7 @@ func toPackages(rels *relationship.Index, catalog *pkg.Collection, sbom sbom.SBO
// 2. syft has generated a sha1 digest for the package's contents
packageChecksums, filesAnalyzed := toPackageChecksums(p)
packageVerificationCode := newPackageVerificationCode(rels, p, sbom)
packageVerificationCode := newPackageVerificationCode(p, sbom)
if packageVerificationCode != nil {
filesAnalyzed = true
}
@@ -746,12 +744,12 @@ func toOtherLicenses(catalog *pkg.Collection) []*spdx.OtherLicense {
// f file is an "excludes" file, skip it /* exclude SPDX analysis file(s) */
// see: https://spdx.github.io/spdx-spec/v2.3/package-information/#79-package-verification-code-field
// the above link contains the SPDX algorithm for a package verification code
func newPackageVerificationCode(rels *relationship.Index, p pkg.Package, sbom sbom.SBOM) *spdx.PackageVerificationCode {
func newPackageVerificationCode(p pkg.Package, sbom sbom.SBOM) *spdx.PackageVerificationCode {
// key off of the contains relationship;
// spdx validator will fail if a package claims to contain a file but no sha1 provided
// if a sha1 for a file is provided then the validator will fail if the package does not have
// a package verification code
coordinates := rels.Coordinates(p, artifact.ContainsRelationship)
coordinates := sbom.CoordinatesForPackage(p, artifact.ContainsRelationship)
var digests []file.Digest
for _, c := range coordinates {
digest := sbom.Artifacts.FileDigests[c]
@@ -12,7 +12,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/internal/relationship"
"github.com/anchore/syft/syft/artifact"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/format/internal/spdxutil/helpers"
@@ -666,7 +665,7 @@ func Test_H1Digest(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
catalog := pkg.NewCollection(test.pkg)
pkgs := toPackages(relationship.NewIndex(), catalog, s)
pkgs := toPackages(catalog, s)
require.Len(t, pkgs, 1)
for _, p := range pkgs {
if test.expectedDigest == "" {
@@ -1,59 +0,0 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"serialNumber": "urn:uuid:5208fea9-73dd-4624-b596-69fddccdb9e7",
"version": 1,
"metadata": {
"timestamp": "2023-09-29T12:02:02-04:00",
"tools": [
{
"vendor": "anchore",
"name": "syft",
"version": "[not provided]"
}
],
"component": {
"bom-ref": "a0ff99a6af10f11f",
"type": "file",
"name": "go.mod",
"version": "sha256:sha256:dc333f342905248a52e424d8dfd061251d01867d01a4f9d7397144a775ff9ebd"
}
},
"components": [
{
"bom-ref": "pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651?package-id=2ff71a67fb024c86",
"type": "library",
"name": "github.com/wagoodman/go-partybus",
"version": "v0.0.0-20230516145632-8ccac152c651",
"cpe": "cpe:2.3:a:wagoodman:go-partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*",
"purl": "pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651",
"properties": [
{
"name": "syft:package:foundBy",
"value": "go-module-file-cataloger"
},
{
"name": "syft:package:language",
"value": "go"
},
{
"name": "syft:package:metadataType",
"value": "GolangModMetadata"
},
{
"name": "syft:package:type",
"value": "go-module"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:wagoodman:go_partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*"
},
{
"name": "syft:location:0:path",
"value": "/go.mod"
}
]
}
]
}
@@ -1,28 +0,0 @@
{
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"version": 1,
"components": [
{
"type": "library",
"group": "io.netty",
"name": "netty-codec-http2",
"version": "4.1.73.Final",
"properties": [
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:codec:codec:4.1.73.Final:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:codec:netty-codec-http2:4.1.73.Final:*:*:*:*:*:*:*"
},
{
"name": "syft:cpe23",
"value": "cpe:2.3:a:codec:netty_codec_http2:4.1.73.Final:*:*:*:*:*:*:*"
}
]
}
],
"serialNumber": "urn:uuid:3eb5ec7a-cb05-4339-b873-e27b1c1efaba"
}
@@ -1,7 +1,7 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"serialNumber": "urn:uuid:redacted",
"version": 1,
"metadata": {
@@ -1,7 +1,7 @@
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"specVersion": "1.5",
"serialNumber": "urn:uuid:redacted",
"version": 1,
"metadata": {
@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="urn:uuid:098e8516-ecd5-4130-9d5f-c32ba1ddb0dd" version="1">
<metadata>
<timestamp>2023-09-29T11:48:10-04:00</timestamp>
<tools>
<tool>
<vendor>anchore</vendor>
<name>syft</name>
<version>[not provided]</version>
</tool>
</tools>
<component bom-ref="a0ff99a6af10f11f" type="file">
<name>go.mod</name>
<version>sha256:sha256:dc333f342905248a52e424d8dfd061251d01867d01a4f9d7397144a775ff9ebd</version>
</component>
</metadata>
<components>
<component bom-ref="pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651?package-id=2ff71a67fb024c86" type="library">
<name>github.com/wagoodman/go-partybus</name>
<version>v0.0.0-20230516145632-8ccac152c651</version>
<cpe>cpe:2.3:a:wagoodman:go-partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*</cpe>
<purl>pkg:golang/github.com/wagoodman/go-partybus@v0.0.0-20230516145632-8ccac152c651</purl>
<properties>
<property name="syft:package:foundBy">go-module-file-cataloger</property>
<property name="syft:package:language">go</property>
<property name="syft:package:metadataType">GolangModMetadata</property>
<property name="syft:package:type">go-module</property>
<property name="syft:cpe23">cpe:2.3:a:wagoodman:go_partybus:v0.0.0-20230516145632-8ccac152c651:*:*:*:*:*:*:*</property>
<property name="syft:location:0:path">/go.mod</property>
</properties>
</component>
</components>
</bom>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="redacted" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.5" serialNumber="redacted" version="1">
<metadata>
<timestamp>redacted</timestamp>
<tools>
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.6" serialNumber="redacted" version="1">
<bom xmlns="http://cyclonedx.org/schema/bom/1.5" serialNumber="redacted" version="1">
<metadata>
<timestamp>redacted</timestamp>
<tools>
@@ -9,7 +9,7 @@ import (
"github.com/anchore/syft/syft/sbom"
)
const DefaultVersion = "1.6"
const DefaultVersion = "1.5"
type Encoder struct {
version cyclonedx.SpecVersion
@@ -19,7 +19,6 @@ func SupportedVersions(id sbom.FormatID) []string {
"1.3",
"1.4",
"1.5",
"1.6",
}
if id != JSONFormatID {
@@ -44,8 +43,6 @@ func SpecVersionFromString(v string) (cyclonedx.SpecVersion, error) {
return cyclonedx.SpecVersion1_4, nil
case "1.5":
return cyclonedx.SpecVersion1_5, nil
case "1.6":
return cyclonedx.SpecVersion1_6, nil
}
return -1, fmt.Errorf("unsupported CycloneDX version %q", v)
}
@@ -64,8 +61,6 @@ func VersionFromSpecVersion(spec cyclonedx.SpecVersion) string {
return "1.4"
case cyclonedx.SpecVersion1_5:
return "1.5"
case cyclonedx.SpecVersion1_6:
return "1.6"
}
return ""
}
@@ -22,10 +22,6 @@ func DownloadLocation(p pkg.Package) string {
return NoneIfEmpty(metadata.URL)
case pkg.NpmPackageLockEntry:
return NoneIfEmpty(metadata.Resolved)
case pkg.PhpComposerLockEntry:
return NoneIfEmpty(metadata.Dist.URL)
case pkg.PhpComposerInstalledEntry:
return NoneIfEmpty(metadata.Dist.URL)
}
}
return NOASSERTION
@@ -64,50 +64,6 @@ func Test_DownloadLocation(t *testing.T) {
},
expected: NONE,
},
{
name: "from php installed.json",
input: pkg.Package{
Metadata: pkg.PhpComposerInstalledEntry{
Dist: pkg.PhpComposerExternalReference{
URL: "http://package-lock.test",
},
},
},
expected: "http://package-lock.test",
},
{
name: "empty",
input: pkg.Package{
Metadata: pkg.PhpComposerInstalledEntry{
Dist: pkg.PhpComposerExternalReference{
URL: "",
},
},
},
expected: "NONE",
},
{
name: "from php composer.lock",
input: pkg.Package{
Metadata: pkg.PhpComposerLockEntry{
Dist: pkg.PhpComposerExternalReference{
URL: "http://package-lock.test",
},
},
},
expected: "http://package-lock.test",
},
{
name: "empty",
input: pkg.Package{
Metadata: pkg.PhpComposerLockEntry{
Dist: pkg.PhpComposerExternalReference{
URL: "",
},
},
},
expected: "NONE",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@@ -56,10 +56,6 @@ func Originator(p pkg.Package) (typ string, author string) { // nolint: funlen
if author == "" {
author = metadata.Manifest.Main.MustGet("Implementation-Vendor")
}
// Vendor is specified, hence set 'Organization' as the PackageSupplier
if author != "" {
typ = orgType
}
}
case pkg.LinuxKernelModule:
@@ -107,8 +103,6 @@ func Originator(p pkg.Package) (typ string, author string) { // nolint: funlen
// it seems that the vast majority of the time the author is an org, not a person
typ = orgType
author = metadata.Author
case pkg.SwiplPackEntry:
author = formatPersonOrOrg(metadata.Author, metadata.AuthorEmail)
}
if typ == "" && author != "" {
@@ -150,10 +144,6 @@ func Supplier(p pkg.Package) (typ string, author string) {
author = metadata.Packager
}
if metadata, ok := p.Metadata.(pkg.SwiplPackEntry); ok {
author = formatPersonOrOrg(metadata.Packager, metadata.PackagerEmail)
}
if author == "" {
// TODO: this uses the Originator function for now until a better distinction can be made for supplier
return Originator(p)
@@ -40,7 +40,6 @@ func Test_OriginatorSupplier(t *testing.T) {
pkg.RustBinaryAuditEntry{},
pkg.RustCargoLockEntry{},
pkg.SwiftPackageManagerResolvedEntry{},
pkg.SwiplPackEntry{},
pkg.YarnLockEntry{},
)
tests := []struct {
@@ -138,8 +137,8 @@ func Test_OriginatorSupplier(t *testing.T) {
},
},
},
originator: "Organization: auth-spec",
supplier: "Organization: auth-spec",
originator: "Person: auth-spec",
supplier: "Person: auth-spec",
},
{
name: "from java -- fallback to impl vendor in main manifest section",
@@ -155,8 +154,8 @@ func Test_OriginatorSupplier(t *testing.T) {
},
},
},
originator: "Organization: auth-impl",
supplier: "Organization: auth-impl",
originator: "Person: auth-impl",
supplier: "Person: auth-impl",
},
{
name: "from java -- non-main manifest sections ignored",
@@ -337,19 +336,6 @@ func Test_OriginatorSupplier(t *testing.T) {
originator: "Organization: auth",
supplier: "Organization: auth",
},
{
name: "from swipl pack",
input: pkg.Package{
Metadata: pkg.SwiplPackEntry{
Author: "auth",
AuthorEmail: "auth@auth.gov",
Packager: "me",
PackagerEmail: "me@auth.com",
},
},
originator: "Person: auth (auth@auth.gov)",
supplier: "Person: me (me@auth.com)",
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
@@ -62,8 +62,6 @@ func SourceInfo(p pkg.Package) string {
answer = "acquired package info from Rockspec package file"
case pkg.SwiftPkg:
answer = "acquired package info from resolved Swift package manifest"
case pkg.SwiplPackPkg:
answer = "acquired package info from SWI Prolo pack package file"
case pkg.GithubActionPkg, pkg.GithubActionWorkflowPkg:
answer = "acquired package info from GitHub Actions workflow file or composite action file"
case pkg.WordpressPluginPkg:
@@ -263,14 +263,6 @@ func Test_SourceInfo(t *testing.T) {
"from resolved Swift package manifest",
},
},
{
input: pkg.Package{
Type: pkg.SwiplPackPkg,
},
expected: []string{
"acquired package info from SWI Prolo pack package file",
},
},
{
input: pkg.Package{
Type: pkg.GithubActionPkg,
@@ -9,11 +9,13 @@ import (
"path/filepath"
"strings"
"github.com/moby/sys/mountinfo"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
"github.com/anchore/stereoscope/pkg/file"
"github.com/anchore/stereoscope/pkg/filetree"
"github.com/anchore/syft/internal"
"github.com/anchore/syft/internal/bus"
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/event"
@@ -41,8 +43,7 @@ func newDirectoryIndexer(path, base string, visitors ...PathIndexVisitor) *direc
[]PathIndexVisitor{
requireFileInfo,
disallowByFileType,
skipPathsByMountTypeAndName(path),
},
newUnixSystemMountFinder().disallowUnixSystemRuntimePath},
visitors...,
),
errPaths: make(map[string]error),
@@ -449,6 +450,57 @@ func (r *directoryIndexer) disallowRevisitingVisitor(_, path string, _ os.FileIn
return nil
}
type unixSystemMountFinder struct {
disallowedMountPaths []string
}
func newUnixSystemMountFinder() unixSystemMountFinder {
infos, err := mountinfo.GetMounts(nil)
if err != nil {
log.WithFields("error", err).Warnf("unable to get system mounts")
return unixSystemMountFinder{}
}
return unixSystemMountFinder{
disallowedMountPaths: keepUnixSystemMountPaths(infos),
}
}
func keepUnixSystemMountPaths(infos []*mountinfo.Info) []string {
var mountPaths []string
for _, info := range infos {
if info == nil {
continue
}
// we're only interested in ignoring the logical filesystems typically found at these mount points:
// - /proc
// - procfs
// - proc
// - /sys
// - sysfs
// - /dev
// - devfs - BSD/darwin flavored systems and old linux systems
// - devtmpfs - driver core maintained /dev tmpfs
// - udev - userspace implementation that replaced devfs
// - tmpfs - used for /dev in special instances (within a container)
switch info.FSType {
case "proc", "procfs", "sysfs", "devfs", "devtmpfs", "udev", "tmpfs":
log.WithFields("mountpoint", info.Mountpoint).Debug("ignoring system mountpoint")
mountPaths = append(mountPaths, info.Mountpoint)
}
}
return mountPaths
}
func (f unixSystemMountFinder) disallowUnixSystemRuntimePath(_, path string, _ os.FileInfo, _ error) error {
if internal.HasAnyOfPrefixes(path, f.disallowedMountPaths...) {
return fs.SkipDir
}
return nil
}
func disallowByFileType(_, _ string, info os.FileInfo, _ error) error {
if info == nil {
// we can't filter out by filetype for non-existent files
@@ -10,6 +10,7 @@ import (
"testing"
"github.com/google/go-cmp/cmp"
"github.com/moby/sys/mountinfo"
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -461,3 +462,145 @@ func relativePath(basePath, givenPath string) string {
return relPath
}
func Test_disallowUnixSystemRuntimePath(t *testing.T) {
unixSubject := unixSystemMountFinder{
// mock out detecting the mount points
disallowedMountPaths: []string{"/proc", "/sys", "/dev"},
}
tests := []struct {
name string
path string
base string
expected error
}{
{
name: "relative path to proc is allowed",
path: "proc/place",
},
{
name: "relative path within proc is not allowed",
path: "/proc/place",
expected: fs.SkipDir,
},
{
name: "path exactly to proc is not allowed",
path: "/proc",
expected: fs.SkipDir,
},
{
name: "similar to proc",
path: "/pro/c",
},
{
name: "similar to proc",
path: "/pro",
},
{
name: "dev is not allowed",
path: "/dev",
expected: fs.SkipDir,
},
{
name: "sys is not allowed",
path: "/sys",
expected: fs.SkipDir,
},
{
name: "unrelated allowed path",
path: "/something/sys",
},
{
name: "do not consider base when matching paths (non-matching)",
base: "/a/b/c",
path: "/a/b/c/dev",
},
{
name: "do not consider base when matching paths (matching)",
base: "/a/b/c",
path: "/dev",
expected: fs.SkipDir,
},
}
for _, test := range tests {
t.Run(test.path, func(t *testing.T) {
assert.Equal(t, test.expected, unixSubject.disallowUnixSystemRuntimePath(test.base, test.path, nil, nil))
})
}
}
func Test_keepUnixSystemMountPaths(t *testing.T) {
tests := []struct {
name string
infos []*mountinfo.Info
want []string
}{
{
name: "all valid filesystems",
infos: []*mountinfo.Info{
{
Mountpoint: "/etc/hostname",
FSType: "/dev/vda1",
},
{
Mountpoint: "/sys/fs/cgroup",
FSType: "cgroup",
},
{
Mountpoint: "/",
FSType: "overlay",
},
},
want: nil,
},
{
name: "no valid filesystems",
infos: []*mountinfo.Info{
{
Mountpoint: "/proc",
FSType: "proc",
},
{
Mountpoint: "/proc-2",
FSType: "procfs",
},
{
Mountpoint: "/sys",
FSType: "sysfs",
},
{
Mountpoint: "/dev",
FSType: "devfs",
},
{
Mountpoint: "/dev-u",
FSType: "udev",
},
{
Mountpoint: "/dev-tmp",
FSType: "devtmpfs",
},
{
Mountpoint: "/run",
FSType: "tmpfs",
},
},
want: []string{
"/proc",
"/proc-2",
"/sys",
"/dev",
"/dev-u",
"/dev-tmp",
"/run",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, keepUnixSystemMountPaths(tt.infos))
})
}
}
-157
View File
@@ -1,157 +0,0 @@
package fileresolver
import (
"io/fs"
"os"
"sort"
"strings"
"github.com/moby/sys/mountinfo"
"github.com/anchore/syft/internal/log"
)
type pathSkipper struct {
// scanTarget is the root path that is being scanned (without any base-path logic applied).
scanTarget string
// ignorableMountTypes is a set of mount types that should be ignored. Optionally a list of paths (the map values)
// can be provided that this mount type should be ignored at. For example in some containers /dev is mounted
// as a tmpfs and should be ignored, but /tmp should not be ignored. An empty list of paths means that paths
// within the mount type should always be ignored.
ignorableMountTypes map[string][]string
// current mount paths for the current system
mounts []*mountinfo.Info
mountsByType map[string][]*mountinfo.Info
}
// skipPathsByMountTypeAndName accepts the root path and returns a PathIndexVisitor that will skip paths based
// the filesystem type, the mountpoint, and configured blocklist paths for each filesystem type.
// This will help syft dodge filesystem topologies that have the potential to make the search space much bigger in
// areas known to not traditionally contain files of interest (installed software). It is meant to allow scanning
// "/" on a unix host to succeed, while also not causing any files in a narrow directory scan to be skipped unnecessarily.
func skipPathsByMountTypeAndName(root string) PathIndexVisitor {
infos, err := mountinfo.GetMounts(nil)
if err != nil {
log.WithFields("error", err).Warnf("unable to get system mounts")
return func(_ string, _ string, _ os.FileInfo, _ error) error {
return nil
}
}
return newPathSkipperFromMounts(root, infos).pathIndexVisitor
}
func newPathSkipperFromMounts(root string, infos []*mountinfo.Info) pathSkipper {
// we're only interested in ignoring the logical filesystems typically found at these mount points:
// - /proc
// - procfs
// - proc
// - /sys
// - sysfs
// - /dev
// - devfs - BSD/darwin flavored systems and old linux systems
// - devtmpfs - driver core maintained /dev tmpfs
// - udev - userspace implementation that replaced devfs
// - tmpfs - used for /dev in special instances (within a container)
ignorableMountTypes := map[string][]string{
"proc": nil,
"procfs": nil,
"sysfs": nil,
"devfs": nil,
"devtmpfs": nil,
"udev": nil,
// note: there should be no order required (e.g. search /sys/thing before /sys) since that would imply that
// we could not ignore a nested path within a path that would be ignored anyway.
"tmpfs": {"/run", "/dev", "/var/run", "/var/lock", "/sys"},
}
// The longest path is the most specific path, e.g.
// if / is mounted as tmpfs, but /home/syft/permanent is mounted as ext4,
// then the mount type for /home/syft/permanent/foo is ext4, and the mount info
// stating that /home/syft/permanent is ext4 has the longer mount point.
sort.Slice(infos, func(i, j int) bool {
return len(infos[i].Mountpoint) > len(infos[j].Mountpoint)
})
mountsByType := make(map[string][]*mountinfo.Info)
for _, mi := range infos {
mountsByType[mi.FSType] = append(mountsByType[mi.FSType], mi)
}
return pathSkipper{
scanTarget: root,
ignorableMountTypes: ignorableMountTypes,
mounts: infos,
mountsByType: mountsByType,
}
}
func (ps pathSkipper) pathIndexVisitor(_ string, givenPath string, _ os.FileInfo, _ error) error {
for _, mi := range ps.mounts {
conditionalPaths, ignorable := ps.ignorableMountTypes[mi.FSType]
if len(conditionalPaths) == 0 {
// Rule 1: ignore any path within a mount point that is of the given filesystem type unconditionally
if !containsPath(givenPath, mi.Mountpoint) {
continue
}
if !ignorable {
// we've matched on the most specific path at this point, which means we should stop searching
// mount points for this path
break
}
log.WithFields(
"path", givenPath,
"mountpoint", mi.Mountpoint,
"fs", mi.FSType,
).Debug("ignoring path based on mountpoint filesystem type")
return fs.SkipDir
}
// Rule 2: ignore any path within a mount point that is of the given filesystem type, only if
// the path is on a known blocklist of paths for that filesystem type.
// For example: /dev can be mounted as a tmpfs, which should always be skipped.
for _, conditionalPath := range conditionalPaths {
if !containsPath(givenPath, conditionalPath) {
continue
}
log.WithFields(
"path", givenPath,
"mountpoint", mi.Mountpoint,
"fs", mi.FSType,
"condition", conditionalPath,
).Debug("ignoring path based on mountpoint filesystem type")
return fs.SkipDir
}
}
return nil
}
func containsPath(p1, p2 string) bool {
p1Clean := simpleClean(p1)
p2Clean := simpleClean(p2)
if p1Clean == p2Clean {
return true
}
return strings.HasPrefix(p1Clean, p2Clean+"/")
}
func simpleClean(p string) string {
p = strings.TrimSpace(p)
if p == "" {
return "."
}
if p == "/" {
return "/"
}
return strings.TrimSuffix(p, "/")
}
@@ -1,395 +0,0 @@
package fileresolver
import (
"io/fs"
"testing"
"github.com/moby/sys/mountinfo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_newPathSkipper(t *testing.T) {
type expect struct {
path string
wantErr assert.ErrorAssertionFunc
}
unixSubject := []*mountinfo.Info{
{
Mountpoint: "/proc",
FSType: "procfs",
},
{
Mountpoint: "/sys",
FSType: "sysfs",
},
{
Mountpoint: "/dev",
FSType: "devfs",
},
{
Mountpoint: "/",
FSType: "/dev/disk3s1s1",
},
{
Mountpoint: "/dev/shm",
FSType: "shm",
},
{
Mountpoint: "/tmp",
FSType: "tmpfs",
},
}
tests := []struct {
name string
root string
base string
mounts []*mountinfo.Info
want []expect
}{
{
name: "happy path",
root: "/somewhere",
mounts: []*mountinfo.Info{
{
Mountpoint: "/home/somewhere/else",
FSType: "/dev/disk3s6",
},
{
Mountpoint: "/somewhere",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
// within a known mountpoint with valid type (1)
path: "/somewhere/dev",
},
{
// is a known mountpoint with valid type
path: "/somewhere",
},
{
// within a known mountpoint with valid type (2)
path: "/home/somewhere/else/too",
},
{
// outside of any known mountpoint should not be an error
path: "/bogus",
},
},
},
{
name: "ignore paths within a scan target",
root: "/somewhere",
mounts: []*mountinfo.Info{
{
Mountpoint: "/somewhere/doesnt/matter/proc",
FSType: "procfs",
},
{
Mountpoint: "/somewhere",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
// within a known mountpoint with valid type (1)
path: "/somewhere/dev",
},
{
// is a known mountpoint with valid type
path: "/somewhere",
},
{
// mountpoint that should be ignored
path: "/somewhere/doesnt/matter/proc",
wantErr: assertSkipErr(),
},
{
// within a mountpoint that should be ignored
path: "/somewhere/doesnt/matter/proc",
wantErr: assertSkipErr(),
},
},
},
{
name: "nested mountpoints behave correctly",
root: "/somewhere",
mounts: []*mountinfo.Info{
{
Mountpoint: "/somewhere/dev",
FSType: "devfs",
},
{
Mountpoint: "/somewhere/dev/includeme",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
// is a known mountpoint with valid type
path: "/somewhere/dev",
wantErr: assertSkipErr(),
},
{
// is a known mountpoint with valid type
path: "/somewhere/dev/includeme",
},
{
// within a known mountpoint with valid type
path: "/somewhere/dev/includeme/too!",
},
},
},
{
name: "keep some tmpfs mounts conditionally",
root: "/",
mounts: []*mountinfo.Info{
{
Mountpoint: "/run/somewhere",
FSType: "tmpfs",
},
{
Mountpoint: "/run/terrafirma",
FSType: "/dev/disk3s8",
},
{
Mountpoint: "/tmp",
FSType: "tmpfs",
},
{
Mountpoint: "/else/othertmp",
FSType: "tmpfs",
},
{
Mountpoint: "/else/othertmp/includeme",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
// since /run is explicitly ignored, this should be skipped
path: "/run/somewhere/else",
wantErr: assertSkipErr(),
},
{
path: "/run/terrafirma",
},
{
path: "/run/terrafirma/nested",
},
{
path: "/tmp",
},
{
path: "/else/othertmp/includeme",
},
{
path: "/else/othertmp/includeme/nested",
},
{
// no mount path, so we should include it
path: "/somewhere/dev/includeme",
},
{
// keep additional tmpfs mounts that are not explicitly ignored
path: "/else/othertmp",
},
},
},
{
name: "ignore known trixy tmpfs paths",
root: "/",
mounts: []*mountinfo.Info{
{
Mountpoint: "/",
FSType: "/dev/disk3s7",
},
{
Mountpoint: "/dev",
FSType: "tmpfs",
},
{
Mountpoint: "/run",
FSType: "tmpfs",
},
{
Mountpoint: "/var/run",
FSType: "tmpfs",
},
{
Mountpoint: "/var/lock",
FSType: "tmpfs",
},
{
Mountpoint: "/sys",
FSType: "tmpfs",
},
{
Mountpoint: "/tmp",
FSType: "tmpfs",
},
},
want: []expect{
{
path: "/dev",
wantErr: assertSkipErr(),
},
{
path: "/run",
wantErr: assertSkipErr(),
},
{
path: "/var/run",
wantErr: assertSkipErr(),
},
{
path: "/var/lock",
wantErr: assertSkipErr(),
},
{
path: "/sys",
wantErr: assertSkipErr(),
},
// show that we honor ignoring nested paths
{
path: "/sys/nested",
wantErr: assertSkipErr(),
},
// show that paths outside of the known mountpoints are not skipped
{
path: "/stuff",
},
// show that we allow other tmpfs paths that are not on the blocklist
{
path: "/tmp/allowed",
},
// show sibling paths with same prefix (e.g. /sys vs /system) to that of not allowed paths are not skipped
{
path: "/system",
},
},
},
{
name: "test unix paths",
mounts: unixSubject,
root: "/",
want: []expect{
{
// relative path to proc is allowed
path: "proc/place",
},
{
// relative path within proc is not allowed
path: "/proc/place",
wantErr: assertSkipErr(),
},
{
// path exactly to proc is not allowed
path: "/proc",
wantErr: assertSkipErr(),
},
{
// similar to proc
path: "/pro/c",
},
{
// similar to proc
path: "/pro",
},
{
// dev is not allowed
path: "/dev",
wantErr: assertSkipErr(),
},
{
// sys is not allowed
path: "/sys",
wantErr: assertSkipErr(),
},
},
},
{
name: "test unix paths with base",
mounts: unixSubject,
root: "/",
base: "/a/b/c",
want: []expect{
{
// do not consider base when matching paths (non-matching)
path: "/a/b/c/dev",
},
{
// do not consider base when matching paths (matching)
path: "/dev",
wantErr: assertSkipErr(),
},
},
},
{
name: "mimic nixos setup",
root: "/",
mounts: []*mountinfo.Info{
{
Mountpoint: "/",
FSType: "tmpfs", // this is an odd setup, but valid
},
{
Mountpoint: "/home",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
path: "/home/somewhere",
},
{
path: "/home",
},
{
path: "/somewhere",
},
{
// still not allowed...
path: "/run",
wantErr: assertSkipErr(),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.base == "" {
tt.base = tt.root
}
require.NotEmpty(t, tt.want)
ps := newPathSkipperFromMounts(tt.root, tt.mounts)
for _, exp := range tt.want {
t.Run(exp.path, func(t *testing.T) {
got := ps.pathIndexVisitor(tt.base, exp.path, nil, nil)
if exp.wantErr == nil {
assert.NoError(t, got)
return
}
exp.wantErr(t, got)
})
}
})
}
}
func assertSkipErr() assert.ErrorAssertionFunc {
return assertErrorIs(fs.SkipDir)
}
func assertErrorIs(want error) assert.ErrorAssertionFunc {
return func(t assert.TestingT, got error, msgAndArgs ...interface{}) bool {
return assert.ErrorIs(t, got, want, msgAndArgs...)
}
}
@@ -49,7 +49,6 @@ func AllTypes() []any {
pkg.RustBinaryAuditEntry{},
pkg.RustCargoLockEntry{},
pkg.SwiftPackageManagerResolvedEntry{},
pkg.SwiplPackEntry{},
pkg.WordpressPluginEntry{},
pkg.YarnLockEntry{},
}
-1
View File
@@ -102,7 +102,6 @@ var jsonTypes = makeJSONTypes(
jsonNames(pkg.RpmDBEntry{}, "rpm-db-entry", "RpmMetadata", "RpmdbMetadata"),
jsonNamesWithoutLookup(pkg.RpmArchive{}, "rpm-archive", "RpmMetadata"), // the legacy value is split into two types, where the other is preferred
jsonNames(pkg.SwiftPackageManagerResolvedEntry{}, "swift-package-manager-lock-entry", "SwiftPackageManagerMetadata"),
jsonNames(pkg.SwiplPackEntry{}, "swiplpack-package"),
jsonNames(pkg.RustCargoLockEntry{}, "rust-cargo-lock-entry", "RustCargoPackageMetadata"),
jsonNamesWithoutLookup(pkg.RustBinaryAuditEntry{}, "rust-cargo-audit-entry", "RustCargoPackageMetadata"), // the legacy value is split into two types, where the other is preferred
jsonNames(pkg.WordpressPluginEntry{}, "wordpress-plugin-entry", "WordpressMetadata"),
@@ -473,6 +473,12 @@ func Test_JSONName_JSONLegacyName(t *testing.T) {
expectedJSONName: "rpm-archive",
expectedLegacyName: "RpmMetadata", // note: conflicts with <=v11.x schema for "rpm-db-entry" metadata type
},
{
name: "SwiftPackageManagerMetadata",
metadata: pkg.SwiftPackageManagerResolvedEntry{},
expectedJSONName: "swift-package-manager-lock-entry",
expectedLegacyName: "SwiftPackageManagerMetadata",
},
{
name: "CargoPackageMetadata",
metadata: pkg.RustCargoLockEntry{},
+4 -29
View File
@@ -15,34 +15,9 @@ type ClassifierMatch struct {
// ELFBinaryPackageNoteJSONPayload Represents metadata captured from the .note.package section of the binary
type ELFBinaryPackageNoteJSONPayload struct {
// these are well-known fields as defined by systemd ELF package metadata "spec" https://systemd.io/ELF_PACKAGE_METADATA/
// Type is the type of the package (e.g. "rpm", "deb", "apk", etc.)
Type string `json:"type,omitempty"`
// Architecture of the binary package (e.g. "amd64", "arm", etc.)
Architecture string `json:"architecture,omitempty"`
// OS CPE is a CPE name for the OS, typically corresponding to CPE_NAME in os-release (e.g. cpe:/o:fedoraproject:fedora:33)
OSCPE string `json:"osCPE,omitempty"`
// OS is the OS name, typically corresponding to ID in os-release (e.g. "fedora")
OS string `json:"os,omitempty"`
// osVersion is the version of the OS, typically corresponding to VERSION_ID in os-release (e.g. "33")
OSVersion string `json:"osVersion,omitempty"`
// these are additional fields that are not part of the systemd spec
// System is a context-specific name for the system that the binary package is intended to run on or a part of
System string `json:"system,omitempty"`
// Vendor is the individual or organization that produced the source code for the binary
Vendor string `json:"vendor,omitempty"`
// SourceRepo is the URL to the source repository for which the binary was built from
Type string `json:"type,omitempty"`
Vendor string `json:"vendor,omitempty"`
System string `json:"system,omitempty"`
SourceRepo string `json:"sourceRepo,omitempty"`
// Commit is the commit hash of the source repository for which the binary was built from
Commit string `json:"commit,omitempty"`
Commit string `json:"commit,omitempty"`
}
+2 -4
View File
@@ -22,10 +22,8 @@ func dbEntryDependencySpecifier(p pkg.Package) dependency.Specification {
provides = append(provides, stripVersionSpecifiers(meta.Provides)...)
return dependency.Specification{
ProvidesRequires: dependency.ProvidesRequires{
Provides: provides,
Requires: stripVersionSpecifiers(meta.Dependencies),
},
Provides: provides,
Requires: stripVersionSpecifiers(meta.Dependencies),
}
}
+6 -12
View File
@@ -25,10 +25,8 @@ func Test_dbEntryDependencySpecifier(t *testing.T) {
},
},
want: dependency.Specification{
ProvidesRequires: dependency.ProvidesRequires{
Provides: []string{"package-c", "a-thing"},
Requires: []string{"b-thing"},
},
Provides: []string{"package-c", "a-thing"},
Requires: []string{"b-thing"},
},
},
{
@@ -41,10 +39,8 @@ func Test_dbEntryDependencySpecifier(t *testing.T) {
},
},
want: dependency.Specification{
ProvidesRequires: dependency.ProvidesRequires{
Provides: []string{"package-a", "so:libc.musl-x86_64.so.1"},
Requires: []string{"so:libc.musl-x86_64.so.2"},
},
Provides: []string{"package-a", "so:libc.musl-x86_64.so.1"},
Requires: []string{"so:libc.musl-x86_64.so.2"},
},
},
{
@@ -57,10 +53,8 @@ func Test_dbEntryDependencySpecifier(t *testing.T) {
},
},
want: dependency.Specification{
ProvidesRequires: dependency.ProvidesRequires{
Provides: []string{"package-a"},
Requires: nil,
},
Provides: []string{"package-a"},
Requires: nil,
},
},
}
+2 -4
View File
@@ -34,10 +34,8 @@ func dbEntryDependencySpecifier(p pkg.Package) dependency.Specification {
}
return dependency.Specification{
ProvidesRequires: dependency.ProvidesRequires{
Provides: provides,
Requires: requires,
},
Provides: provides,
Requires: requires,
}
}
+6 -12
View File
@@ -25,10 +25,8 @@ func Test_dbEntryDependencySpecifier(t *testing.T) {
},
},
want: dependency.Specification{
ProvidesRequires: dependency.ProvidesRequires{
Provides: []string{"package-c", "a-thing", "a-thing"}, // note: gets deduplicated downstream
Requires: []string{"b-thing"},
},
Provides: []string{"package-c", "a-thing", "a-thing"}, // note: gets deduplicated downstream
Requires: []string{"b-thing"},
},
},
{
@@ -41,10 +39,8 @@ func Test_dbEntryDependencySpecifier(t *testing.T) {
},
},
want: dependency.Specification{
ProvidesRequires: dependency.ProvidesRequires{
Provides: []string{"package-a", "libtree-sitter.so.me=1-64", "libtree-sitter.so.me"},
Requires: []string{"libtree-sitter.so.thing=2-64"},
},
Provides: []string{"package-a", "libtree-sitter.so.me=1-64", "libtree-sitter.so.me"},
Requires: []string{"libtree-sitter.so.thing=2-64"},
},
},
{
@@ -57,10 +53,8 @@ func Test_dbEntryDependencySpecifier(t *testing.T) {
},
},
want: dependency.Specification{
ProvidesRequires: dependency.ProvidesRequires{
Provides: []string{"package-a"},
Requires: nil,
},
Provides: []string{"package-a"},
Requires: nil,
},
},
}
@@ -48,17 +48,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("arangodb-binary"),
},
},
{
logicalFixture: "arangodb/3.12.0-2/linux-amd64",
expected: pkg.Package{
Name: "arangodb",
Version: "3.12.0-2",
Type: "binary",
PURL: "pkg:generic/arangodb@3.12.0-2",
Locations: locations("arangosh"),
Metadata: metadata("arangodb-binary"),
},
},
{
logicalFixture: "postgres/15beta4/linux-amd64",
expected: pkg.Package{
@@ -204,17 +193,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("traefik-binary"),
},
},
{
logicalFixture: "traefik/3.0.4/linux-riscv64",
expected: pkg.Package{
Name: "traefik",
Version: "3.0.4",
Type: "binary",
PURL: "pkg:generic/traefik@3.0.4",
Locations: locations("traefik"),
Metadata: metadata("traefik-binary"),
},
},
{
logicalFixture: "memcached/1.6.18/linux-amd64",
expected: pkg.Package{
@@ -638,16 +616,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("busybox-binary", "[", "busybox"),
},
},
{
logicalFixture: `util-linux/2.37.4/linux-amd64`,
expected: pkg.Package{
Name: "util-linux",
Version: "2.37.4",
PURL: "pkg:generic/util-linux@2.37.4",
Locations: locations("getopt"),
Metadata: metadata("util-linux-binary"),
},
},
{
logicalFixture: "java-jre-openjdk/1.8.0_352-b08/linux-amd64",
expected: pkg.Package{
@@ -918,28 +886,6 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
Metadata: metadata("erlang-library"),
},
},
{
logicalFixture: "erlang/27.0/linux-amd64",
expected: pkg.Package{
Name: "erlang",
Version: "27.0",
Type: "binary",
PURL: "pkg:generic/erlang@27.0",
Locations: locations("beam.smp"),
Metadata: metadata("erlang-alpine-binary"),
},
},
{
logicalFixture: "swipl/9.3.8/linux-amd64",
expected: pkg.Package{
Name: "swipl",
Version: "9.3.8",
Type: "binary",
PURL: "pkg:generic/swipl@9.3.8",
Locations: locations("swipl"),
Metadata: metadata("swipl-binary"),
},
},
{
logicalFixture: "nginx/1.25.1/linux-amd64",
expected: pkg.Package{
+2 -39
View File
@@ -173,15 +173,6 @@ func DefaultClassifiers() []Classifier {
PURL: mustPURL("pkg:generic/busybox@version"),
CPEs: singleCPE("cpe:2.3:a:busybox:busybox:*:*:*:*:*:*:*:*"),
},
{
Class: "util-linux-binary",
FileGlob: "**/getopt",
EvidenceMatcher: FileContentsVersionMatcher(
`\x00util-linux\s(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`),
Package: "util-linux",
PURL: mustPURL("pkg:generic/util-linux@version"),
CPEs: singleCPE("cpe:2.3:a:kernel:util-linux:*:*:*:*:*:*:*:*"),
},
{
Class: "haproxy-binary",
FileGlob: "**/haproxy",
@@ -263,8 +254,7 @@ func DefaultClassifiers() []Classifier {
EvidenceMatcher: FileContentsVersionMatcher(
// [NUL]v1.7.34[NUL]
// [NUL]2.9.6[NUL]
// 3.0.4[NUL]
`(?m)(\x00|\x{FFFD})?v?(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00`),
`(?m)(\x00|\x{FFFD})v?(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00`),
Package: "traefik",
PURL: mustPURL("pkg:generic/traefik@version"),
CPEs: singleCPE("cpe:2.3:a:traefik:traefik:*:*:*:*:*:*:*:*"),
@@ -273,7 +263,7 @@ func DefaultClassifiers() []Classifier {
Class: "arangodb-binary",
FileGlob: "**/arangosh",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)\x00*(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-[0-9]+)?)\s\[linux\]`),
`(?m)ArangoDB\s\x00*(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\s\[linux\]`),
Package: "arangodb",
PURL: mustPURL("pkg:generic/arangodb@version"),
CPEs: singleCPE("cpe:2.3:a:arangodb:arangodb:*:*:*:*:*:*:*:*"),
@@ -394,23 +384,6 @@ func DefaultClassifiers() []Classifier {
PURL: mustPURL("pkg:generic/erlang@version"),
CPEs: singleCPE("cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"),
},
{
Class: "erlang-alpine-binary",
FileGlob: "**/beam.smp",
EvidenceMatcher: evidenceMatchers(
FileContentsVersionMatcher(
// <artificial>[NUL]/usr/src/otp_src_25.3.2.6/erts/
`(?m)/src/otp_src_(?P<version>[0-9]+\.[0-9]+(\.[0-9]+){0,2}(-rc[0-9])?)/erts/`,
),
FileContentsVersionMatcher(
// <artificial>[NUL]/usr/local/src/otp-25.3.2.7/erts/
`(?m)/usr/local/src/otp-(?P<version>[0-9]+\.[0-9]+(\.[0-9]+){0,2}(-rc[0-9])?)/erts/`,
),
),
Package: "erlang",
PURL: mustPURL("pkg:generic/erlang@version"),
CPEs: singleCPE("cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"),
},
{
Class: "erlang-library",
FileGlob: "**/liberts_internal.a",
@@ -428,16 +401,6 @@ func DefaultClassifiers() []Classifier {
PURL: mustPURL("pkg:generic/erlang@version"),
CPEs: singleCPE("cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"),
},
{
Class: "swipl-binary",
FileGlob: "**/swipl",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)swipl-(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\/`,
),
Package: "swipl",
PURL: mustPURL("pkg:generic/swipl@version"),
CPEs: singleCPE("cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"),
},
{
Class: "consul-binary",
FileGlob: "**/consul",
+7 -70
View File
@@ -2,18 +2,17 @@ package binary
import (
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
func newELFPackage(metadata elfBinaryPackageNotes, locations file.LocationSet) pkg.Package {
func newELFPackage(metadata elfBinaryPackageNotes, locations file.LocationSet, licenses []pkg.License) pkg.Package {
p := pkg.Package{
Name: metadata.Name,
Version: metadata.Version,
Licenses: pkg.NewLicenseSet(pkg.NewLicense(metadata.License)),
Licenses: pkg.NewLicenseSet(licenses...),
PURL: packageURL(metadata),
Type: pkgType(metadata.Type),
Type: pkg.BinaryPkg,
Locations: locations,
Metadata: metadata.ELFBinaryPackageNoteJSONPayload,
}
@@ -24,75 +23,13 @@ func newELFPackage(metadata elfBinaryPackageNotes, locations file.LocationSet) p
}
func packageURL(metadata elfBinaryPackageNotes) string {
var qualifiers []packageurl.Qualifier
os := metadata.OS
osVersion := metadata.OSVersion
atts, err := cpe.NewAttributes(metadata.OSCPE)
if err == nil {
// only "upgrade" the OS information if there is something more specific to use in it's place
if os == "" && osVersion == "" || os == "" && atts.Version != "" || atts.Product != "" && osVersion == "" {
os = atts.Product
osVersion = atts.Version
}
}
if os != "" {
osQualifier := os
if osVersion != "" {
osQualifier += "-" + osVersion
}
qualifiers = append(qualifiers, packageurl.Qualifier{
Key: "distro",
Value: osQualifier,
})
}
ty := purlDistroType(metadata.Type)
namespace := os
if ty == packageurl.TypeGeneric || os == "" {
namespace = metadata.System
}
// TODO: what if the System value is not set?
return packageurl.NewPackageURL(
ty,
namespace,
packageurl.TypeGeneric,
metadata.System,
metadata.Name,
metadata.Version,
qualifiers,
nil,
"",
).ToString()
}
const alpmType = "alpm"
func purlDistroType(ty string) string {
switch ty {
case "rpm":
return packageurl.TypeRPM
case "deb":
return packageurl.TypeDebian
case "apk":
return packageurl.TypeAlpine
case alpmType:
return alpmType
}
return packageurl.TypeGeneric
}
func pkgType(ty string) pkg.Type {
switch ty {
case "rpm":
return pkg.RpmPkg
case "deb":
return pkg.DebPkg
case "apk":
return pkg.ApkPkg
case alpmType:
return pkg.AlpmPkg
}
return pkg.BinaryPkg
}
@@ -1,10 +1,8 @@
package binary
import (
"bytes"
"context"
"debug/elf"
"encoding/binary"
"encoding/json"
"fmt"
@@ -81,7 +79,7 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
}
// create a package for each unique name/version pair (based on the first note found)
pkgs = append(pkgs, newELFPackage(notes[0], noteLocations))
pkgs = append(pkgs, newELFPackage(notes[0], noteLocations, nil))
}
// why not return relationships? We have an executable cataloger that will note the dynamic libraries imported by
@@ -156,42 +154,11 @@ func getELFNotes(r file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
return nil, err
}
if len(notes) == 0 {
var metadata elfBinaryPackageNotes
if err := json.Unmarshal(notes, &metadata); err != nil {
log.WithFields("file", r.Location.Path(), "error", err).Trace("unable to unmarshal ELF package notes as JSON")
return nil, nil
}
{
var metadata elfBinaryPackageNotes
if err := json.Unmarshal(notes, &metadata); err == nil {
return &metadata, nil
}
}
{
var header elf64SectionHeader
headerSize := binary.Size(header) / 4
if len(notes) > headerSize {
var metadata elfBinaryPackageNotes
newPayload := bytes.TrimRight(notes[headerSize:], "\x00")
if err := json.Unmarshal(newPayload, &metadata); err == nil {
return &metadata, nil
}
log.WithFields("file", r.Location.Path(), "error", err).Trace("unable to unmarshal ELF package notes as JSON")
}
}
return nil, err
}
type elf64SectionHeader struct {
ShName uint32
ShType uint32
ShFlags uint64
ShAddr uint64
ShOffset uint64
ShSize uint64
ShLink uint32
ShInfo uint32
ShAddralign uint64
ShEntsize uint64
return &metadata, err
}

Some files were not shown because too many files have changed in this diff Show More