mirror of
https://github.com/anchore/syft.git
synced 2026-10-15 15:47:23 +02:00
Compare commits
113
Commits
+6
-6
@@ -18,7 +18,7 @@ tools:
|
||||
# used to sign mac binaries at release
|
||||
- name: quill
|
||||
version:
|
||||
want: v0.4.2
|
||||
want: v0.5.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: anchore/quill
|
||||
@@ -26,7 +26,7 @@ tools:
|
||||
# used for linting
|
||||
- name: golangci-lint
|
||||
version:
|
||||
want: v1.61.0
|
||||
want: v1.62.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.4.0
|
||||
want: v2.4.1
|
||||
method: github-release
|
||||
with:
|
||||
repo: sigstore/cosign
|
||||
@@ -58,7 +58,7 @@ tools:
|
||||
# used to release all artifacts
|
||||
- name: goreleaser
|
||||
version:
|
||||
want: v2.3.2
|
||||
want: v2.4.5
|
||||
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.39.2
|
||||
want: v3.40.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: go-task/task
|
||||
@@ -111,7 +111,7 @@ tools:
|
||||
# used for triggering a release
|
||||
- name: gh
|
||||
version:
|
||||
want: v2.57.0
|
||||
want: v2.62.0
|
||||
method: github-release
|
||||
with:
|
||||
repo: cli/cli
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Description
|
||||
|
||||
Please include a summary of the changes along with any relevant motivation and context,
|
||||
or link to an issue where this is explained.
|
||||
|
||||
<!-- If this completes an issue, please include: -->
|
||||
|
||||
- Fixes #
|
||||
|
||||
## Type of change
|
||||
|
||||
<!-- Delete any that are not relevant -->
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (please discuss with the team first; Syft is 1.0 software and we won't accept breaking changes without going to 2.0)
|
||||
- [ ] Documentation (updates the documentation)
|
||||
- [ ] Chore (improve the developer experience, fix a test flake, etc, without changing the visible behavior of Syft)
|
||||
- [ ] Performance (make Syft run faster or use less memory, without changing visible behavior much)
|
||||
|
||||
# Checklist:
|
||||
|
||||
- [ ] I have added unit tests that cover changed behavior
|
||||
- [ ] I have tested my code in common scenarios and confirmed there are no regressions
|
||||
- [ ] I have added comments to my code, particularly in hard-to-understand sections
|
||||
@@ -17,13 +17,13 @@ jobs:
|
||||
# the job by event.
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
- name: Restore base benchmark result
|
||||
uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
|
||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
with:
|
||||
path: test/results/benchmark-main.txt
|
||||
# use base sha for PR or new commit hash for main push in benchmark result key
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
OUTPUT="${OUTPUT//$'\r'/'%0D'}" # URL encode all '\r' characters
|
||||
echo "result=$OUTPUT" >> $GITHUB_OUTPUT
|
||||
|
||||
- uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
- uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # v4.4.3
|
||||
with:
|
||||
name: benchmark-test-results
|
||||
path: test/results/**/*
|
||||
|
||||
@@ -36,16 +36,16 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 #v5.0.2
|
||||
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed #v5.1.0
|
||||
with:
|
||||
go-version-file: go.mod
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@294a9d92911152fe08befb9ec03e240add280cb3 #v3.26.8
|
||||
uses: github/codeql-action/init@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f #v3.27.4
|
||||
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@294a9d92911152fe08befb9ec03e240add280cb3 #v3.26.8
|
||||
uses: github/codeql-action/autobuild@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f #v3.27.4
|
||||
|
||||
# ℹ️ 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@294a9d92911152fe08befb9ec03e240add280cb3 #v3.26.8
|
||||
uses: github/codeql-action/analyze@ea9e4e37992a54ee68a9622e985e60c8e8f12d9f #v3.27.4
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- run: python .github/scripts/labeler.py
|
||||
env:
|
||||
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
release:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Update version file
|
||||
run: make ci-release-version-file
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
environment: release
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Check if running on main
|
||||
if: github.ref != 'refs/heads/main'
|
||||
@@ -105,7 +105,7 @@ jobs:
|
||||
# required for goreleaser signs section with cosign
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -148,9 +148,10 @@ jobs:
|
||||
# for updating brew formula in anchore/homebrew-syft
|
||||
GITHUB_BREW_TOKEN: ${{ secrets.ANCHOREOPS_GITHUB_OSS_WRITE_TOKEN }}
|
||||
|
||||
- uses: anchore/sbom-action@61119d458adab75f756bc0b9e4bde25725f86a7a #v0.17.2
|
||||
- uses: anchore/sbom-action@fc46e51fd3cb168ffb36c6d1915723c47db58abb #v0.17.7
|
||||
continue-on-error: true
|
||||
with:
|
||||
file: go.mod
|
||||
artifact-name: sbom.spdx.json
|
||||
|
||||
- uses: 8398a7/action-slack@28ba43ae48961b90635b50953d216767a6bea486 #v3.16.2
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
name: "Manage Awaiting Response Label"
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
uses: "anchore/workflows/.github/workflows/remove-awaiting-response-label.yaml@main"
|
||||
secrets:
|
||||
token: ${{ secrets.OSS_PROJECT_GH_TOKEN }}
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
permissions:
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
@@ -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@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
@@ -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@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
|
||||
@@ -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@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 #v5.0.2
|
||||
- uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed #v5.1.0
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
stable: ${{ env.GO_STABLE_VERSION }}
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
name: "Static analysis"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- 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@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -47,7 +47,7 @@ jobs:
|
||||
name: "Integration tests"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -65,7 +65,7 @@ jobs:
|
||||
name: "Build snapshot artifacts"
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
# why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
|
||||
# see https://github.com/actions/upload-artifact/issues/199 for more info
|
||||
- name: Upload snapshot artifacts
|
||||
uses: actions/cache/save@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
|
||||
uses: actions/cache/save@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
with:
|
||||
# we need to preserve the snapshot data itself as well as the task data that confirms if the
|
||||
# snapshot build is stale or not. Otherwise the downstream jobs will attempt to rebuild the snapshot
|
||||
@@ -95,7 +95,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -104,7 +104,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
|
||||
uses: actions/cache/restore@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -139,9 +139,9 @@ jobs:
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Install Cosign
|
||||
uses: sigstore/cosign-installer@v3.6.0
|
||||
uses: sigstore/cosign-installer@v3.7.0
|
||||
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -152,7 +152,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
|
||||
uses: actions/cache/restore@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -178,7 +178,7 @@ jobs:
|
||||
needs: [Build-Snapshot-Artifacts]
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Bootstrap environment
|
||||
uses: ./.github/actions/bootstrap
|
||||
@@ -187,7 +187,7 @@ jobs:
|
||||
|
||||
- name: Download snapshot build
|
||||
id: snapshot-cache
|
||||
uses: actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 #v4.0.2
|
||||
uses: actions/cache/restore@6849a6489940f00c2f30c0fb92c6274307ccb58a #v4.1.2
|
||||
with:
|
||||
path: |
|
||||
snapshot
|
||||
@@ -215,7 +215,7 @@ jobs:
|
||||
- Acceptance-Mac
|
||||
- Cli-Linux
|
||||
steps:
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 #v4.1.7
|
||||
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 #v4.2.2
|
||||
|
||||
- name: Delete snapshot cache
|
||||
run: gh cache delete "snapshot-build-${{ github.run_id }}"
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<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>
|
||||
<a href="" target="_blank"><img alt="License: Apache-2.0" src="https://img.shields.io/badge/License-Apache%202.0-blue.svg"></a>
|
||||
<a href="https://anchore.com/discourse" target="_blank"><img alt="Join our Discourse" src="https://img.shields.io/badge/Discourse-Join-blue?logo=discourse"/></a>
|
||||
<a rel="me" href="https://fosstodon.org/@syft"><img alt="Follow on Mastodon" src="https://img.shields.io/badge/Mastodon-Follow-blue?logoColor=white&logo=mastodon"/></a>
|
||||
</p>
|
||||
|
||||

|
||||
@@ -178,3 +179,7 @@ The Syft Team hold regular community meetings online. All are welcome to join to
|
||||
- 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!
|
||||
|
||||
## Syft Logo
|
||||
|
||||
<p xmlns:cc="http://creativecommons.org/ns#" xmlns:dct="http://purl.org/dc/terms/"><a property="dct:title" rel="cc:attributionURL" href="https://anchore.com/wp-content/uploads/2024/11/syft-logo.svg">Syft Logo</a> by <a rel="cc:attributionURL dct:creator" property="cc:attributionName" href="https://anchore.com/">Anchore</a> is licensed under <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="license noopener noreferrer" style="display:inline-block;">CC BY 4.0<img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/cc.svg" alt=""><img style="height:22px!important;margin-left:3px;vertical-align:text-bottom;" src="https://mirrors.creativecommons.org/presskit/icons/by.svg" alt=""></a></p>
|
||||
|
||||
+13
-86
@@ -1,5 +1,16 @@
|
||||
# Release
|
||||
|
||||
A release of syft comprises:
|
||||
- a new semver git tag from the current tip of the main branch
|
||||
- a new [github release](https://github.com/anchore/syft/releases) with a changelog and archived binary assets
|
||||
- docker images published to `ghcr.io` and `dockerhub`, including multi architecture images + manifest
|
||||
- [`anchore/homebrew-syft`](https://github.com/anchore/homebrew-syft) tap updated to point to assets in the latest github release
|
||||
|
||||
Ideally releasing should be done often with small increments when possible. Unless a
|
||||
breaking change is blocking the release, or no fixes/features have been merged, a good
|
||||
target release cadence is between every 1 or 2 weeks.
|
||||
|
||||
|
||||
## Creating a release
|
||||
|
||||
This release process itself should be as automated as possible, and has only a few steps:
|
||||
@@ -9,16 +20,10 @@ This release process itself should be as automated as possible, and has only a f
|
||||
you can abort and adjust the labels on the PRs and issues to be included in the release and
|
||||
re-run the release trigger command.
|
||||
|
||||
1. A release admin must approve the release on the GitHub Actions release pipeline run page.
|
||||
1. A release admin must approve the release on the GitHub Actions [release pipeline](https://github.com/anchore/syft/actions/workflows/release.yaml) run page.
|
||||
Once approved, the release pipeline will generate all assets and publish a GitHub Release.
|
||||
|
||||
1. If there is a release Milestone, close it.
|
||||
|
||||
Ideally releasing should be done often with small increments when possible. Unless a
|
||||
breaking change is blocking the release, or no fixes/features have been merged, a good
|
||||
target release cadence is between every 1 or 2 weeks.
|
||||
|
||||
|
||||
|
||||
## Retracting a release
|
||||
|
||||
If a release is found to be problematic, it can be retracted with the following steps:
|
||||
@@ -32,81 +37,3 @@ If a release is found to be problematic, it can be retracted with the following
|
||||
in the go proxy, which will cause confusion when trying to reuse the tag later (the H1 hash will not match and there
|
||||
will be a warning when users try to pull the new release).
|
||||
|
||||
|
||||
## Background
|
||||
|
||||
A good release process has the following qualities:
|
||||
|
||||
1. There is a way to plan what should be in a release
|
||||
1. There is a way to see what is actually in a release
|
||||
1. Allow for different kinds of releases (major breaking vs backwards compatible enhancements vs patch updates)
|
||||
1. Specify a repeatable way to build and publish software artifacts
|
||||
|
||||
|
||||
### Planning a release
|
||||
|
||||
To indicate a set of features to be released together add each issue to an in-repository
|
||||
Milestone named with major-minor version to be released (e.g. `v0.1`). It is OK for other
|
||||
features to be in the release that were not originally planned, and these issues and PRs
|
||||
do not need to be added to the Milestone in question. Only the set of features that, when
|
||||
completed, would allow the release to be considered complete. A Milestone is only used to:
|
||||
|
||||
- Plan what is desired to be in a release
|
||||
- Track progress to indicate when we may be ready to cut a new release
|
||||
|
||||
Not all releases need to be planned. For instance, patch releases for fixes should be
|
||||
released when they are ready and when releasing would not interfere with another current
|
||||
release (where some partial or breaking features have already been merged).
|
||||
|
||||
Unless necessary, feature releases should be small and frequent, which may obviate the
|
||||
need for regular release planning under a Milestone.
|
||||
|
||||
|
||||
### What is in a release
|
||||
|
||||
Milestones are specifically for planning a release, not necessarily tracking all changes
|
||||
that a release may bring (and more importantly, not all releases are necessarily planned
|
||||
either).
|
||||
|
||||
This is one of the (many) reasons for a Changelog. A good Changelog lists changes grouped
|
||||
by the type of change (new, enhancement, deprecation, breaking, bug fix, security fix), in
|
||||
chronological order (within groups), linking the PR where the change was made in the
|
||||
Changelog line. Furthermore, there should be a place to see all released versions, the
|
||||
release date for each release, the semantic version of the release, and the set of changes
|
||||
for each release.
|
||||
|
||||
**This project auto-generates the Changelog contents for each current release and posts the
|
||||
generated contents to the GitHub Release page**. Leveraging the GitHub Releases feature
|
||||
allows GitHub to manage the Changelog on each release outside of the git source tree while
|
||||
still being hosted with the released assets.
|
||||
|
||||
The Changelog is generated from the metadata from in-repository issues and PRs, using
|
||||
labels to guide what kind of change each item is (e.g. breaking, new feature, bug fix,
|
||||
etx). Only issues/PRs with select labels are included in the Changelog, and only if the
|
||||
issue/PR was created after the last release. Additional labels are used to exclude items
|
||||
from the Changelog.
|
||||
|
||||
The above suggestions imply that we should:
|
||||
|
||||
- Ensure there is a sufficient title for each PR and issue title to be included in the
|
||||
Changelog
|
||||
- The appropriate label is applied to PRs and/or issues to drive specific change type
|
||||
sections (deprecated, breaking, security, bug, etc)
|
||||
|
||||
**With this approach as we cultivate good organization of PRs and issues we automatically
|
||||
get an equally good Changelog.**
|
||||
|
||||
|
||||
### Major, minor, and patch releases
|
||||
|
||||
The latest version of the tool is the only supported version, which implies that multiple
|
||||
parallel release branches will not be a regular process (if ever). Multiple releases can
|
||||
be planned in parallel, however, only one can be actively developed at a time. That is, if
|
||||
PRs attached to a release Milestone have been merged into the main branch, that release is
|
||||
now the "next" release. **This implies that the source of truth for release lies with the
|
||||
git log and Changelog, not with the release Milestones** (which are purely for planning and
|
||||
tracking).
|
||||
|
||||
Semantic versioning should be used to indicate breaking changes, new features, and fixes.
|
||||
The exception to this is `< 1.0`, where the major version is not bumped for breaking changes,
|
||||
instead the minor version indicates both new features and breaking changes.
|
||||
|
||||
@@ -20,18 +20,18 @@ func AppClioSetupConfig(id clio.Identification, out io.Writer) *clio.SetupConfig
|
||||
WithConfigInRootHelp(). // --help on the root command renders the full application config in the help text
|
||||
WithUIConstructor(
|
||||
// select a UI based on the logging configuration and state of stdin (if stdin is a tty)
|
||||
func(cfg clio.Config) ([]clio.UI, error) {
|
||||
func(cfg clio.Config) (*clio.UICollection, error) {
|
||||
noUI := ui.None(out, cfg.Log.Quiet)
|
||||
if !cfg.Log.AllowUI(os.Stdin) || cfg.Log.Quiet {
|
||||
return []clio.UI{noUI}, nil
|
||||
return clio.NewUICollection(noUI), nil
|
||||
}
|
||||
|
||||
return []clio.UI{
|
||||
return clio.NewUICollection(
|
||||
ui.New(out, cfg.Log.Quiet,
|
||||
ui2.New(ui2.DefaultHandlerConfig()),
|
||||
),
|
||||
noUI,
|
||||
}, nil
|
||||
), nil
|
||||
},
|
||||
).
|
||||
WithInitializers(
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
const (
|
||||
convertExample = ` {{.appName}} {{.command}} img.syft.json -o spdx-json convert a syft SBOM to spdx-json, output goes to stdout
|
||||
{{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output is written to the file "img.cdx.json""
|
||||
{{.appName}} {{.command}} img.syft.json -o cyclonedx-json=img.cdx.json convert a syft SBOM to CycloneDX, output is written to the file "img.cdx.json"
|
||||
{{.appName}} {{.command}} - -o spdx-json convert an SBOM from STDIN to spdx-json
|
||||
`
|
||||
)
|
||||
@@ -39,7 +39,7 @@ func Convert(app clio.Application) *cobra.Command {
|
||||
return app.SetupCommand(&cobra.Command{
|
||||
Use: "convert [SOURCE-SBOM] -o [FORMAT]",
|
||||
Short: "Convert between SBOM formats",
|
||||
Long: "[Experimental] Convert SBOM files to, and from, SPDX, CycloneDX and Syft's format. For more info about data loss between formats see https://github.com/anchore/syft#format-conversion-experimental",
|
||||
Long: "[Experimental] Convert SBOM files to, and from, SPDX, CycloneDX and Syft's format. For more info about data loss between formats see https://github.com/anchore/syft/wiki/format-conversion",
|
||||
Example: internal.Tprintf(convertExample, map[string]interface{}{
|
||||
"appName": id.Name,
|
||||
"command": "convert",
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/fangs"
|
||||
"github.com/anchore/go-collections"
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
@@ -109,7 +110,7 @@ func (o *scanOptions) PostLoad() error {
|
||||
}
|
||||
|
||||
func (o *scanOptions) validateLegacyOptionsNotUsed() error {
|
||||
if o.Config.ConfigFile == "" {
|
||||
if len(fangs.Flatten(o.Config.ConfigFile)) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -121,32 +122,33 @@ func (o *scanOptions) validateLegacyOptionsNotUsed() error {
|
||||
File any `yaml:"file" json:"file" mapstructure:"file"`
|
||||
}
|
||||
|
||||
by, err := os.ReadFile(o.Config.ConfigFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read config file during validations %q: %w", o.Config.ConfigFile, err)
|
||||
}
|
||||
for _, f := range fangs.Flatten(o.Config.ConfigFile) {
|
||||
by, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read config file during validations %q: %w", f, err)
|
||||
}
|
||||
|
||||
var legacy legacyConfig
|
||||
if err := yaml.Unmarshal(by, &legacy); err != nil {
|
||||
return fmt.Errorf("unable to parse config file during validations %q: %w", o.Config.ConfigFile, err)
|
||||
}
|
||||
var legacy legacyConfig
|
||||
if err := yaml.Unmarshal(by, &legacy); err != nil {
|
||||
return fmt.Errorf("unable to parse config file during validations %q: %w", f, err)
|
||||
}
|
||||
|
||||
if legacy.DefaultImagePullSource != nil {
|
||||
return fmt.Errorf("the config file option 'default-image-pull-source' has been removed, please use 'source.image.default-pull-source' instead")
|
||||
}
|
||||
if legacy.DefaultImagePullSource != nil {
|
||||
return fmt.Errorf("the config file option 'default-image-pull-source' has been removed, please use 'source.image.default-pull-source' instead")
|
||||
}
|
||||
|
||||
if legacy.ExcludeBinaryOverlapByOwnership != nil {
|
||||
return fmt.Errorf("the config file option 'exclude-binary-overlap-by-ownership' has been removed, please use 'package.exclude-binary-overlap-by-ownership' instead")
|
||||
}
|
||||
if legacy.ExcludeBinaryOverlapByOwnership != nil {
|
||||
return fmt.Errorf("the config file option 'exclude-binary-overlap-by-ownership' has been removed, please use 'package.exclude-binary-overlap-by-ownership' instead")
|
||||
}
|
||||
|
||||
if legacy.BasePath != nil {
|
||||
return fmt.Errorf("the config file option 'base-path' has been removed, please use 'source.base-path' instead")
|
||||
}
|
||||
if legacy.BasePath != nil {
|
||||
return fmt.Errorf("the config file option 'base-path' has been removed, please use 'source.base-path' instead")
|
||||
}
|
||||
|
||||
if legacy.File != nil && reflect.TypeOf(legacy.File).Kind() == reflect.String {
|
||||
return fmt.Errorf("the config file option 'file' has been removed, please use 'outputs' instead")
|
||||
if legacy.File != nil && reflect.TypeOf(legacy.File).Kind() == reflect.String {
|
||||
return fmt.Errorf("the config file option 'file' has been removed, please use 'outputs' instead")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -154,13 +156,13 @@ func validateScanArgs(cmd *cobra.Command, args []string) error {
|
||||
return validateArgs(cmd, args, "an image/directory argument is required")
|
||||
}
|
||||
|
||||
func validateArgs(cmd *cobra.Command, args []string, error string) error {
|
||||
func validateArgs(cmd *cobra.Command, args []string, err string) error {
|
||||
if len(args) == 0 {
|
||||
// in the case that no arguments are given we want to show the help text and return with a non-0 return code.
|
||||
if err := cmd.Help(); err != nil {
|
||||
return fmt.Errorf("unable to display help: %w", err)
|
||||
}
|
||||
return fmt.Errorf("%v", error)
|
||||
return fmt.Errorf("%v", err)
|
||||
}
|
||||
|
||||
return cobra.MaximumNArgs(1)(cmd, args)
|
||||
|
||||
@@ -53,6 +53,9 @@ type Catalog struct {
|
||||
Platform string `yaml:"platform" json:"platform" mapstructure:"platform"`
|
||||
Source sourceConfig `yaml:"source" json:"source" mapstructure:"source"`
|
||||
Exclusions []string `yaml:"exclude" json:"exclude" mapstructure:"exclude"`
|
||||
|
||||
// configuration for inclusion of unknown information within elements
|
||||
Unknowns unknownsConfig `yaml:"unknowns" mapstructure:"unknowns"`
|
||||
}
|
||||
|
||||
var _ interface {
|
||||
@@ -71,6 +74,7 @@ func DefaultCatalog() Catalog {
|
||||
Java: defaultJavaConfig(),
|
||||
File: defaultFileConfig(),
|
||||
Relationships: defaultRelationshipsConfig(),
|
||||
Unknowns: defaultUnknowns(),
|
||||
Source: defaultSourceConfig(),
|
||||
Parallelism: 1,
|
||||
}
|
||||
@@ -82,6 +86,7 @@ func (cfg Catalog) ToSBOMConfig(id clio.Identification) *syft.CreateSBOMConfig {
|
||||
WithParallelism(cfg.Parallelism).
|
||||
WithRelationshipsConfig(cfg.ToRelationshipsConfig()).
|
||||
WithComplianceConfig(cfg.ToComplianceConfig()).
|
||||
WithUnknownsConfig(cfg.ToUnknownsConfig()).
|
||||
WithSearchConfig(cfg.ToSearchConfig()).
|
||||
WithPackagesConfig(cfg.ToPackagesConfig()).
|
||||
WithFilesConfig(cfg.ToFilesConfig()).
|
||||
@@ -114,6 +119,13 @@ func (cfg Catalog) ToComplianceConfig() cataloging.ComplianceConfig {
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg Catalog) ToUnknownsConfig() cataloging.UnknownsConfig {
|
||||
return cataloging.UnknownsConfig{
|
||||
IncludeExecutablesWithoutPackages: cfg.Unknowns.ExecutablesWithoutPackages,
|
||||
IncludeUnexpandedArchives: cfg.Unknowns.UnexpandedArchives,
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg Catalog) ToFilesConfig() filecataloging.Config {
|
||||
hashers, err := intFile.Hashers(cfg.File.Metadata.Digests...)
|
||||
if err != nil {
|
||||
@@ -154,6 +166,7 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
||||
WithFromLDFlags(cfg.Golang.MainModuleVersion.FromLDFlags),
|
||||
),
|
||||
JavaScript: javascript.DefaultCatalogerConfig().
|
||||
WithIncludeDevDependencies(*multiLevelOption(false, cfg.JavaScript.IncludeDevDependencies)).
|
||||
WithSearchRemoteLicenses(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.JavaScript, task.Node, task.NPM), cfg.JavaScript.SearchRemoteLicenses)).
|
||||
WithNpmBaseURL(cfg.JavaScript.NpmBaseURL),
|
||||
LinuxKernel: kernel.LinuxKernelCatalogerConfig{
|
||||
@@ -167,7 +180,8 @@ func (cfg Catalog) ToPackagesConfig() pkgcataloging.Config {
|
||||
WithMavenLocalRepositoryDir(cfg.Java.MavenLocalRepositoryDir).
|
||||
WithUseNetwork(*multiLevelOption(false, enrichmentEnabled(cfg.Enrich, task.Java, task.Maven), cfg.Java.UseNetwork)).
|
||||
WithMavenBaseURL(cfg.Java.MavenURL).
|
||||
WithArchiveTraversal(archiveSearch, cfg.Java.MaxParentRecursiveDepth),
|
||||
WithArchiveTraversal(archiveSearch, cfg.Java.MaxParentRecursiveDepth).
|
||||
WithResolveTransitiveDependencies(cfg.Java.ResolveTransitiveDependencies),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ package options
|
||||
|
||||
import "github.com/anchore/fangs"
|
||||
|
||||
// Config holds a reference to the specific config file that was used to load application configuration
|
||||
// Config holds a reference to the specific config file(s) that were used to load application configuration
|
||||
type Config struct {
|
||||
ConfigFile string `yaml:"config" json:"config" mapstructure:"config"`
|
||||
}
|
||||
|
||||
func (cfg *Config) DescribeFields(descriptions fangs.FieldDescriptionSet) {
|
||||
descriptions.Add(&cfg.ConfigFile, "the configuration file that was used to load application configuration")
|
||||
descriptions.Add(&cfg.ConfigFile, "the configuration file(s) used to load application configuration")
|
||||
}
|
||||
|
||||
@@ -6,22 +6,24 @@ import (
|
||||
)
|
||||
|
||||
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"`
|
||||
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"`
|
||||
ResolveTransitiveDependencies bool `yaml:"resolve-transitive-dependencies" json:"resolve-transitive-dependencies" mapstructure:"resolve-transitive-dependencies"`
|
||||
}
|
||||
|
||||
func defaultJavaConfig() javaConfig {
|
||||
def := java.DefaultArchiveCatalogerConfig()
|
||||
|
||||
return javaConfig{
|
||||
UseNetwork: nil, // this defaults to false, which is the API default
|
||||
MaxParentRecursiveDepth: def.MaxParentRecursiveDepth,
|
||||
UseMavenLocalRepository: nil, // this defaults to false, which is the API default
|
||||
MavenLocalRepositoryDir: def.MavenLocalRepositoryDir,
|
||||
MavenURL: def.MavenBaseURL,
|
||||
UseNetwork: nil, // this defaults to false, which is the API default
|
||||
MaxParentRecursiveDepth: def.MaxParentRecursiveDepth,
|
||||
UseMavenLocalRepository: nil, // this defaults to false, which is the API default
|
||||
MavenLocalRepositoryDir: def.MavenLocalRepositoryDir,
|
||||
MavenURL: def.MavenBaseURL,
|
||||
ResolveTransitiveDependencies: def.ResolveTransitiveDependencies,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,4 +45,5 @@ TIP: If you want to download all required pom files to the local repository with
|
||||
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.ResolveTransitiveDependencies, `resolve transient dependencies such as those defined in a dependency's POM on Maven central`)
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@ package options
|
||||
import "github.com/anchore/clio"
|
||||
|
||||
type javaScriptConfig struct {
|
||||
SearchRemoteLicenses *bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
NpmBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"`
|
||||
SearchRemoteLicenses *bool `json:"search-remote-licenses" yaml:"search-remote-licenses" mapstructure:"search-remote-licenses"`
|
||||
NpmBaseURL string `json:"npm-base-url" yaml:"npm-base-url" mapstructure:"npm-base-url"`
|
||||
IncludeDevDependencies *bool `json:"include-dev-dependencies" yaml:"include-dev-dependencies" mapstructure:"include-dev-dependencies"`
|
||||
}
|
||||
|
||||
var _ interface {
|
||||
@@ -14,4 +15,5 @@ var _ interface {
|
||||
func (o *javaScriptConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&o.SearchRemoteLicenses, `enables Syft to use the network to fill in more detailed license information`)
|
||||
descriptions.Add(&o.NpmBaseURL, `base NPM url to use`)
|
||||
descriptions.Add(&o.IncludeDevDependencies, `include development-scoped dependencies`)
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ func (o *OutputFile) AddFlags(flags clio.FlagSet) {
|
||||
|
||||
if pfp, ok := flags.(fangs.PFlagSetProvider); ok {
|
||||
flagSet := pfp.PFlagSet()
|
||||
flagSet.Lookup("file").Deprecated = "use: output"
|
||||
flagSet.Lookup("file").Deprecated = "use: --output FORMAT=PATH"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
package options
|
||||
|
||||
import (
|
||||
"github.com/anchore/clio"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
)
|
||||
|
||||
type unknownsConfig struct {
|
||||
RemoveWhenPackagesDefined bool `json:"remove-when-packages-defined" yaml:"remove-when-packages-defined" mapstructure:"remove-when-packages-defined"`
|
||||
ExecutablesWithoutPackages bool `json:"executables-without-packages" yaml:"executables-without-packages" mapstructure:"executables-without-packages"`
|
||||
UnexpandedArchives bool `json:"unexpanded-archives" yaml:"unexpanded-archives" mapstructure:"unexpanded-archives"`
|
||||
}
|
||||
|
||||
var _ interface {
|
||||
clio.FieldDescriber
|
||||
} = (*unknownsConfig)(nil)
|
||||
|
||||
func (o *unknownsConfig) DescribeFields(descriptions clio.FieldDescriptionSet) {
|
||||
descriptions.Add(&o.RemoveWhenPackagesDefined, `remove unknown errors on files with discovered packages`)
|
||||
descriptions.Add(&o.ExecutablesWithoutPackages, `include executables without any identified packages`)
|
||||
descriptions.Add(&o.UnexpandedArchives, `include archives which were not expanded and searched`)
|
||||
}
|
||||
|
||||
func defaultUnknowns() unknownsConfig {
|
||||
def := cataloging.DefaultUnknownsConfig()
|
||||
return unknownsConfig{
|
||||
RemoveWhenPackagesDefined: def.RemoveWhenPackagesDefined,
|
||||
ExecutablesWithoutPackages: def.IncludeExecutablesWithoutPackages,
|
||||
UnexpandedArchives: def.IncludeUnexpandedArchives,
|
||||
}
|
||||
}
|
||||
@@ -265,7 +265,7 @@ var dirOnlyTestCases = []testCase{
|
||||
name: "find java packages including pom.xml", // directory scans can include packages that have yet to be installed
|
||||
pkgType: pkg.JavaPkg,
|
||||
pkgLanguage: pkg.Java,
|
||||
duplicates: 1, // joda-time is included in both pom.xml AND the .jar collection
|
||||
duplicates: 2, // joda-time and example-java-app-maven are included in both pom.xml AND the .jar collection
|
||||
pkgInfo: map[string]string{
|
||||
"example-java-app-maven": "0.1.0",
|
||||
"joda-time": "2.9.2",
|
||||
|
||||
@@ -25,7 +25,7 @@ func TestNpmPackageLockDirectory(t *testing.T) {
|
||||
}
|
||||
|
||||
// ensure that integration test commonTestCases stay in sync with the available catalogers
|
||||
const expectedPackageCount = 6
|
||||
const expectedPackageCount = 2
|
||||
if foundPackages.Size() != expectedPackageCount {
|
||||
t.Errorf("found the wrong set of npm package-lock.json packages (expected: %d, actual: %d)", expectedPackageCount, foundPackages.Size())
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ const defaultStdoutLogBufferSize = 1024
|
||||
// restore := CaptureStdoutToTraceLog()
|
||||
// // here, stdout will be captured and redirected to the provided writer
|
||||
// restore() // block until the output has all been sent to the writer and restore the original stdout
|
||||
func CaptureStdoutToTraceLog() (close func()) {
|
||||
func CaptureStdoutToTraceLog() func() {
|
||||
return capture(&os.Stdout, newLogWriter(), defaultStdoutLogBufferSize)
|
||||
}
|
||||
|
||||
func capture(target **os.File, writer io.Writer, bufSize int) (close func()) {
|
||||
func capture(target **os.File, writer io.Writer, bufSize int) func() {
|
||||
original := *target
|
||||
|
||||
r, w, _ := os.Pipe()
|
||||
|
||||
@@ -78,6 +78,13 @@ func (m *UI) Handle(e partybus.Event) error {
|
||||
}
|
||||
|
||||
func (m *UI) Teardown(force bool) error {
|
||||
defer func() {
|
||||
// allow for traditional logging to resume now that the UI is shutting down
|
||||
if logWrapper, ok := log.Get().(logger.Controller); ok {
|
||||
logWrapper.SetOutput(m.err)
|
||||
}
|
||||
}()
|
||||
|
||||
if !force {
|
||||
m.handler.Wait()
|
||||
m.program.Quit()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/anchore/syft
|
||||
|
||||
go 1.22.0
|
||||
go 1.22.9
|
||||
|
||||
require (
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.1
|
||||
@@ -9,33 +9,33 @@ require (
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
|
||||
github.com/acobaugh/osrelease v0.1.0
|
||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9
|
||||
github.com/anchore/clio v0.0.0-20240522144804-d81e109008aa
|
||||
github.com/anchore/fangs v0.0.0-20240903175602-e716ef12c23d
|
||||
github.com/anchore/clio v0.0.0-20241015191535-f538a9016e10
|
||||
github.com/anchore/fangs v0.0.0-20241014201141-b6e4b3469f10
|
||||
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537
|
||||
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a
|
||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20240507183024-848e011fc24f
|
||||
github.com/anchore/stereoscope v0.0.3
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f
|
||||
github.com/anchore/stereoscope v0.0.9-0.20241113211114-aa3a3ef4efe8
|
||||
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/bmatcuk/doublestar/v4 v4.7.1
|
||||
github.com/charmbracelet/bubbles v0.20.0
|
||||
github.com/charmbracelet/bubbletea v1.1.1
|
||||
github.com/charmbracelet/lipgloss v0.13.0
|
||||
github.com/charmbracelet/bubbletea v1.2.2
|
||||
github.com/charmbracelet/lipgloss v1.0.0
|
||||
github.com/dave/jennifer v1.7.1
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/docker v27.3.0+incompatible
|
||||
github.com/docker/docker v27.3.1+incompatible
|
||||
github.com/dustin/go-humanize v1.0.1
|
||||
github.com/elliotchance/phpserialize v1.4.0
|
||||
github.com/facebookincubator/nvdtools v0.1.5
|
||||
github.com/github/go-spdx/v2 v2.3.2
|
||||
github.com/gkampitakis/go-snaps v0.5.7
|
||||
github.com/go-git/go-billy/v5 v5.5.0
|
||||
github.com/go-git/go-billy/v5 v5.6.0
|
||||
github.com/go-git/go-git/v5 v5.12.0
|
||||
github.com/go-test/deep v1.1.1
|
||||
github.com/google/go-cmp v0.6.0
|
||||
@@ -46,7 +46,7 @@ require (
|
||||
github.com/hashicorp/go-multierror v1.1.1
|
||||
github.com/iancoleman/strcase v0.3.0
|
||||
github.com/invopop/jsonschema v0.7.0
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.9
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.1
|
||||
github.com/jinzhu/copier v0.4.0
|
||||
github.com/kastenhq/goversion v0.0.0-20230811215019-93b2f8823953
|
||||
github.com/knqyf263/go-rpmdb v0.1.1
|
||||
@@ -60,7 +60,7 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0
|
||||
github.com/pelletier/go-toml v1.9.5
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22
|
||||
github.com/saferwall/pe v1.5.4
|
||||
github.com/saferwall/pe v1.5.5
|
||||
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
|
||||
@@ -78,8 +78,8 @@ require (
|
||||
github.com/xeipuuv/gojsonschema v1.2.0
|
||||
github.com/zyedidia/generic v1.2.2-0.20230320175451-4410d2372cb1
|
||||
go.uber.org/goleak v1.3.0
|
||||
golang.org/x/mod v0.21.0
|
||||
golang.org/x/net v0.29.0
|
||||
golang.org/x/mod v0.22.0
|
||||
golang.org/x/net v0.31.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
modernc.org/sqlite v1.33.1
|
||||
)
|
||||
@@ -89,9 +89,9 @@ require google.golang.org/genproto v0.0.0-20240213162025-012b6fc9bca9 // indirec
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.4.0
|
||||
github.com/OneOfOne/xxhash v1.2.8
|
||||
github.com/adrg/xdg v0.5.0
|
||||
github.com/adrg/xdg v0.5.3
|
||||
github.com/magiconair/properties v1.8.7
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -101,9 +101,9 @@ require (
|
||||
github.com/DataDog/zstd v1.5.5 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
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/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Microsoft/hcsshim v0.11.7 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.2 // indirect
|
||||
github.com/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
|
||||
@@ -111,20 +111,23 @@ 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.2.3 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.4.5 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/cloudflare/circl v1.3.8 // indirect
|
||||
github.com/containerd/cgroups v1.1.0 // indirect
|
||||
github.com/containerd/containerd v1.7.11 // indirect
|
||||
github.com/containerd/containerd v1.7.23 // indirect
|
||||
github.com/containerd/containerd/api v1.7.19 // indirect
|
||||
github.com/containerd/continuity v0.4.2 // indirect
|
||||
github.com/containerd/errdefs v0.3.0 // indirect
|
||||
github.com/containerd/fifo v1.1.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
|
||||
github.com/containerd/ttrpc v1.2.2 // indirect
|
||||
github.com/containerd/ttrpc v1.2.5 // indirect
|
||||
github.com/containerd/typeurl/v2 v2.1.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/cli v27.1.1+incompatible // indirect
|
||||
github.com/docker/cli v27.3.1+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
|
||||
@@ -137,7 +140,7 @@ require (
|
||||
github.com/felixge/fgprof v0.9.3 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // 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.6 // 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
|
||||
@@ -146,7 +149,7 @@ require (
|
||||
github.com/go-restruct/restruct v1.2.0-alpha // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
@@ -175,14 +178,15 @@ require (
|
||||
github.com/moby/locker v1.0.1 // indirect
|
||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||
github.com/moby/sys/signal v0.7.0 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.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/runc v1.1.14 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.1 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/selinux v1.11.0 // indirect
|
||||
github.com/pborman/indent v1.2.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
@@ -206,7 +210,7 @@ require (
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/sylabs/sif/v2 v2.17.1 // indirect
|
||||
github.com/sylabs/sif/v2 v2.20.0 // indirect
|
||||
github.com/sylabs/squashfs v1.0.0 // indirect
|
||||
github.com/therootcompany/xz v1.0.1 // indirect
|
||||
github.com/tidwall/gjson v1.17.0 // indirect
|
||||
@@ -227,16 +231,15 @@ require (
|
||||
go.opentelemetry.io/otel/trace v1.24.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.uber.org/multierr v1.9.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/term v0.24.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/term v0.26.0 // indirect
|
||||
golang.org/x/text v0.20.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240314234333-6e1732d8331c // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 // indirect
|
||||
|
||||
@@ -74,21 +74,21 @@ github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lpr
|
||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
||||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
||||
github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
|
||||
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/Microsoft/hcsshim v0.11.7 h1:vl/nj3Bar/CvJSYo7gIQPyRWc9f3c6IeSNavBTSZNZQ=
|
||||
github.com/Microsoft/hcsshim v0.11.7/go.mod h1:MV8xMfmECjl5HdO7U/3/hFVnkmSBjAjmA09d4bExKcU=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
|
||||
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
|
||||
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
|
||||
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
|
||||
github.com/ProtonMail/go-crypto v1.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0=
|
||||
github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8=
|
||||
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo=
|
||||
github.com/acobaugh/osrelease v0.1.0 h1:Yb59HQDGGNhCj4suHaFQQfBps5wyoKLSSX/J/+UifRE=
|
||||
github.com/acobaugh/osrelease v0.1.0/go.mod h1:4bFEs0MtgHNHBrmHCt67gNisnabCRAlzdVasCEGHTWY=
|
||||
github.com/adrg/xdg v0.5.0 h1:dDaZvhMXatArP1NPHhnfaQUqWBLBsmx1h1HXQdMoFCY=
|
||||
github.com/adrg/xdg v0.5.0/go.mod h1:dDdY4M4DF9Rjy4kHPeNL+ilVF+p2lK8IdM9/rTSGcI4=
|
||||
github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78=
|
||||
github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ=
|
||||
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=
|
||||
@@ -97,10 +97,10 @@ github.com/anchore/archiver/v3 v3.5.2 h1:Bjemm2NzuRhmHy3m0lRe5tNoClB9A4zYyDV58Pa
|
||||
github.com/anchore/archiver/v3 v3.5.2/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9 h1:p0ZIe0htYOX284Y4axJaGBvXHU0VCCzLN5Wf5XbKStU=
|
||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9/go.mod h1:3ZsFB9tzW3vl4gEiUeuSOMDnwroWxIxJelOOHUp8dSw=
|
||||
github.com/anchore/clio v0.0.0-20240522144804-d81e109008aa h1:pwlAn4O9SBUnlgfa69YcqIynbUyobLVFYu8HxSoCffA=
|
||||
github.com/anchore/clio v0.0.0-20240522144804-d81e109008aa/go.mod h1:nD3H5uIvjxlfmakOBgtyFQbk5Zjp3l538kxfpHPslzI=
|
||||
github.com/anchore/fangs v0.0.0-20240903175602-e716ef12c23d h1:ZD4wdCBgJJzJybjTUIEiiupLF7B9H3WLuBTjspBO2Mc=
|
||||
github.com/anchore/fangs v0.0.0-20240903175602-e716ef12c23d/go.mod h1:Xh4ObY3fmoMzOEVXwDtS1uK44JC7+nRD0n29/1KYFYg=
|
||||
github.com/anchore/clio v0.0.0-20241015191535-f538a9016e10 h1:3xmanFdoQEH0REvPA+gLm3Km0/981F4z2a/7ADTlv8k=
|
||||
github.com/anchore/clio v0.0.0-20241015191535-f538a9016e10/go.mod h1:h6Ly2hlKjQoPtI3rA8oB5afSmB/XimhcY55xbuW4Dwo=
|
||||
github.com/anchore/fangs v0.0.0-20241014201141-b6e4b3469f10 h1:w+HibE+e/heP6ysADh7sWxg5LhYdVqrpB1A4Hmgjyx8=
|
||||
github.com/anchore/fangs v0.0.0-20241014201141-b6e4b3469f10/go.mod h1:s0L1//Sxn6Rq0Dcxx+dmT/RRmD9HhsaJjJkPUJHLJLM=
|
||||
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537 h1:GjNGuwK5jWjJMyVppBjYS54eOiiSNv4Ba869k4wh72Q=
|
||||
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537/go.mod h1:1aiktV46ATCkuVg0O573ZrH56BUawTECPETbZyBcqT8=
|
||||
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a h1:nJ2G8zWKASyVClGVgG7sfM5mwoZlZ2zYpIzN2OhjWkw=
|
||||
@@ -113,10 +113,10 @@ github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04 h1:VzprUTpc0v
|
||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04/go.mod h1:6dK64g27Qi1qGQZ67gFmBFvEHScy0/C8qhQhNe5B5pQ=
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZVsCYMrIZBpFxwV26CbsuoEh5muXD5I1Ods=
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.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 h1:JRPHySy8S6P+Ff3IDiQ29ap1i8/laUQxDk9K1eFh/2U=
|
||||
github.com/anchore/stereoscope v0.0.3/go.mod h1:5DJheGPjVRsSqegTB24Zi6SCHnYQnA519yeIG+RG+I4=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f h1:dAQPIrQ3a5PBqZeZ+B9NGZsGmodk4NO9OjDIsQmQyQM=
|
||||
github.com/anchore/packageurl-go v0.1.1-0.20241018175412-5c22e6360c4f/go.mod h1:KoYIv7tdP5+CC9VGkeZV4/vGCKsY55VvoG+5dadg4YI=
|
||||
github.com/anchore/stereoscope v0.0.9-0.20241113211114-aa3a3ef4efe8 h1:gYed/fx5OeW+isRay2q60JGt8+ED/odEPiJ/SWlbw8s=
|
||||
github.com/anchore/stereoscope v0.0.9-0.20241113211114-aa3a3ef4efe8/go.mod h1:c2oGDU0R+llJObsatBSenjYPV1raKhMq9GEqe8J56EI=
|
||||
github.com/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=
|
||||
@@ -145,11 +145,10 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1 h1:FH9SifrbvJhnlQpztAx++wlkk70QBf0iBWDwNy7PA4I=
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
|
||||
github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
|
||||
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@@ -159,23 +158,22 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE=
|
||||
github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU=
|
||||
github.com/charmbracelet/bubbletea v1.1.1 h1:KJ2/DnmpfqFtDNVTvYZ6zpPFL9iRCRr0qqKOCvppbPY=
|
||||
github.com/charmbracelet/bubbletea v1.1.1/go.mod h1:9Ogk0HrdbHolIKHdjfFpyXJmiCzGwy+FesYkZr7hYU4=
|
||||
github.com/charmbracelet/bubbletea v1.2.2 h1:EMz//Ky/aFS2uLcKqpCst5UOE6z5CFDGRsUpyXz0chs=
|
||||
github.com/charmbracelet/bubbletea v1.2.2/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM=
|
||||
github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
|
||||
github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
|
||||
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
|
||||
github.com/charmbracelet/x/ansi v0.2.3 h1:VfFN0NUpcjBRd4DnKfRaIRo53KRgey/nhOoEqosGDEY=
|
||||
github.com/charmbracelet/x/ansi v0.2.3/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/charmbracelet/x/term v0.2.0 h1:cNB9Ot9q8I711MyZ7myUR5HFWL/lc3OpU8jZ4hwm0x0=
|
||||
github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4cktEQCN6GWyF0=
|
||||
github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg=
|
||||
github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo=
|
||||
github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM=
|
||||
github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
|
||||
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
|
||||
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
@@ -190,18 +188,24 @@ github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWH
|
||||
github.com/cncf/xds/go v0.0.0-20211130200136-a8f946100490/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
|
||||
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
|
||||
github.com/containerd/containerd v1.7.11 h1:lfGKw3eU35sjV0aG2eYZTiwFEY1pCzxdzicHP3SZILw=
|
||||
github.com/containerd/containerd v1.7.11/go.mod h1:5UluHxHTX2rdvYuZ5OJTC5m/KJNs0Zs9wVoJm9zf5ZE=
|
||||
github.com/containerd/containerd v1.7.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ=
|
||||
github.com/containerd/containerd v1.7.23/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw=
|
||||
github.com/containerd/containerd/api v1.7.19 h1:VWbJL+8Ap4Ju2mx9c9qS1uFSB1OVYr5JJrW2yT5vFoA=
|
||||
github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig=
|
||||
github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM=
|
||||
github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ=
|
||||
github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4=
|
||||
github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
|
||||
github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY=
|
||||
github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o=
|
||||
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
|
||||
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
|
||||
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
|
||||
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k=
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o=
|
||||
github.com/containerd/ttrpc v1.2.2 h1:9vqZr0pxwOF5koz6N0N3kJ0zDHokrcPxIR/ZR2YFtOs=
|
||||
github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak=
|
||||
github.com/containerd/ttrpc v1.2.5 h1:IFckT1EFQoFBMG4c3sMdT8EP3/aKfumK1msY+Ze4oLU=
|
||||
github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o=
|
||||
github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4=
|
||||
github.com/containerd/typeurl/v2 v2.1.1/go.mod h1:IDp2JFvbwZ31H8dQbEIY7sDl2L3o3HZj1hsSQlywkQ0=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@@ -211,8 +215,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
||||
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/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=
|
||||
github.com/cyphar/filepath-securejoin v0.2.5 h1:6iR5tXJ/e6tJZzzdMc1km3Sa7RRIVBKAK32O2s7AYfo=
|
||||
github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
|
||||
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@@ -225,12 +229,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.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
|
||||
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ=
|
||||
github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
|
||||
github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v27.3.0+incompatible h1:BNb1QY6o4JdKpqwi9IB+HUYcRRrVN4aGFUTvDmWYK1A=
|
||||
github.com/docker/docker v27.3.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker v27.3.1+incompatible h1:KttF0XoteNTicmUtBO0L2tP+J7FGRFTjaEF4k6WdhfI=
|
||||
github.com/docker/docker v27.3.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
|
||||
github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
@@ -282,8 +286,8 @@ 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.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
|
||||
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/github/go-spdx/v2 v2.3.2 h1:IfdyNHTqzs4zAJjXdVQfRnxt1XMfycXoHBE2Vsm1bjs=
|
||||
github.com/github/go-spdx/v2 v2.3.2/go.mod h1:2ZxKsOhvBp+OYBDlsGnUMcchLeo2mrpEBn2L1C+U3IQ=
|
||||
@@ -299,8 +303,8 @@ github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
|
||||
github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
|
||||
github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
|
||||
github.com/go-git/go-billy/v5 v5.6.0 h1:w2hPNtoehvJIxR00Vb4xX94qHQi/ApZfX+nBE2Cjio8=
|
||||
github.com/go-git/go-billy/v5 v5.6.0/go.mod h1:sFDq7xD3fn3E0GOwUSZqHo9lrkmx8xJhA0ZrfvjBRGM=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
|
||||
@@ -359,8 +363,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
@@ -477,8 +481,8 @@ github.com/invopop/jsonschema v0.7.0 h1:2vgQcBz1n256N+FpX3Jq7Y17AjYt46Ig3zIWyy77
|
||||
github.com/invopop/jsonschema v0.7.0/go.mod h1:O9uiLokuu0+MGFlyiaqtWxwqJm41/+8Nj0lD7A36YH0=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.9 h1:ACteMBRrrmm1gMsXe9PSTOClQ63IXDUt03H5U+UV8OU=
|
||||
github.com/jedib0t/go-pretty/v6 v6.5.9/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.1 h1:iJ65Xjb680rHcikRj6DSIbzCex2huitmc7bDtxYVWyc=
|
||||
github.com/jedib0t/go-pretty/v6 v6.6.1/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
|
||||
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
|
||||
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
@@ -581,6 +585,10 @@ github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5
|
||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
|
||||
github.com/moby/sys/signal v0.7.0/go.mod h1:GQ6ObYZfqacOwTtlXvcmh9A26dVRul/hbOZn88Kg8Tg=
|
||||
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
|
||||
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
|
||||
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
|
||||
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA=
|
||||
github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@@ -603,16 +611,14 @@ github.com/nwaples/rardecode v1.1.0 h1:vSxaY8vQhOcVr4mm5e8XllHWTiM4JF507A0Katqw7
|
||||
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||
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/runc v1.1.14 h1:rgSuzbmgz5DUJjeSnw337TxDbRuqjs6iqQck/2weR6w=
|
||||
github.com/opencontainers/runc v1.1.14/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1LjPNeMTfRGvOA=
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w=
|
||||
github.com/opencontainers/runtime-spec v1.1.0-rc.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=
|
||||
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
|
||||
github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU=
|
||||
github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
@@ -655,7 +661,6 @@ github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8b
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
|
||||
@@ -673,8 +678,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.5 h1:GGbzKjXDm7i+1K6riOgtgblyTdRmTbr3r11IzjovAK8=
|
||||
github.com/saferwall/pe v1.5.5/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=
|
||||
@@ -691,8 +696,8 @@ github.com/sassoftware/go-rpmutils v0.4.0/go.mod h1:3goNWi7PGAT3/dlql2lv3+MSN5jN
|
||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e h1:7q6NSFZDeGfvvtIRwBrU/aegEYJYmvev0cHAwo17zZQ=
|
||||
github.com/scylladb/go-set v1.0.3-0.20200225121959-cc7b2070d91e/go.mod h1:DkpGd78rljTxKAnTDPFqXSGxvETQnJyuSOQwsHycqfs=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sebdah/goldie/v2 v2.5.3 h1:9ES/mNN+HNUbNWpVAlrzuZ7jE+Nrczbj8uFRjM7624Y=
|
||||
github.com/sebdah/goldie/v2 v2.5.3/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||
github.com/sebdah/goldie/v2 v2.5.5 h1:rx1mwF95RxZ3/83sdS4Yp7t2C5TCokvWP4TBRbAyEWY=
|
||||
github.com/sebdah/goldie/v2 v2.5.5/go.mod h1:oZ9fp0+se1eapSRjfYbsV/0Hqhbuu3bJVvKI/NNtssI=
|
||||
github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d h1:RQqyEogx5J6wPdoxqL132b100j8KjcVHO1c0KLRoIhc=
|
||||
github.com/secDre4mer/pkcs7 v0.0.0-20240322103146-665324a4461d/go.mod h1:PegD7EVqlN88z7TpCqH92hHP+GBpfomGCCnw1PFtNOA=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
@@ -705,7 +710,6 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
@@ -756,8 +760,8 @@ 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/sif/v2 v2.20.0 h1:RfDHEltUrchZbp/XGcWaw3nRSbufoNWqvwmf91/Q2gI=
|
||||
github.com/sylabs/sif/v2 v2.20.0/go.mod h1:z6dq3B7QXK0pD71n15kAapven+gE+PZAIPOewBTNDpU=
|
||||
github.com/sylabs/squashfs v1.0.0 h1:xAyMS21ogglkuR5HaY55PCfqY3H32ma9GkasTYo28Zg=
|
||||
github.com/sylabs/squashfs v1.0.0/go.mod h1:rhWzvgefq1X+R+LZdts10hfMsTg3g74OfGunW8tvg/4=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.5 h1:RqpJnf6HGE2CB/lZB1A8BYguk8uRtcvYAPLCF15qguo=
|
||||
@@ -834,8 +838,8 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMey
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
|
||||
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
|
||||
go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8=
|
||||
go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E=
|
||||
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
|
||||
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
@@ -862,10 +866,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
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=
|
||||
@@ -876,8 +878,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 h1:mchzmB1XO2pMaKFRqk/+MV3mgGG96aqaPXaMifQU47w=
|
||||
golang.org/x/exp v0.0.0-20231108232855-2478ac86f678/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -905,9 +907,8 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
|
||||
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -951,11 +952,8 @@ golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
golang.org/x/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=
|
||||
@@ -985,9 +983,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1062,20 +1059,13 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/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.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/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=
|
||||
@@ -1085,11 +1075,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/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=
|
||||
@@ -1150,9 +1138,8 @@ golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.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.23.0 h1:SGsXPZ+2l4JsgaCKkx+FQ9YZ5XEtA1GZYuoDjenLjvg=
|
||||
golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI=
|
||||
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=
|
||||
@@ -1310,8 +1297,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
|
||||
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -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.17"
|
||||
JSONSchemaVersion = "16.0.19"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -19,12 +18,13 @@ func NewZipFileManifest(archivePath string) (ZipFileManifest, error) {
|
||||
zipReader, err := OpenZip(archivePath)
|
||||
manifest := make(ZipFileManifest)
|
||||
if err != nil {
|
||||
return manifest, fmt.Errorf("unable to open zip archive (%s): %w", archivePath, err)
|
||||
log.Debugf("unable to open zip archive (%s): %v", archivePath, err)
|
||||
return manifest, err
|
||||
}
|
||||
defer func() {
|
||||
err = zipReader.Close()
|
||||
if err != nil {
|
||||
log.Warnf("unable to close zip archive (%s): %+v", archivePath, err)
|
||||
log.Debugf("unable to close zip archive (%s): %+v", archivePath, err)
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
// directoryEndLen, readByf, directoryEnd, and findSignatureInBlock were copied from the golang stdlib, specifically:
|
||||
@@ -46,7 +48,8 @@ func OpenZip(filepath string) (*ZipReadCloser, error) {
|
||||
// need to find the start of the archive and keep track of this offset.
|
||||
offset, err := findArchiveStartOffset(f, fi.Size())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot find beginning of zip archive=%q : %w", filepath, err)
|
||||
log.Debugf("cannot find beginning of zip archive=%q : %v", filepath, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := f.Seek(0, io.SeekStart); err != nil {
|
||||
@@ -62,7 +65,8 @@ func OpenZip(filepath string) (*ZipReadCloser, error) {
|
||||
|
||||
r, err := zip.NewReader(io.NewSectionReader(f, offset64, size), size)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open ZipReadCloser @ %q: %w", filepath, err)
|
||||
log.Debugf("unable to open ZipReadCloser @ %q: %v", filepath, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ZipReadCloser{
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type licenseScannerKey struct{}
|
||||
|
||||
func SetContextLicenseScanner(ctx context.Context, s Scanner) context.Context {
|
||||
return context.WithValue(ctx, licenseScannerKey{}, s)
|
||||
}
|
||||
|
||||
func ContextLicenseScanner(ctx context.Context) Scanner {
|
||||
if s, ok := ctx.Value(licenseScannerKey{}).(Scanner); ok {
|
||||
return s
|
||||
}
|
||||
return NewDefaultScanner()
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/google/licensecheck"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
const (
|
||||
coverageThreshold = 75
|
||||
unknownLicenseType = "UNKNOWN"
|
||||
)
|
||||
|
||||
// Parse scans the contents of a license file to attempt to determine the type of license it is
|
||||
func Parse(reader io.Reader, l file.Location) (licenses []pkg.License, err error) {
|
||||
licenses = make([]pkg.License, 0)
|
||||
contents, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cov := licensecheck.Scan(contents)
|
||||
if cov.Percent < coverageThreshold {
|
||||
// unknown or no licenses here?
|
||||
return licenses, nil
|
||||
}
|
||||
|
||||
for _, m := range cov.Match {
|
||||
lic := pkg.NewLicenseFromLocations(m.ID, l)
|
||||
lic.Type = license.Concluded
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
|
||||
return licenses, nil
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
|
||||
"github.com/google/licensecheck"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
)
|
||||
|
||||
const coverageThreshold = 75 // determined by experimentation
|
||||
|
||||
type Scanner interface {
|
||||
IdentifyLicenseIDs(context.Context, io.Reader) ([]string, error)
|
||||
}
|
||||
|
||||
var _ Scanner = (*scanner)(nil)
|
||||
|
||||
type scanner struct {
|
||||
coverageThreshold float64 // between 0 and 100
|
||||
scanner func([]byte) licensecheck.Coverage
|
||||
}
|
||||
|
||||
// NewDefaultScanner returns a scanner that uses a new instance of the default licensecheck package scanner.
|
||||
func NewDefaultScanner() Scanner {
|
||||
s, err := licensecheck.NewScanner(licensecheck.BuiltinLicenses())
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to create default license scanner")
|
||||
s = nil
|
||||
}
|
||||
return &scanner{
|
||||
coverageThreshold: coverageThreshold,
|
||||
scanner: s.Scan,
|
||||
}
|
||||
}
|
||||
|
||||
// TestingOnlyScanner returns a scanner that uses the built-in license scanner from the licensecheck package.
|
||||
// THIS IS ONLY MEANT FOR TEST CODE, NOT PRODUCTION CODE.
|
||||
func TestingOnlyScanner() Scanner {
|
||||
return &scanner{
|
||||
coverageThreshold: coverageThreshold,
|
||||
scanner: licensecheck.Scan,
|
||||
}
|
||||
}
|
||||
|
||||
func (s scanner) IdentifyLicenseIDs(_ context.Context, reader io.Reader) ([]string, error) {
|
||||
if s.scanner == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
content, err := io.ReadAll(reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cov := s.scanner(content)
|
||||
if cov.Percent < s.coverageThreshold {
|
||||
// unknown or no licenses here?
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var ids []string
|
||||
for _, m := range cov.Match {
|
||||
ids = append(ids, m.ID)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package licenses
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
// Search scans the contents of a license file to attempt to determine the type of license it is
|
||||
func Search(ctx context.Context, scanner Scanner, reader file.LocationReadCloser) (licenses []pkg.License, err error) {
|
||||
licenses = make([]pkg.License, 0)
|
||||
|
||||
ids, err := scanner.IdentifyLicenseIDs(ctx, reader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, id := range ids {
|
||||
lic := pkg.NewLicenseFromLocations(id, reader.Location)
|
||||
lic.Type = license.Concluded
|
||||
|
||||
licenses = append(licenses, lic)
|
||||
}
|
||||
|
||||
return licenses, nil
|
||||
}
|
||||
@@ -9,15 +9,18 @@ func EvidentBy(catalog *pkg.Collection) []artifact.Relationship {
|
||||
var edges []artifact.Relationship
|
||||
for _, p := range catalog.Sorted() {
|
||||
for _, l := range p.Locations.ToSlice() {
|
||||
if v, exists := l.Annotations[pkg.EvidenceAnnotationKey]; !exists || v != pkg.PrimaryEvidenceAnnotation {
|
||||
// skip non-primary evidence from being expressed as a relationship.
|
||||
// note: this may be configurable in the future.
|
||||
continue
|
||||
kind := pkg.SupportingEvidenceAnnotation
|
||||
if v, exists := l.Annotations[pkg.EvidenceAnnotationKey]; exists {
|
||||
kind = v
|
||||
}
|
||||
|
||||
edges = append(edges, artifact.Relationship{
|
||||
From: p,
|
||||
To: l.Coordinates,
|
||||
Type: artifact.EvidentByRelationship,
|
||||
Data: map[string]string{
|
||||
"kind": kind,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -11,8 +12,10 @@ import (
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/event/monitor"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
type Executor struct {
|
||||
@@ -35,6 +38,12 @@ func NewTaskExecutor(tasks []Task, numWorkers int) *Executor {
|
||||
}
|
||||
|
||||
func (p *Executor) Execute(ctx context.Context, resolver file.Resolver, s sbomsync.Builder, prog *monitor.CatalogerTaskProgress) error {
|
||||
var lock sync.Mutex
|
||||
withLock := func(fn func()) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
fn()
|
||||
}
|
||||
var errs error
|
||||
wg := &sync.WaitGroup{}
|
||||
for i := 0; i < p.numWorkers; i++ {
|
||||
@@ -48,9 +57,16 @@ func (p *Executor) Execute(ctx context.Context, resolver file.Resolver, s sbomsy
|
||||
return
|
||||
}
|
||||
|
||||
if err := runTaskSafely(ctx, tsk, resolver, s); err != nil {
|
||||
errs = multierror.Append(errs, fmt.Errorf("failed to run task: %w", err))
|
||||
prog.SetError(err)
|
||||
err := runTaskSafely(ctx, tsk, resolver, s)
|
||||
unknowns, remainingErrors := unknown.ExtractCoordinateErrors(err)
|
||||
if len(unknowns) > 0 {
|
||||
appendUnknowns(s, tsk.Name(), unknowns)
|
||||
}
|
||||
if remainingErrors != nil {
|
||||
withLock(func() {
|
||||
errs = multierror.Append(errs, fmt.Errorf("failed to run task: %w", remainingErrors))
|
||||
prog.SetError(remainingErrors)
|
||||
})
|
||||
}
|
||||
prog.Increment()
|
||||
}
|
||||
@@ -62,6 +78,25 @@ func (p *Executor) Execute(ctx context.Context, resolver file.Resolver, s sbomsy
|
||||
return errs
|
||||
}
|
||||
|
||||
func appendUnknowns(builder sbomsync.Builder, taskName string, unknowns []unknown.CoordinateError) {
|
||||
if accessor, ok := builder.(sbomsync.Accessor); ok {
|
||||
accessor.WriteToSBOM(func(sb *sbom.SBOM) {
|
||||
for _, u := range unknowns {
|
||||
if sb.Artifacts.Unknowns == nil {
|
||||
sb.Artifacts.Unknowns = map[file.Coordinates][]string{}
|
||||
}
|
||||
unknownText := formatUnknown(u.Reason.Error(), taskName)
|
||||
existing := sb.Artifacts.Unknowns[u.Coordinates]
|
||||
// don't include duplicate unknowns
|
||||
if slices.Contains(existing, unknownText) {
|
||||
continue
|
||||
}
|
||||
sb.Artifacts.Unknowns[u.Coordinates] = append(existing, unknownText)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func runTaskSafely(ctx context.Context, t Task, resolver file.Resolver, s sbomsync.Builder) (err error) {
|
||||
// handle individual cataloger panics
|
||||
defer func() {
|
||||
|
||||
+16
-21
@@ -3,7 +3,6 @@ package task
|
||||
import (
|
||||
"context"
|
||||
"crypto"
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@@ -32,15 +31,12 @@ func NewFileDigestCatalogerTask(selection file.Selection, hashers ...crypto.Hash
|
||||
}
|
||||
|
||||
result, err := digestsCataloger.Catalog(ctx, resolver, coordinates...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to catalog file digests: %w", err)
|
||||
}
|
||||
|
||||
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
|
||||
sbom.Artifacts.FileDigests = result
|
||||
})
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
return NewTask("file-digest-cataloger", fn)
|
||||
@@ -62,15 +58,12 @@ func NewFileMetadataCatalogerTask(selection file.Selection) Task {
|
||||
}
|
||||
|
||||
result, err := metadataCataloger.Catalog(ctx, resolver, coordinates...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
|
||||
sbom.Artifacts.FileMetadata = result
|
||||
})
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
return NewTask("file-metadata-cataloger", fn)
|
||||
@@ -87,15 +80,12 @@ func NewFileContentCatalogerTask(cfg filecontent.Config) Task {
|
||||
accessor := builder.(sbomsync.Accessor)
|
||||
|
||||
result, err := cat.Catalog(ctx, resolver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
|
||||
sbom.Artifacts.FileContents = result
|
||||
})
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
return NewTask("file-content-cataloger", fn)
|
||||
@@ -112,15 +102,12 @@ func NewExecutableCatalogerTask(selection file.Selection, cfg executable.Config)
|
||||
accessor := builder.(sbomsync.Accessor)
|
||||
|
||||
result, err := cat.Catalog(resolver)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accessor.WriteToSBOM(func(sbom *sbom.SBOM) {
|
||||
sbom.Artifacts.Executables = result
|
||||
})
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
return NewTask("file-executable-cataloger", fn)
|
||||
@@ -134,9 +121,10 @@ func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accesso
|
||||
}
|
||||
|
||||
if selection == file.FilesOwnedByPackageSelection {
|
||||
var coordinates []file.Coordinates
|
||||
var coordinates file.CoordinateSet
|
||||
|
||||
accessor.ReadFromSBOM(func(sbom *sbom.SBOM) {
|
||||
// get any file coordinates that are owned by a package
|
||||
for _, r := range sbom.Relationships {
|
||||
if r.Type != artifact.ContainsRelationship {
|
||||
continue
|
||||
@@ -145,16 +133,23 @@ func coordinatesForSelection(selection file.Selection, accessor sbomsync.Accesso
|
||||
continue
|
||||
}
|
||||
if c, ok := r.To.(file.Coordinates); ok {
|
||||
coordinates = append(coordinates, c)
|
||||
coordinates.Add(c)
|
||||
}
|
||||
}
|
||||
|
||||
// get any file coordinates referenced by a package directly
|
||||
for p := range sbom.Artifacts.Packages.Enumerate() {
|
||||
coordinates.Add(p.Locations.CoordinateSet().ToSlice()...)
|
||||
}
|
||||
})
|
||||
|
||||
if len(coordinates) == 0 {
|
||||
coords := coordinates.ToSlice()
|
||||
|
||||
if len(coords) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return coordinates, true
|
||||
return coords, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
|
||||
@@ -103,9 +103,6 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
|
||||
t := bus.StartCatalogerTask(info, -1, "")
|
||||
|
||||
pkgs, relationships, err := c.Catalog(ctx, resolver)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to catalog packages with %q: %w", catalogerName, err)
|
||||
}
|
||||
|
||||
log.WithFields("cataloger", catalogerName).Debugf("discovered %d packages", len(pkgs))
|
||||
|
||||
@@ -113,6 +110,8 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
|
||||
|
||||
pkgs, relationships = applyCompliance(cfg.ComplianceConfig, pkgs, relationships)
|
||||
|
||||
finalizeDependencyCompleteness(pkgs)
|
||||
|
||||
sbom.AddPackages(pkgs...)
|
||||
sbom.AddRelationships(relationships...)
|
||||
t.Add(int64(len(pkgs)))
|
||||
@@ -120,13 +119,20 @@ func NewPackageTask(cfg CatalogingFactoryConfig, c pkg.Cataloger, tags ...string
|
||||
t.SetCompleted()
|
||||
log.WithFields("name", catalogerName).Trace("package cataloger completed")
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
tags = append(tags, pkgcataloging.PackageTag)
|
||||
|
||||
return NewTask(c.Name(), fn, tags...)
|
||||
}
|
||||
|
||||
func finalizeDependencyCompleteness(pkgs []pkg.Package) {
|
||||
// ensure that all packages have a non-empty dependency list
|
||||
for i := range pkgs {
|
||||
pkgs[i].Dependencies = pkg.ParseDependencyCompleteness(string(pkgs[i].Dependencies))
|
||||
}
|
||||
}
|
||||
|
||||
func finalizePkgCatalogerResults(cfg CatalogingFactoryConfig, resolver file.PathResolver, catalogerName string, pkgs []pkg.Package, relationships []artifact.Relationship) ([]pkg.Package, []artifact.Relationship) {
|
||||
for i, p := range pkgs {
|
||||
if p.FoundBy == "" {
|
||||
|
||||
@@ -68,7 +68,7 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
|
||||
newSimplePackageTaskFactory(php.NewComposerInstalledCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "php", "composer"),
|
||||
newSimplePackageTaskFactory(r.NewPackageCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "r"),
|
||||
newSimplePackageTaskFactory(ruby.NewInstalledGemSpecCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "ruby", "gem", "gemspec"),
|
||||
newSimplePackageTaskFactory(rust.NewAuditBinaryCataloger, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "rust", "binary"),
|
||||
newSimplePackageTaskFactory(rust.NewAuditBinaryCataloger, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, pkgcataloging.LanguageTag, "rust", "binary"),
|
||||
|
||||
// language-specific package declared catalogers ///////////////////////////////////////////////////////////////////////////
|
||||
newSimplePackageTaskFactory(cpp.NewConanCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.LanguageTag, "cpp", "conan"),
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package task
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/mholt/archiver/v3"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
const unknownsLabelerTaskName = "unknowns-labeler"
|
||||
|
||||
func NewUnknownsLabelerTask(cfg cataloging.UnknownsConfig) Task {
|
||||
return NewTask(unknownsLabelerTaskName, unknownsLabelerTask{cfg}.processUnknowns)
|
||||
}
|
||||
|
||||
type unknownsLabelerTask struct {
|
||||
cataloging.UnknownsConfig
|
||||
}
|
||||
|
||||
// processUnknowns removes unknown entries that have valid packages reported for the locations
|
||||
func (c unknownsLabelerTask) processUnknowns(_ context.Context, resolver file.Resolver, builder sbomsync.Builder) error {
|
||||
accessor := builder.(sbomsync.Accessor)
|
||||
accessor.WriteToSBOM(func(s *sbom.SBOM) {
|
||||
c.finalize(resolver, s)
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c unknownsLabelerTask) finalize(resolver file.Resolver, s *sbom.SBOM) {
|
||||
hasPackageReference := coordinateReferenceLookup(resolver, s)
|
||||
|
||||
if c.RemoveWhenPackagesDefined {
|
||||
for coords := range s.Artifacts.Unknowns {
|
||||
if !hasPackageReference(coords) {
|
||||
continue
|
||||
}
|
||||
delete(s.Artifacts.Unknowns, coords)
|
||||
}
|
||||
}
|
||||
|
||||
if s.Artifacts.Unknowns == nil {
|
||||
s.Artifacts.Unknowns = map[file.Coordinates][]string{}
|
||||
}
|
||||
|
||||
if c.IncludeExecutablesWithoutPackages {
|
||||
for coords := range s.Artifacts.Executables {
|
||||
if !hasPackageReference(coords) {
|
||||
s.Artifacts.Unknowns[coords] = append(s.Artifacts.Unknowns[coords], formatUnknown("no package identified in executable file", unknownsLabelerTaskName))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.IncludeUnexpandedArchives {
|
||||
for coords := range s.Artifacts.FileMetadata {
|
||||
unarchiver, notArchiveErr := archiver.ByExtension(coords.RealPath)
|
||||
if unarchiver != nil && notArchiveErr == nil && !hasPackageReference(coords) {
|
||||
s.Artifacts.Unknowns[coords] = append(s.Artifacts.Unknowns[coords], "archive not cataloged")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func formatUnknown(err string, task ...string) string {
|
||||
return strings.Join(task, "/") + ": " + err
|
||||
}
|
||||
|
||||
func coordinateReferenceLookup(resolver file.Resolver, s *sbom.SBOM) func(coords file.Coordinates) bool {
|
||||
allPackageCoords := file.NewCoordinateSet()
|
||||
|
||||
// include all directly included locations that result in packages
|
||||
for p := range s.Artifacts.Packages.Enumerate() {
|
||||
allPackageCoords.Add(p.Locations.CoordinateSet().ToSlice()...)
|
||||
}
|
||||
|
||||
// include owned files, for example specified by package managers.
|
||||
// relationships for these owned files may be disabled, but we always want to include them
|
||||
for p := range s.Artifacts.Packages.Enumerate() {
|
||||
if f, ok := p.Metadata.(pkg.FileOwner); ok {
|
||||
for _, ownedFilePath := range f.OwnedFiles() {
|
||||
// resolve these owned files, as they may have symlinks
|
||||
// but coordinates we will test against are always absolute paths
|
||||
locations, err := resolver.FilesByPath(ownedFilePath)
|
||||
if err != nil {
|
||||
log.Debugf("unable to resolve owned file '%s': %v", ownedFilePath, err)
|
||||
}
|
||||
for _, loc := range locations {
|
||||
allPackageCoords.Add(loc.Coordinates)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// include relationships
|
||||
for _, r := range s.Relationships {
|
||||
_, fromPkgOk := r.From.(pkg.Package)
|
||||
fromFile, fromFileOk := r.From.(file.Coordinates)
|
||||
_, toPkgOk := r.To.(pkg.Package)
|
||||
toFile, toFileOk := r.To.(file.Coordinates)
|
||||
if fromPkgOk && toFileOk {
|
||||
allPackageCoords.Add(toFile)
|
||||
} else if fromFileOk && toPkgOk {
|
||||
allPackageCoords.Add(fromFile)
|
||||
}
|
||||
}
|
||||
|
||||
return allPackageCoords.Contains
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package unknown
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
type hasCoordinates interface {
|
||||
GetCoordinates() file.Coordinates
|
||||
}
|
||||
|
||||
type CoordinateError struct {
|
||||
Coordinates file.Coordinates
|
||||
Reason error
|
||||
}
|
||||
|
||||
var _ error = (*CoordinateError)(nil)
|
||||
|
||||
func (u *CoordinateError) Error() string {
|
||||
if u.Coordinates.FileSystemID == "" {
|
||||
return fmt.Sprintf("%s: %v", u.Coordinates.RealPath, u.Reason)
|
||||
}
|
||||
return fmt.Sprintf("%s (%s): %v", u.Coordinates.RealPath, u.Coordinates.FileSystemID, u.Reason)
|
||||
}
|
||||
|
||||
// New returns a new CoordinateError unless the reason is a CoordinateError itself, in which case
|
||||
// reason will be returned directly or if reason is nil, nil will be returned
|
||||
func New(coords hasCoordinates, reason error) *CoordinateError {
|
||||
if reason == nil {
|
||||
return nil
|
||||
}
|
||||
coordinates := coords.GetCoordinates()
|
||||
reasonCoordinateError := &CoordinateError{}
|
||||
if errors.As(reason, &reasonCoordinateError) {
|
||||
// if the reason is already a coordinate error, it is potentially for a different location,
|
||||
// so we do not want to surface this location having an error
|
||||
return reasonCoordinateError
|
||||
}
|
||||
return &CoordinateError{
|
||||
Coordinates: coordinates,
|
||||
Reason: reason,
|
||||
}
|
||||
}
|
||||
|
||||
// Newf returns a new CoordinateError with a reason of an error created from given format and args
|
||||
func Newf(coords hasCoordinates, format string, args ...any) *CoordinateError {
|
||||
return New(coords, fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// Append returns an error joined to the first error/set of errors, with a new CoordinateError appended to the end
|
||||
func Append(errs error, coords hasCoordinates, reason error) error {
|
||||
return Join(errs, New(coords, reason))
|
||||
}
|
||||
|
||||
// Appendf returns an error joined to the first error/set of errors, with a new CoordinateError appended to the end,
|
||||
// created from the given reason and args
|
||||
func Appendf(errs error, coords hasCoordinates, format string, args ...any) error {
|
||||
return Append(errs, coords, fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// Join joins the provided sets of errors together in a flattened manner, taking into account nested errors created
|
||||
// from other sources, including errors.Join, multierror.Append, and unknown.Join
|
||||
func Join(errs ...error) error {
|
||||
var out []error
|
||||
for _, err := range errs {
|
||||
// append errors, de-duplicated
|
||||
for _, e := range flatten(err) {
|
||||
if containsErr(out, e) {
|
||||
continue
|
||||
}
|
||||
out = append(out, e)
|
||||
}
|
||||
}
|
||||
if len(out) == 1 {
|
||||
return out[0]
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil
|
||||
}
|
||||
return errors.Join(out...)
|
||||
}
|
||||
|
||||
// Joinf joins the provided sets of errors together in a flattened manner, taking into account nested errors created
|
||||
// from other sources, including errors.Join, multierror.Append, and unknown.Join and appending a new error,
|
||||
// created from the format and args provided -- the error is NOT a CoordinateError
|
||||
func Joinf(errs error, format string, args ...any) error {
|
||||
return Join(errs, fmt.Errorf(format, args...))
|
||||
}
|
||||
|
||||
// IfEmptyf returns a new Errorf-formatted error, only when the provided slice is empty or nil when
|
||||
// the slice has entries
|
||||
func IfEmptyf[T any](emptyTest []T, format string, args ...any) error {
|
||||
if len(emptyTest) == 0 {
|
||||
return fmt.Errorf(format, args...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExtractCoordinateErrors extracts all coordinate errors returned, and any _additional_ errors in the graph
|
||||
// are encapsulated in the second, error return parameter
|
||||
func ExtractCoordinateErrors(err error) (coordinateErrors []CoordinateError, remainingErrors error) {
|
||||
remainingErrors = visitErrors(err, func(e error) error {
|
||||
if coordinateError, _ := e.(*CoordinateError); coordinateError != nil {
|
||||
coordinateErrors = append(coordinateErrors, *coordinateError)
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
})
|
||||
return coordinateErrors, remainingErrors
|
||||
}
|
||||
|
||||
func flatten(errs ...error) []error {
|
||||
var out []error
|
||||
for _, err := range errs {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
// turn all errors nested under a coordinate error to individual coordinate errors
|
||||
if e, ok := err.(*CoordinateError); ok {
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
for _, r := range flatten(e.Reason) {
|
||||
out = append(out, New(e.Coordinates, r))
|
||||
}
|
||||
} else
|
||||
// from multierror.Append
|
||||
if e, ok := err.(interface{ WrappedErrors() []error }); ok {
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, flatten(e.WrappedErrors()...)...)
|
||||
} else
|
||||
// from errors.Join
|
||||
if e, ok := err.(interface{ Unwrap() []error }); ok {
|
||||
if e == nil {
|
||||
continue
|
||||
}
|
||||
out = append(out, flatten(e.Unwrap()...)...)
|
||||
} else {
|
||||
out = append(out, err)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// containsErr returns true if a duplicate error is found
|
||||
func containsErr(out []error, err error) bool {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
log.Tracef("error comparing errors: %v", err)
|
||||
}
|
||||
}()
|
||||
for _, e := range out {
|
||||
if e == err {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// visitErrors visits every wrapped error. the returned error replaces the provided error, null errors are omitted from
|
||||
// the object graph
|
||||
func visitErrors(err error, fn func(error) error) error {
|
||||
// unwrap errors from errors.Join
|
||||
if errs, ok := err.(interface{ Unwrap() []error }); ok {
|
||||
var out []error
|
||||
for _, e := range errs.Unwrap() {
|
||||
out = append(out, visitErrors(e, fn))
|
||||
}
|
||||
// errors.Join omits nil errors and will return nil if all passed errors are nil
|
||||
return errors.Join(out...)
|
||||
}
|
||||
// unwrap errors from multierror.Append -- these also implement Unwrap() error, so check this first
|
||||
if errs, ok := err.(interface{ WrappedErrors() []error }); ok {
|
||||
var out []error
|
||||
for _, e := range errs.WrappedErrors() {
|
||||
out = append(out, visitErrors(e, fn))
|
||||
}
|
||||
// errors.Join omits nil errors and will return nil if all passed errors are nil
|
||||
return errors.Join(out...)
|
||||
}
|
||||
// unwrap singly wrapped errors
|
||||
if e, ok := err.(interface{ Unwrap() error }); ok {
|
||||
wrapped := e.Unwrap()
|
||||
got := visitErrors(wrapped, fn)
|
||||
if got == nil {
|
||||
return nil
|
||||
}
|
||||
if wrapped.Error() != got.Error() {
|
||||
prefix := strings.TrimSuffix(err.Error(), wrapped.Error())
|
||||
return fmt.Errorf("%s%w", prefix, got)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return fn(err)
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
package unknown
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
func Test_visitErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in error
|
||||
transform func(error) error
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "return",
|
||||
in: fmt.Errorf("err1"),
|
||||
transform: func(e error) error {
|
||||
return e
|
||||
},
|
||||
expected: "err1",
|
||||
},
|
||||
{
|
||||
name: "omit",
|
||||
in: fmt.Errorf("err1"),
|
||||
transform: func(_ error) error {
|
||||
return nil
|
||||
},
|
||||
expected: "<nil>",
|
||||
},
|
||||
{
|
||||
name: "wrapped return",
|
||||
in: fmt.Errorf("wrapped: %w", fmt.Errorf("err1")),
|
||||
transform: func(e error) error {
|
||||
return e
|
||||
},
|
||||
expected: "wrapped: err1",
|
||||
},
|
||||
{
|
||||
name: "wrapped omit",
|
||||
in: fmt.Errorf("wrapped: %w", fmt.Errorf("err1")),
|
||||
transform: func(e error) error {
|
||||
if e.Error() == "err1" {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
},
|
||||
expected: "<nil>",
|
||||
},
|
||||
{
|
||||
name: "joined return",
|
||||
in: errors.Join(fmt.Errorf("err1"), fmt.Errorf("err2")),
|
||||
transform: func(e error) error {
|
||||
return e
|
||||
},
|
||||
expected: "err1\nerr2",
|
||||
},
|
||||
{
|
||||
name: "joined omit",
|
||||
in: errors.Join(fmt.Errorf("err1"), fmt.Errorf("err2")),
|
||||
transform: func(_ error) error {
|
||||
return nil
|
||||
},
|
||||
expected: "<nil>",
|
||||
},
|
||||
{
|
||||
name: "joined omit first",
|
||||
in: errors.Join(fmt.Errorf("err1"), fmt.Errorf("err2")),
|
||||
transform: func(e error) error {
|
||||
if e.Error() == "err1" {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
},
|
||||
expected: "err2",
|
||||
},
|
||||
{
|
||||
name: "joined wrapped return",
|
||||
in: errors.Join(fmt.Errorf("wrapped: %w", fmt.Errorf("err1")), fmt.Errorf("err2")),
|
||||
transform: func(e error) error {
|
||||
return e
|
||||
},
|
||||
expected: "wrapped: err1\nerr2",
|
||||
},
|
||||
{
|
||||
name: "joined wrapped omit first",
|
||||
in: errors.Join(fmt.Errorf("wrapped: %w", fmt.Errorf("err1")), fmt.Errorf("err2")),
|
||||
transform: func(e error) error {
|
||||
if e.Error() == "err1" {
|
||||
return nil
|
||||
}
|
||||
return e
|
||||
},
|
||||
expected: "err2",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
gotErr := visitErrors(test.in, test.transform)
|
||||
got := fmt.Sprintf("%v", gotErr)
|
||||
require.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Join(t *testing.T) {
|
||||
err1 := fmt.Errorf("err1")
|
||||
err2 := fmt.Errorf("err2")
|
||||
|
||||
tests := []struct {
|
||||
name string ``
|
||||
in []error
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "basic",
|
||||
in: []error{fmt.Errorf("err")},
|
||||
expected: "err",
|
||||
},
|
||||
{
|
||||
name: "wrapped",
|
||||
in: []error{fmt.Errorf("outer: %w", fmt.Errorf("err"))},
|
||||
expected: "outer: err",
|
||||
},
|
||||
{
|
||||
name: "wrapped joined",
|
||||
in: []error{errors.Join(fmt.Errorf("outer: %w", fmt.Errorf("err1")), fmt.Errorf("err2"))},
|
||||
expected: "outer: err1\nerr2",
|
||||
},
|
||||
{
|
||||
name: "duplicates",
|
||||
in: []error{err1, err1, err2},
|
||||
expected: "err1\nerr2",
|
||||
},
|
||||
{
|
||||
name: "nested duplicates",
|
||||
in: []error{errors.Join(err1, err2), err1, err2},
|
||||
expected: "err1\nerr2",
|
||||
},
|
||||
{
|
||||
name: "nested duplicates coords",
|
||||
in: []error{New(file.NewLocation("l1"), errors.Join(fmt.Errorf("err1"), fmt.Errorf("err2"))), fmt.Errorf("err1"), fmt.Errorf("err2")},
|
||||
expected: "l1: err1\nl1: err2\nerr1\nerr2",
|
||||
},
|
||||
{
|
||||
name: "all nil",
|
||||
in: []error{nil, nil, nil},
|
||||
expected: "",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := Join(test.in...)
|
||||
if test.expected == "" {
|
||||
require.Nil(t, got)
|
||||
return
|
||||
}
|
||||
require.NotNil(t, got)
|
||||
require.Equal(t, test.expected, got.Error())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_flatten(t *testing.T) {
|
||||
coords := file.Coordinates{
|
||||
RealPath: "real/path",
|
||||
}
|
||||
e1 := fmt.Errorf("e1")
|
||||
e2 := fmt.Errorf("e2")
|
||||
c1 := New(coords, fmt.Errorf("c1"))
|
||||
c2 := New(coords, fmt.Errorf("c2"))
|
||||
tests := []struct {
|
||||
name string ``
|
||||
in error
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "basic",
|
||||
in: errors.Join(e1, e2),
|
||||
expected: "e1//e2",
|
||||
},
|
||||
{
|
||||
name: "coords",
|
||||
in: New(coords, e1),
|
||||
expected: "real/path: e1",
|
||||
},
|
||||
{
|
||||
name: "coords with joined children",
|
||||
in: New(coords, errors.Join(e1, e2)),
|
||||
expected: "real/path: e1//real/path: e2",
|
||||
},
|
||||
{
|
||||
name: "very nested",
|
||||
in: errors.Join(errors.Join(errors.Join(errors.Join(e1, c1), e2), c2), e2),
|
||||
expected: "e1//real/path: c1//e2//real/path: c2//e2",
|
||||
},
|
||||
}
|
||||
toString := func(errs ...error) string {
|
||||
var parts []string
|
||||
for _, e := range errs {
|
||||
parts = append(parts, e.Error())
|
||||
}
|
||||
return strings.Join(parts, "//")
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got := flatten(test.in)
|
||||
require.NotNil(t, got)
|
||||
require.Equal(t, test.expected, toString(got...))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_Append(t *testing.T) {
|
||||
e1 := New(file.NewLocation("l1"), fmt.Errorf("e1"))
|
||||
e2 := Append(e1, file.NewLocation("l2"), fmt.Errorf("e2"))
|
||||
e3 := Appendf(e2, file.NewLocation("l3"), "%s", "e3")
|
||||
require.Equal(t, "l1: e1\nl2: e2\nl3: e3", e3.Error())
|
||||
|
||||
e1 = New(file.NewLocation("l1"), nil)
|
||||
require.Nil(t, e1)
|
||||
e2 = Append(e1, file.NewLocation("l2"), fmt.Errorf("e2"))
|
||||
e3 = Appendf(e2, file.NewLocation("l3"), "%s", "e3")
|
||||
require.Equal(t, "l2: e2\nl3: e3", e3.Error())
|
||||
|
||||
e1 = New(file.NewLocation("l1"), fmt.Errorf("e1"))
|
||||
e2 = Append(e1, file.NewLocation("l2"), nil)
|
||||
e3 = Appendf(e2, file.NewLocation("l3"), "%s", "e3")
|
||||
require.Equal(t, "l1: e1\nl3: e3", e3.Error())
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package unknown
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
var pathErrorRegex = regexp.MustCompile(`.*path="([^"]+)".*`)
|
||||
|
||||
// ProcessPathErrors replaces "path" errors returned from the file.Resolver into unknowns,
|
||||
// and warn logs non-unknown errors, returning only the unknown errors
|
||||
func ProcessPathErrors(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
errText := err.Error()
|
||||
if pathErrorRegex.MatchString(errText) {
|
||||
foundPath := pathErrorRegex.ReplaceAllString(err.Error(), "$1")
|
||||
if foundPath != "" {
|
||||
return New(file.NewLocation(foundPath), err)
|
||||
}
|
||||
}
|
||||
unknowns, remainingErrors := ExtractCoordinateErrors(err)
|
||||
log.Warn(remainingErrors)
|
||||
|
||||
var out []error
|
||||
for _, u := range unknowns {
|
||||
out = append(out, &u)
|
||||
}
|
||||
return Join(out...)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package unknown
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
func Test_ProcessPathErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
errorText string
|
||||
expected error
|
||||
}{
|
||||
{
|
||||
errorText: `prefix path="/var/lib/thing" suffix`,
|
||||
expected: &CoordinateError{
|
||||
Coordinates: file.Coordinates{
|
||||
RealPath: "/var/lib/thing",
|
||||
},
|
||||
Reason: fmt.Errorf(`prefix path="/var/lib/thing" suffix`),
|
||||
},
|
||||
},
|
||||
{
|
||||
errorText: `prefix path="/var/lib/thing"`,
|
||||
expected: &CoordinateError{
|
||||
Coordinates: file.Coordinates{
|
||||
RealPath: "/var/lib/thing",
|
||||
},
|
||||
Reason: fmt.Errorf(`prefix path="/var/lib/thing"`),
|
||||
},
|
||||
},
|
||||
{
|
||||
errorText: `path="/var/lib/thing" suffix`,
|
||||
expected: &CoordinateError{
|
||||
Coordinates: file.Coordinates{
|
||||
RealPath: "/var/lib/thing",
|
||||
},
|
||||
Reason: fmt.Errorf(`path="/var/lib/thing" suffix`),
|
||||
},
|
||||
},
|
||||
{
|
||||
errorText: "all your base are belong to us",
|
||||
expected: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.errorText, func(t *testing.T) {
|
||||
got := ProcessPathErrors(fmt.Errorf("%s", test.errorText))
|
||||
require.Equal(t, test.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.17/document",
|
||||
"$id": "anchore.io/schema/syft/json/16.0.19/document",
|
||||
"$ref": "#/$defs/Document",
|
||||
"$defs": {
|
||||
"AlpmDbEntry": {
|
||||
@@ -777,6 +777,12 @@
|
||||
},
|
||||
"executable": {
|
||||
"$ref": "#/$defs/Executable"
|
||||
},
|
||||
"unknowns": {
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -1604,6 +1610,9 @@
|
||||
"purl": {
|
||||
"type": "string"
|
||||
},
|
||||
"dependencies": {
|
||||
"type": "string"
|
||||
},
|
||||
"metadataType": {
|
||||
"type": "string"
|
||||
},
|
||||
@@ -1767,7 +1776,8 @@
|
||||
"licenses",
|
||||
"language",
|
||||
"cpes",
|
||||
"purl"
|
||||
"purl",
|
||||
"dependencies"
|
||||
]
|
||||
},
|
||||
"PhpComposerAuthors": {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package cataloging
|
||||
|
||||
type UnknownsConfig struct {
|
||||
RemoveWhenPackagesDefined bool
|
||||
IncludeExecutablesWithoutPackages bool
|
||||
IncludeUnexpandedArchives bool
|
||||
}
|
||||
|
||||
func DefaultUnknownsConfig() UnknownsConfig {
|
||||
return UnknownsConfig{
|
||||
RemoveWhenPackagesDefined: true,
|
||||
IncludeExecutablesWithoutPackages: true,
|
||||
IncludeUnexpandedArchives: true,
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -95,7 +95,7 @@ func NewAttributes(cpeStr string) (Attributes, error) {
|
||||
}
|
||||
|
||||
// ensure that this Attributes can be validated after being fully sanitized
|
||||
if ValidateString(c.String()) != nil {
|
||||
if err = ValidateString(c.String()); err != nil {
|
||||
return Attributes{}, err
|
||||
}
|
||||
|
||||
|
||||
+10
-2
@@ -17,6 +17,7 @@ func Test_NewAttributes(t *testing.T) {
|
||||
name string
|
||||
input string
|
||||
expected Attributes
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "gocase",
|
||||
@@ -33,14 +34,21 @@ func Test_NewAttributes(t *testing.T) {
|
||||
input: `cpe:/a:%240.99_kindle_books_project:%240.99_kindle_books:6::~~~android~~`,
|
||||
expected: MustAttributes(`cpe:2.3:a:\$0.99_kindle_books_project:\$0.99_kindle_books:6:*:*:*:*:android:*:*`),
|
||||
},
|
||||
{
|
||||
name: "null byte in version for some reason",
|
||||
input: "cpe:2.3:a:oracle:openjdk:11.0.22+7\u0000-J-ms8m:*:*:*:*:*:*:*",
|
||||
wantErr: require.Error,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
actual, err := NewAttributes(test.input)
|
||||
if err != nil {
|
||||
t.Fatalf("got an error while creating Attributes: %+v", err)
|
||||
if test.wantErr != nil {
|
||||
test.wantErr(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
if d := cmp.Diff(actual, test.expected); d != "" {
|
||||
t.Errorf("Attributes mismatch (-want +got):\n%s", d)
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"github.com/anchore/syft/internal/licenses"
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/internal/task"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
@@ -60,6 +61,9 @@ func CreateSBOM(ctx context.Context, src source.Source, cfg *CreateSBOMConfig) (
|
||||
},
|
||||
}
|
||||
|
||||
// inject a single license scanner for all package cataloging tasks into context
|
||||
ctx = licenses.SetContextLicenseScanner(ctx, licenses.NewDefaultScanner())
|
||||
|
||||
catalogingProgress := monitorCatalogingTask(src.ID(), taskGroups)
|
||||
packageCatalogingProgress := monitorPackageCatalogingTask()
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ type CreateSBOMConfig struct {
|
||||
Compliance cataloging.ComplianceConfig
|
||||
Search cataloging.SearchConfig
|
||||
Relationships cataloging.RelationshipsConfig
|
||||
Unknowns cataloging.UnknownsConfig
|
||||
DataGeneration cataloging.DataGenerationConfig
|
||||
Packages pkgcataloging.Config
|
||||
Files filecataloging.Config
|
||||
@@ -113,6 +114,12 @@ func (c *CreateSBOMConfig) WithRelationshipsConfig(cfg cataloging.RelationshipsC
|
||||
return c
|
||||
}
|
||||
|
||||
// WithUnknownsConfig allows for defining the specific behavior dealing with unknowns
|
||||
func (c *CreateSBOMConfig) WithUnknownsConfig(cfg cataloging.UnknownsConfig) *CreateSBOMConfig {
|
||||
c.Unknowns = cfg
|
||||
return c
|
||||
}
|
||||
|
||||
// WithDataGenerationConfig allows for defining what data elements that cannot be discovered from the underlying
|
||||
// target being scanned that should be generated after package creation.
|
||||
func (c *CreateSBOMConfig) WithDataGenerationConfig(cfg cataloging.DataGenerationConfig) *CreateSBOMConfig {
|
||||
@@ -173,6 +180,7 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
|
||||
// generate package and file tasks based on the configuration
|
||||
environmentTasks := c.environmentTasks()
|
||||
relationshipsTasks := c.relationshipTasks(src)
|
||||
unknownTasks := c.unknownsTasks()
|
||||
fileTasks := c.fileTasks()
|
||||
pkgTasks, selectionEvidence, err := c.packageTasks(src)
|
||||
if err != nil {
|
||||
@@ -192,6 +200,11 @@ func (c *CreateSBOMConfig) makeTaskGroups(src source.Description) ([][]task.Task
|
||||
taskGroups = append(taskGroups, relationshipsTasks)
|
||||
}
|
||||
|
||||
// all unknowns tasks should happen after all scanning is complete
|
||||
if len(unknownTasks) > 0 {
|
||||
taskGroups = append(taskGroups, unknownTasks)
|
||||
}
|
||||
|
||||
// identifying the environment (i.e. the linux release) must be done first as this is required for package cataloging
|
||||
taskGroups = append(
|
||||
[][]task.Task{
|
||||
@@ -338,6 +351,18 @@ func (c *CreateSBOMConfig) environmentTasks() []task.Task {
|
||||
return tsks
|
||||
}
|
||||
|
||||
// unknownsTasks returns a set of tasks that perform any necessary post-processing
|
||||
// to identify SBOM elements as unknowns
|
||||
func (c *CreateSBOMConfig) unknownsTasks() []task.Task {
|
||||
var tasks []task.Task
|
||||
|
||||
if t := task.NewUnknownsLabelerTask(c.Unknowns); t != nil {
|
||||
tasks = append(tasks, t)
|
||||
}
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
func (c *CreateSBOMConfig) validate() error {
|
||||
if c.Relationships.ExcludeBinaryPackagesWithFileOwnershipOverlap {
|
||||
if !c.Relationships.PackageFileOwnershipOverlap {
|
||||
|
||||
@@ -90,6 +90,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -108,6 +109,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
pkgCatalogerNamesWithTagOrName(t, "directory"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -127,6 +129,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
pkgCatalogerNamesWithTagOrName(t, "directory"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -145,6 +148,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
fileCatalogerNames(false, true, true), // note: the digest cataloger is not included
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -163,6 +167,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
// note: there are no file catalogers in their own group
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -184,6 +189,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
fileCatalogerNames(true, true, true)...,
|
||||
),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -204,6 +210,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "persistent"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -224,6 +231,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
addTo(pkgCatalogerNamesWithTagOrName(t, "directory"), "persistent"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -244,6 +252,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
addTo(pkgIntersect("image", "javascript"), "persistent"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -265,6 +274,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
addTo(pkgCatalogerNamesWithTagOrName(t, "image"), "user-provided"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -285,6 +295,7 @@ func TestCreateSBOMConfig_makeTaskGroups(t *testing.T) {
|
||||
pkgCatalogerNamesWithTagOrName(t, "image"),
|
||||
fileCatalogerNames(true, true, true),
|
||||
relationshipCatalogerNames(),
|
||||
unknownsTaskNames(),
|
||||
},
|
||||
wantManifest: &catalogerManifest{
|
||||
Requested: pkgcataloging.SelectionRequest{
|
||||
@@ -385,6 +396,10 @@ func relationshipCatalogerNames() []string {
|
||||
return []string{"relationships-cataloger"}
|
||||
}
|
||||
|
||||
func unknownsTaskNames() []string {
|
||||
return []string{"unknowns-labeler"}
|
||||
}
|
||||
|
||||
func environmentCatalogerNames() []string {
|
||||
return []string{"environment-cataloger"}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/mimetype"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/event/monitor"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
@@ -45,6 +46,8 @@ func NewCataloger(cfg Config) *Cataloger {
|
||||
}
|
||||
|
||||
func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.Executable, error) {
|
||||
var errs error
|
||||
|
||||
locs, err := resolver.FilesByMIMEType(i.config.MIMETypes...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get file locations for binaries: %w", err)
|
||||
@@ -61,7 +64,10 @@ func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.E
|
||||
for _, loc := range locs {
|
||||
prog.AtomicStage.Set(loc.Path())
|
||||
|
||||
exec := processExecutableLocation(loc, resolver)
|
||||
exec, err := processExecutableLocation(loc, resolver)
|
||||
if err != nil {
|
||||
errs = unknown.Append(errs, loc, err)
|
||||
}
|
||||
|
||||
if exec != nil {
|
||||
prog.Increment()
|
||||
@@ -74,30 +80,24 @@ func (i *Cataloger) Catalog(resolver file.Resolver) (map[file.Coordinates]file.E
|
||||
prog.AtomicStage.Set(fmt.Sprintf("%s executables", humanize.Comma(prog.Current())))
|
||||
prog.SetCompleted()
|
||||
|
||||
return results, nil
|
||||
return results, errs
|
||||
}
|
||||
|
||||
func processExecutableLocation(loc file.Location, resolver file.Resolver) *file.Executable {
|
||||
func processExecutableLocation(loc file.Location, resolver file.Resolver) (*file.Executable, error) {
|
||||
reader, err := resolver.FileContentsByLocation(loc)
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Warnf("unable to get file contents for %q", loc.RealPath)
|
||||
return nil
|
||||
return nil, fmt.Errorf("unable to get file contents: %w", err)
|
||||
}
|
||||
defer internal.CloseAndLogError(reader, loc.RealPath)
|
||||
|
||||
uReader, err := unionreader.GetUnionReader(reader)
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Warnf("unable to get union reader for %q", loc.RealPath)
|
||||
return nil
|
||||
return nil, fmt.Errorf("unable to get union reader: %w", err)
|
||||
}
|
||||
|
||||
exec, err := processExecutable(loc, uReader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Warnf("unable to process executable %q", loc.RealPath)
|
||||
}
|
||||
return exec
|
||||
return processExecutable(loc, uReader)
|
||||
}
|
||||
|
||||
func catalogingProgress(locations int64) *monitor.CatalogerTaskProgress {
|
||||
@@ -153,10 +153,12 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
|
||||
|
||||
format, err := findExecutableFormat(reader)
|
||||
if err != nil {
|
||||
log.Debugf("unable to determine executable kind for %v: %v", loc.RealPath, err)
|
||||
return nil, fmt.Errorf("unable to determine executable kind: %w", err)
|
||||
}
|
||||
|
||||
if format == "" {
|
||||
// this is not an "unknown", so just log -- this binary does not have parseable data in it
|
||||
log.Debugf("unable to determine executable format for %q", loc.RealPath)
|
||||
return nil, nil
|
||||
}
|
||||
@@ -165,16 +167,19 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
|
||||
|
||||
switch format {
|
||||
case file.ELF:
|
||||
if err := findELFFeatures(&data, reader); err != nil {
|
||||
if err = findELFFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine ELF features for %q", loc.RealPath)
|
||||
err = fmt.Errorf("unable to determine ELF features: %w", err)
|
||||
}
|
||||
case file.PE:
|
||||
if err := findPEFeatures(&data, reader); err != nil {
|
||||
if err = findPEFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine PE features for %q", loc.RealPath)
|
||||
err = fmt.Errorf("unable to determine PE features: %w", err)
|
||||
}
|
||||
case file.MachO:
|
||||
if err := findMachoFeatures(&data, reader); err != nil {
|
||||
if err = findMachoFeatures(&data, reader); err != nil {
|
||||
log.WithFields("error", err).Tracef("unable to determine Macho features for %q", loc.RealPath)
|
||||
err = fmt.Errorf("unable to determine Macho features: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +188,7 @@ func processExecutable(loc file.Location, reader unionreader.UnionReader) (*file
|
||||
data.ImportedLibraries = []string{}
|
||||
}
|
||||
|
||||
return &data, nil
|
||||
return &data, err
|
||||
}
|
||||
|
||||
func findExecutableFormat(reader unionreader.UnionReader) (file.ExecutableFormat, error) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/scylladb/go-set/strset"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
)
|
||||
@@ -20,8 +21,8 @@ func findELFFeatures(data *file.Executable, reader unionreader.UnionReader) erro
|
||||
|
||||
libs, err := f.ImportedLibraries()
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Trace("unable to read imported libraries from elf file")
|
||||
err = unknown.Joinf(err, "unable to read imported libraries from elf file: %w", err)
|
||||
libs = nil
|
||||
}
|
||||
|
||||
@@ -34,7 +35,7 @@ func findELFFeatures(data *file.Executable, reader unionreader.UnionReader) erro
|
||||
data.HasEntrypoint = elfHasEntrypoint(f)
|
||||
data.HasExports = elfHasExports(f)
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func findELFSecurityFeatures(f *elf.File) *file.ELFSecurityFeatures {
|
||||
@@ -62,7 +63,6 @@ func checkElfStackCanary(file *elf.File) *bool {
|
||||
func hasAnyDynamicSymbols(file *elf.File, symbolNames ...string) *bool {
|
||||
dynSyms, err := file.DynamicSymbols()
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Trace("unable to read dynamic symbols from elf file")
|
||||
return nil
|
||||
}
|
||||
@@ -129,7 +129,6 @@ func hasBindNowDynTagOrFlag(f *elf.File) bool {
|
||||
func hasElfDynFlag(f *elf.File, flag elf.DynFlag) bool {
|
||||
vals, err := f.DynValue(elf.DT_FLAGS)
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Trace("unable to read DT_FLAGS from elf file")
|
||||
return false
|
||||
}
|
||||
@@ -144,7 +143,6 @@ func hasElfDynFlag(f *elf.File, flag elf.DynFlag) bool {
|
||||
func hasElfDynFlag1(f *elf.File, flag elf.DynFlag1) bool {
|
||||
vals, err := f.DynValue(elf.DT_FLAGS_1)
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Trace("unable to read DT_FLAGS_1 from elf file")
|
||||
return false
|
||||
}
|
||||
@@ -203,7 +201,6 @@ func checkLLVMControlFlowIntegrity(file *elf.File) *bool {
|
||||
// look for any symbols that are functions and end with ".cfi"
|
||||
dynSyms, err := file.Symbols()
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Trace("unable to read symbols from elf file")
|
||||
return nil
|
||||
}
|
||||
@@ -225,7 +222,6 @@ var fortifyPattern = regexp.MustCompile(`__\w+_chk@.+`)
|
||||
func checkClangFortifySource(file *elf.File) *bool {
|
||||
dynSyms, err := file.Symbols()
|
||||
if err != nil {
|
||||
// TODO: known-unknowns
|
||||
log.WithFields("error", err).Trace("unable to read symbols from elf file")
|
||||
return nil
|
||||
}
|
||||
@@ -254,7 +250,7 @@ func elfHasExports(f *elf.File) bool {
|
||||
// really anything that is not marked with 'U' (undefined) is considered an export.
|
||||
symbols, err := f.DynamicSymbols()
|
||||
if err != nil {
|
||||
// TODO: known-unknowns?
|
||||
log.WithFields("error", err).Trace("unable to get ELF dynamic symbols")
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ func Test_findELFSecurityFeatures(t *testing.T) {
|
||||
name string
|
||||
fixture string
|
||||
want *file.ELFSecurityFeatures
|
||||
wantErr require.ErrorAssertionFunc
|
||||
wantStripped bool
|
||||
}{
|
||||
{
|
||||
@@ -221,6 +222,7 @@ func Test_elfHasExports(t *testing.T) {
|
||||
f, err := elf.NewFile(readerForFixture(t, tt.fixture))
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.want, elfHasExports(f))
|
||||
require.NoError(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
intFile "github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/event/monitor"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
@@ -46,6 +47,7 @@ func NewCataloger(cfg Config) *Cataloger {
|
||||
func (i *Cataloger) Catalog(_ context.Context, resolver file.Resolver) (map[file.Coordinates]string, error) {
|
||||
results := make(map[file.Coordinates]string)
|
||||
var locations []file.Location
|
||||
var errs error
|
||||
|
||||
locations, err := resolver.FilesByGlob(i.globs...)
|
||||
if err != nil {
|
||||
@@ -59,8 +61,9 @@ func (i *Cataloger) Catalog(_ context.Context, resolver file.Resolver) (map[file
|
||||
|
||||
metadata, err := resolver.FileMetadataByLocation(location)
|
||||
if err != nil {
|
||||
errs = unknown.Append(errs, location, err)
|
||||
prog.SetError(err)
|
||||
return nil, err
|
||||
continue
|
||||
}
|
||||
|
||||
if i.skipFilesAboveSizeInBytes > 0 && metadata.Size() > i.skipFilesAboveSizeInBytes {
|
||||
@@ -69,12 +72,12 @@ func (i *Cataloger) Catalog(_ context.Context, resolver file.Resolver) (map[file
|
||||
|
||||
result, err := i.catalogLocation(resolver, location)
|
||||
if internal.IsErrPathPermission(err) {
|
||||
log.Debugf("file contents cataloger skipping - %+v", err)
|
||||
errs = unknown.Append(errs, location, fmt.Errorf("permission error reading file contents: %w", err))
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
prog.SetError(err)
|
||||
return nil, err
|
||||
errs = unknown.Append(errs, location, err)
|
||||
continue
|
||||
}
|
||||
|
||||
prog.Increment()
|
||||
@@ -87,7 +90,7 @@ func (i *Cataloger) Catalog(_ context.Context, resolver file.Resolver) (map[file
|
||||
prog.AtomicStage.Set(fmt.Sprintf("%s files", humanize.Comma(prog.Current())))
|
||||
prog.SetCompleted()
|
||||
|
||||
return results, nil
|
||||
return results, errs
|
||||
}
|
||||
|
||||
func (i *Cataloger) catalogLocation(resolver file.Resolver, location file.Location) (string, error) {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
intFile "github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/event/monitor"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
intCataloger "github.com/anchore/syft/syft/file/cataloger/internal"
|
||||
@@ -33,6 +34,7 @@ func NewCataloger(hashes []crypto.Hash) *Cataloger {
|
||||
func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates][]file.Digest, error) {
|
||||
results := make(map[file.Coordinates][]file.Digest)
|
||||
var locations []file.Location
|
||||
var errs error
|
||||
|
||||
if len(coordinates) == 0 {
|
||||
locations = intCataloger.AllRegularFiles(ctx, resolver)
|
||||
@@ -58,12 +60,14 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
|
||||
|
||||
if internal.IsErrPathPermission(err) {
|
||||
log.Debugf("file digests cataloger skipping %q: %+v", location.RealPath, err)
|
||||
errs = unknown.Append(errs, location, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
prog.SetError(err)
|
||||
return nil, fmt.Errorf("failed to process file %q: %w", location.RealPath, err)
|
||||
errs = unknown.Append(errs, location, err)
|
||||
continue
|
||||
}
|
||||
|
||||
prog.Increment()
|
||||
@@ -76,7 +80,7 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
|
||||
prog.AtomicStage.Set(fmt.Sprintf("%s files", humanize.Comma(prog.Current())))
|
||||
prog.SetCompleted()
|
||||
|
||||
return results, nil
|
||||
return results, errs
|
||||
}
|
||||
|
||||
func (i *Cataloger) catalogLocation(resolver file.Resolver, location file.Location) ([]file.Digest, error) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/anchore/syft/internal/bus"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/event/monitor"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
@@ -20,6 +21,7 @@ func NewCataloger() *Cataloger {
|
||||
}
|
||||
|
||||
func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordinates ...file.Coordinates) (map[file.Coordinates]file.Metadata, error) {
|
||||
var errs error
|
||||
results := make(map[file.Coordinates]file.Metadata)
|
||||
var locations <-chan file.Location
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
@@ -34,7 +36,7 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
|
||||
for _, c := range coordinates {
|
||||
locs, err := resolver.FilesByPath(c.RealPath)
|
||||
if err != nil {
|
||||
log.Warn("unable to get file locations for path %q: %w", c.RealPath, err)
|
||||
errs = unknown.Append(errs, c, err)
|
||||
continue
|
||||
}
|
||||
for _, loc := range locs {
|
||||
@@ -71,7 +73,7 @@ func (i *Cataloger) Catalog(ctx context.Context, resolver file.Resolver, coordin
|
||||
prog.AtomicStage.Set(fmt.Sprintf("%s locations", humanize.Comma(prog.Current())))
|
||||
prog.SetCompleted()
|
||||
|
||||
return results, nil
|
||||
return results, errs
|
||||
}
|
||||
|
||||
func catalogingProgress(locations int64) *monitor.CatalogerTaskProgress {
|
||||
|
||||
@@ -39,3 +39,7 @@ func (c Coordinates) String() string {
|
||||
}
|
||||
return fmt.Sprintf("Location<%s>", str)
|
||||
}
|
||||
|
||||
func (c Coordinates) GetCoordinates() Coordinates {
|
||||
return c
|
||||
}
|
||||
|
||||
@@ -121,7 +121,8 @@ func newDirectoryCatalog() *pkg.Collection {
|
||||
},
|
||||
},
|
||||
},
|
||||
PURL: "a-purl-2", // intentionally a bad pURL for test fixtures
|
||||
PURL: "a-purl-2", // intentionally a bad pURL for test fixtures
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*", cpe.Source("")),
|
||||
},
|
||||
@@ -138,7 +139,8 @@ func newDirectoryCatalog() *pkg.Collection {
|
||||
Package: "package-2",
|
||||
Version: "2.0.1",
|
||||
},
|
||||
PURL: "pkg:deb/debian/package-2@2.0.1",
|
||||
PURL: "pkg:deb/debian/package-2@2.0.1",
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*", cpe.Source("")),
|
||||
},
|
||||
@@ -163,6 +165,7 @@ func newDirectoryCatalogWithAuthorField() *pkg.Collection {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("MIT"),
|
||||
),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.PythonPackage{
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
@@ -190,7 +193,8 @@ func newDirectoryCatalogWithAuthorField() *pkg.Collection {
|
||||
Package: "package-2",
|
||||
Version: "2.0.1",
|
||||
},
|
||||
PURL: "pkg:deb/debian/package-2@2.0.1",
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
PURL: "pkg:deb/debian/package-2@2.0.1",
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*", "another-test-source"),
|
||||
},
|
||||
|
||||
@@ -117,7 +117,8 @@ func populateImageCatalog(catalog *pkg.Collection, img *image.Image) {
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
},
|
||||
PURL: "a-purl-1", // intentionally a bad pURL for test fixtures
|
||||
PURL: "a-purl-1", // intentionally a bad pURL for test fixtures
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:1:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
},
|
||||
@@ -137,7 +138,8 @@ func populateImageCatalog(catalog *pkg.Collection, img *image.Image) {
|
||||
Package: "package-2",
|
||||
Version: "2.0.1",
|
||||
},
|
||||
PURL: "pkg:deb/debian/package-2@2.0.1",
|
||||
PURL: "pkg:deb/debian/package-2@2.0.1",
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:*:some:package:2:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
|
||||
@@ -221,6 +221,7 @@ func Test_encodeDecodeFileMetadata(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
Unknowns: map[file.Coordinates][]string{},
|
||||
Executables: map[file.Coordinates]file.Executable{
|
||||
c: {
|
||||
Format: file.ELF,
|
||||
|
||||
@@ -141,10 +141,11 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
RealPath: "/a/place/a",
|
||||
}),
|
||||
),
|
||||
Type: pkg.PythonPkg,
|
||||
FoundBy: "the-cataloger-1",
|
||||
Language: pkg.Python,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")),
|
||||
Type: pkg.PythonPkg,
|
||||
FoundBy: "the-cataloger-1",
|
||||
Language: pkg.Python,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicense("MIT")),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.PythonPackage{
|
||||
Name: "package-1",
|
||||
Version: "1.0.1",
|
||||
@@ -164,8 +165,9 @@ func TestEncodeFullJSONDocument(t *testing.T) {
|
||||
RealPath: "/b/place/b",
|
||||
}),
|
||||
),
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "the-cataloger-2",
|
||||
Type: pkg.DebPkg,
|
||||
FoundBy: "the-cataloger-2",
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.DpkgDBEntry{
|
||||
Package: "package-2",
|
||||
Version: "2.0.1",
|
||||
|
||||
@@ -13,6 +13,7 @@ type File struct {
|
||||
Digests []file.Digest `json:"digests,omitempty"`
|
||||
Licenses []FileLicense `json:"licenses,omitempty"`
|
||||
Executable *file.Executable `json:"executable,omitempty"`
|
||||
Unknowns []string `json:"unknowns,omitempty"`
|
||||
}
|
||||
|
||||
type FileMetadataEntry struct {
|
||||
|
||||
@@ -24,16 +24,17 @@ type Package struct {
|
||||
|
||||
// PackageBasicData contains non-ambiguous values (type-wise) from pkg.Package.
|
||||
type PackageBasicData struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Type pkg.Type `json:"type"`
|
||||
FoundBy string `json:"foundBy"`
|
||||
Locations []file.Location `json:"locations"`
|
||||
Licenses licenses `json:"licenses"`
|
||||
Language pkg.Language `json:"language"`
|
||||
CPEs cpes `json:"cpes"`
|
||||
PURL string `json:"purl"`
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version"`
|
||||
Type pkg.Type `json:"type"`
|
||||
FoundBy string `json:"foundBy"`
|
||||
Locations []file.Location `json:"locations"`
|
||||
Licenses licenses `json:"licenses"`
|
||||
Language pkg.Language `json:"language"`
|
||||
CPEs cpes `json:"cpes"`
|
||||
PURL string `json:"purl"`
|
||||
Dependencies pkg.DependencyCompleteness `json:"dependencies"`
|
||||
}
|
||||
|
||||
type cpes []CPE
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
}
|
||||
],
|
||||
"purl": "a-purl-2",
|
||||
"dependencies": "complete",
|
||||
"metadataType": "python-package",
|
||||
"metadata": {
|
||||
"name": "package-1",
|
||||
@@ -63,6 +64,7 @@
|
||||
}
|
||||
],
|
||||
"purl": "pkg:deb/debian/package-2@2.0.1",
|
||||
"dependencies": "complete",
|
||||
"metadataType": "dpkg-db-entry",
|
||||
"metadata": {
|
||||
"package": "package-2",
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
}
|
||||
],
|
||||
"purl": "a-purl-1",
|
||||
"dependencies": "complete",
|
||||
"metadataType": "python-package",
|
||||
"metadata": {
|
||||
"name": "package-1",
|
||||
@@ -60,6 +61,7 @@
|
||||
}
|
||||
],
|
||||
"purl": "a-purl-2",
|
||||
"dependencies": "complete",
|
||||
"metadataType": "dpkg-db-entry",
|
||||
"metadata": {
|
||||
"package": "package-2",
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
}
|
||||
],
|
||||
"purl": "a-purl-1",
|
||||
"dependencies": "complete",
|
||||
"metadataType": "python-package",
|
||||
"metadata": {
|
||||
"name": "package-1",
|
||||
@@ -62,6 +63,7 @@
|
||||
}
|
||||
],
|
||||
"purl": "pkg:deb/debian/package-2@2.0.1",
|
||||
"dependencies": "complete",
|
||||
"metadataType": "dpkg-db-entry",
|
||||
"metadata": {
|
||||
"package": "package-2",
|
||||
|
||||
@@ -101,6 +101,11 @@ func toFile(s sbom.SBOM) []model.File {
|
||||
contents = contentsForLocation
|
||||
}
|
||||
|
||||
var unknowns []string
|
||||
if unknownsForLocation, exists := artifacts.Unknowns[coordinates]; exists {
|
||||
unknowns = unknownsForLocation
|
||||
}
|
||||
|
||||
var licenses []model.FileLicense
|
||||
for _, l := range artifacts.FileLicenses[coordinates] {
|
||||
var evidence *model.FileLicenseEvidence
|
||||
@@ -132,6 +137,7 @@ func toFile(s sbom.SBOM) []model.File {
|
||||
Contents: contents,
|
||||
Licenses: licenses,
|
||||
Executable: executable,
|
||||
Unknowns: unknowns,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -253,16 +259,17 @@ func toPackageModel(p pkg.Package, cfg EncoderConfig) model.Package {
|
||||
|
||||
return model.Package{
|
||||
PackageBasicData: model.PackageBasicData{
|
||||
ID: string(p.ID()),
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
Type: p.Type,
|
||||
FoundBy: p.FoundBy,
|
||||
Locations: p.Locations.ToSlice(),
|
||||
Licenses: licenses,
|
||||
Language: p.Language,
|
||||
CPEs: cpes,
|
||||
PURL: p.PURL,
|
||||
ID: string(p.ID()),
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
Type: p.Type,
|
||||
FoundBy: p.FoundBy,
|
||||
Locations: p.Locations.ToSlice(),
|
||||
Licenses: licenses,
|
||||
Language: p.Language,
|
||||
CPEs: cpes,
|
||||
PURL: p.PURL,
|
||||
Dependencies: p.Dependencies,
|
||||
},
|
||||
PackageCustomData: model.PackageCustomData{
|
||||
MetadataType: metadataType(p.Metadata, cfg.Legacy),
|
||||
|
||||
@@ -38,6 +38,7 @@ func toSyftModel(doc model.Document) *sbom.SBOM {
|
||||
FileContents: fileArtifacts.FileContents,
|
||||
FileLicenses: fileArtifacts.FileLicenses,
|
||||
Executables: fileArtifacts.Executables,
|
||||
Unknowns: fileArtifacts.Unknowns,
|
||||
LinuxDistribution: toSyftLinuxRelease(doc.Distro),
|
||||
},
|
||||
Source: *toSyftSourceData(doc.Source),
|
||||
@@ -66,6 +67,7 @@ func deduplicateErrors(errors []error) []string {
|
||||
return errorMessages
|
||||
}
|
||||
|
||||
//nolint:funlen
|
||||
func toSyftFiles(files []model.File) sbom.Artifacts {
|
||||
ret := sbom.Artifacts{
|
||||
FileMetadata: make(map[file.Coordinates]file.Metadata),
|
||||
@@ -73,6 +75,7 @@ func toSyftFiles(files []model.File) sbom.Artifacts {
|
||||
FileContents: make(map[file.Coordinates]string),
|
||||
FileLicenses: make(map[file.Coordinates][]file.License),
|
||||
Executables: make(map[file.Coordinates]file.Executable),
|
||||
Unknowns: make(map[file.Coordinates][]string),
|
||||
}
|
||||
|
||||
for _, f := range files {
|
||||
@@ -130,6 +133,10 @@ func toSyftFiles(files []model.File) sbom.Artifacts {
|
||||
if f.Executable != nil {
|
||||
ret.Executables[coord] = *f.Executable
|
||||
}
|
||||
|
||||
if len(f.Unknowns) > 0 {
|
||||
ret.Unknowns[coord] = f.Unknowns
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
@@ -331,16 +338,17 @@ func toSyftPackage(p model.Package, idAliases map[string]string) pkg.Package {
|
||||
}
|
||||
|
||||
out := pkg.Package{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
FoundBy: p.FoundBy,
|
||||
Locations: file.NewLocationSet(p.Locations...),
|
||||
Licenses: pkg.NewLicenseSet(toSyftLicenses(p.Licenses)...),
|
||||
Language: p.Language,
|
||||
Type: p.Type,
|
||||
CPEs: cpes,
|
||||
PURL: p.PURL,
|
||||
Metadata: p.Metadata,
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
FoundBy: p.FoundBy,
|
||||
Locations: file.NewLocationSet(p.Locations...),
|
||||
Licenses: pkg.NewLicenseSet(toSyftLicenses(p.Licenses)...),
|
||||
Language: p.Language,
|
||||
Type: p.Type,
|
||||
CPEs: cpes,
|
||||
PURL: p.PURL,
|
||||
Dependencies: p.Dependencies,
|
||||
Metadata: p.Metadata,
|
||||
}
|
||||
|
||||
// we don't know if this package ID is truly unique, however, we need to trust the user input in case there are
|
||||
|
||||
@@ -234,6 +234,7 @@ func Test_toSyftFiles(t *testing.T) {
|
||||
FileMetadata: map[file.Coordinates]file.Metadata{},
|
||||
FileDigests: map[file.Coordinates][]file.Digest{},
|
||||
Executables: map[file.Coordinates]file.Executable{},
|
||||
Unknowns: make(map[file.Coordinates][]string),
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -349,6 +350,7 @@ func Test_toSyftFiles(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tt.want.FileContents = make(map[file.Coordinates]string)
|
||||
tt.want.FileLicenses = make(map[file.Coordinates][]file.License)
|
||||
tt.want.Unknowns = make(map[file.Coordinates][]string)
|
||||
assert.Equal(t, tt.want, toSyftFiles(tt.files))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -93,12 +93,13 @@ func (ps pathSkipper) pathIndexVisitor(_ string, givenPath string, _ os.FileInfo
|
||||
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
|
||||
}
|
||||
// Rule 0: Make sure the given path is within the mount point; if not let the scan continue
|
||||
if !containsPath(givenPath, mi.Mountpoint) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Rule 1: ignore any path within a mount point that is of the given filesystem type unconditionally
|
||||
if len(conditionalPaths) == 0 {
|
||||
if !ignorable {
|
||||
// we've matched on the most specific path at this point, which means we should stop searching
|
||||
// mount points for this path
|
||||
@@ -151,7 +152,7 @@ func simpleClean(p string) string {
|
||||
return "."
|
||||
}
|
||||
if p == "/" {
|
||||
return "/"
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSuffix(p, "/")
|
||||
}
|
||||
|
||||
@@ -358,6 +358,43 @@ func Test_newPathSkipper(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "buildkit github ubuntu 22.04",
|
||||
root: "/run/src/core/sbom",
|
||||
mounts: []*mountinfo.Info{
|
||||
{Mountpoint: "/", FSType: "overlay"},
|
||||
{Mountpoint: "/proc", FSType: "proc"},
|
||||
{Mountpoint: "/dev", FSType: "tmpfs"},
|
||||
{Mountpoint: "/dev/pts", FSType: "devpts"},
|
||||
{Mountpoint: "/dev/shm", FSType: "tmpfs"},
|
||||
{Mountpoint: "/dev/mqueue", FSType: "mqueue"},
|
||||
{Mountpoint: "/sys", FSType: "sysfs"},
|
||||
{Mountpoint: "/etc/resolv.conf", FSType: "ext4"},
|
||||
{Mountpoint: "/etc/hosts", FSType: "ext4"},
|
||||
{Mountpoint: "/sys/fs/cgroup", FSType: "cgroup2"},
|
||||
{Mountpoint: "/run/out", FSType: "ext4"},
|
||||
{Mountpoint: "/run/src/core/sbom", FSType: "overlay"},
|
||||
{Mountpoint: "/tmp", FSType: "tmpfs"},
|
||||
{Mountpoint: "/dev/otel-grpc.sock", FSType: "overlay"},
|
||||
{Mountpoint: "/proc/bus", FSType: "proc"},
|
||||
{Mountpoint: "/proc/fs", FSType: "proc"},
|
||||
{Mountpoint: "/proc/irq", FSType: "proc"},
|
||||
{Mountpoint: "/proc/sys", FSType: "proc"},
|
||||
{Mountpoint: "/proc/sysrq-trigger", FSType: "proc"},
|
||||
{Mountpoint: "/proc/acpi", FSType: "tmpfs"},
|
||||
{Mountpoint: "/proc/kcore", FSType: "tmpfs"},
|
||||
{Mountpoint: "/proc/keys", FSType: "tmpfs"},
|
||||
{Mountpoint: "/proc/latency_stats", FSType: "tmpfs"},
|
||||
{Mountpoint: "/proc/timer_list", FSType: "tmpfs"},
|
||||
{Mountpoint: "/sys/firmware", FSType: "tmpfs"},
|
||||
{Mountpoint: "/proc/scsi", FSType: "tmpfs"},
|
||||
},
|
||||
want: []expect{
|
||||
{
|
||||
path: "/run/src/core/sbom",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -23,7 +23,8 @@ func TestApkDBCataloger(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-3.0-or-later", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "bash",
|
||||
OriginPackage: "bash",
|
||||
@@ -52,7 +53,8 @@ func TestApkDBCataloger(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-2.0-only", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "busybox-binsh",
|
||||
OriginPackage: "busybox",
|
||||
@@ -81,7 +83,8 @@ func TestApkDBCataloger(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "musl",
|
||||
OriginPackage: "musl",
|
||||
@@ -108,7 +111,8 @@ func TestApkDBCataloger(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL-2.0-or-later", dbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Locations: file.NewLocationSet(dbLocation),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "readline",
|
||||
OriginPackage: "readline",
|
||||
@@ -190,6 +194,14 @@ func TestApkDBCataloger(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func Test_corruptDb(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, "test-fixtures/corrupt").
|
||||
WithCompareOptions(cmpopts.IgnoreFields(pkg.ApkDBEntry{}, "Files", "GitCommit", "Checksum")).
|
||||
WithError().
|
||||
TestCataloger(t, NewDBCataloger())
|
||||
}
|
||||
|
||||
func TestCatalogerDependencyTree(t *testing.T) {
|
||||
assertion := func(t *testing.T, pkgs []pkg.Package, relationships []artifact.Relationship) {
|
||||
expected := map[string][]string{
|
||||
|
||||
@@ -20,13 +20,14 @@ func newPackage(d parsedData, release *linux.Release, dbLocation file.Location)
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
Name: d.Package,
|
||||
Version: d.Version,
|
||||
Locations: file.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation, licenseStrings...)...),
|
||||
PURL: packageURL(d.ApkDBEntry, release),
|
||||
Type: pkg.ApkPkg,
|
||||
Metadata: d.ApkDBEntry,
|
||||
Name: d.Package,
|
||||
Version: d.Version,
|
||||
Locations: file.NewLocationSet(dbLocation.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation)),
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation, licenseStrings...)...),
|
||||
PURL: packageURL(d.ApkDBEntry, release),
|
||||
Type: pkg.ApkPkg,
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: d.ApkDBEntry,
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
|
||||
@@ -112,7 +112,7 @@ func Test_PackageURL(t *testing.T) {
|
||||
ID: "alpine",
|
||||
VersionID: "3.4.6",
|
||||
},
|
||||
expected: "pkg:apk/alpine/p@v?arch=a&upstream=origin&distro=alpine-3.4.6",
|
||||
expected: "pkg:apk/alpine/p@v?arch=a&distro=alpine-3.4.6&upstream=origin",
|
||||
},
|
||||
{
|
||||
name: "wolfi distro",
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
@@ -38,6 +39,7 @@ type parsedData struct {
|
||||
func parseApkDB(_ context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
|
||||
var errs error
|
||||
var apks []parsedData
|
||||
var currentEntry parsedData
|
||||
entryParsingInProgress := false
|
||||
@@ -81,10 +83,12 @@ func parseApkDB(_ context.Context, resolver file.Resolver, env *generic.Environm
|
||||
field := parseApkField(line)
|
||||
if field == nil {
|
||||
log.Warnf("unable to parse field data from line %q", line)
|
||||
errs = unknown.Appendf(errs, reader, "unable to parse field data from line %q", line)
|
||||
continue
|
||||
}
|
||||
if len(field.name) == 0 {
|
||||
log.Warnf("failed to parse field name from line %q", line)
|
||||
errs = unknown.Appendf(errs, reader, "failed to parse field name from line %q", line)
|
||||
continue
|
||||
}
|
||||
if len(field.value) == 0 {
|
||||
@@ -131,7 +135,7 @@ func parseApkDB(_ context.Context, resolver file.Resolver, env *generic.Environm
|
||||
pkgs = append(pkgs, newPackage(apk, r, reader.Location))
|
||||
}
|
||||
|
||||
return pkgs, nil, nil
|
||||
return pkgs, nil, errs
|
||||
}
|
||||
|
||||
func findReleases(resolver file.Resolver, dbPath string) []linux.Release {
|
||||
|
||||
@@ -90,7 +90,8 @@ func TestSinglePackageDetails(t *testing.T) {
|
||||
pkg.NewLicense("BSD"),
|
||||
pkg.NewLicense("GPL2+"),
|
||||
),
|
||||
Type: pkg.ApkPkg,
|
||||
Type: pkg.ApkPkg,
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "musl-utils",
|
||||
OriginPackage: "musl",
|
||||
@@ -177,7 +178,8 @@ func TestSinglePackageDetails(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("GPL-2.0-only"),
|
||||
),
|
||||
Type: pkg.ApkPkg,
|
||||
Type: pkg.ApkPkg,
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "alpine-baselayout-data",
|
||||
OriginPackage: "alpine-baselayout",
|
||||
@@ -221,8 +223,8 @@ func TestSinglePackageDetails(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicense("GPL-2.0-only"),
|
||||
),
|
||||
Type: pkg.ApkPkg,
|
||||
PURL: "",
|
||||
Type: pkg.ApkPkg,
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.ApkDBEntry{
|
||||
Package: "alpine-baselayout",
|
||||
OriginPackage: "alpine-baselayout",
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
this is a corrupt db
|
||||
|
||||
C:Q1v4QhLje3kWlC8DJj+ZfJTjlJRSU=
|
||||
P:alpine-baselayout-data
|
||||
V:3.2.0-r22
|
||||
A:x86_64
|
||||
S:11435
|
||||
I:73728
|
||||
o:alpine-baselayout
|
||||
t:1655134784
|
||||
c:cb70ca5c6d6db0399d2dd09189c5d57827bce5cd
|
||||
r:alpine-baselayout
|
||||
F:etc
|
||||
R:fstab
|
||||
Z:Q11Q7hNe8QpDS531guqCdrXBzoA/o=
|
||||
R:group
|
||||
Z:Q13K+olJg5ayzHSVNUkggZJXuB+9Y=
|
||||
R:hostname
|
||||
Z:Q16nVwYVXP/tChvUPdukVD2ifXOmc=
|
||||
R:hosts
|
||||
Z:Q1BD6zJKZTRWyqGnPi4tSfd3krsMU=
|
||||
R:inittab
|
||||
Z:Q1TsthbhW7QzWRe1E/NKwTOuD4pHc=
|
||||
R:modules
|
||||
Z:Q1toogjUipHGcMgECgPJX64SwUT1M=
|
||||
R:mtab
|
||||
a:0:0:777
|
||||
Z:Q1kiljhXXH1LlQroHsEJIkPZg2eiw=
|
||||
R:passwd
|
||||
Z:Q1TchuuLUfur0izvfZQZxgN/LJhB8=
|
||||
R:profile
|
||||
Z:Q1F3DgXUP+jNZDknmQPPb5t9FSfDg=
|
||||
R:protocols
|
||||
Z:Q1omKlp3vgGq2ZqYzyD/KHNdo8rDc=
|
||||
R:services
|
||||
Z:Q19WLCv5ItKg4MH7RWfNRh1I7byQc=
|
||||
R:shadow
|
||||
a:0:42:640
|
||||
Z:Q1ltrPIAW2zHeDiajsex2Bdmq3uqA=
|
||||
R:shells
|
||||
Z:Q1ojm2YdpCJ6B/apGDaZ/Sdb2xJkA=
|
||||
R:sysctl.conf
|
||||
Z:Q14upz3tfnNxZkIEsUhWn7Xoiw96g=
|
||||
@@ -11,6 +11,14 @@ import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func TestAlpmUnknowns(t *testing.T) {
|
||||
pkgtest.NewCatalogTester().
|
||||
FromDirectory(t, "test-fixtures/installed").
|
||||
WithCompareOptions(cmpopts.IgnoreFields(pkg.AlpmFileRecord{}, "Time")).
|
||||
WithError().
|
||||
TestCataloger(t, NewDBCataloger())
|
||||
}
|
||||
|
||||
func TestAlpmCataloger(t *testing.T) {
|
||||
gmpDbLocation := file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/desc")
|
||||
treeSitterDbLocation := file.NewLocation("var/lib/pacman/local/tree-sitter-0.22.6-1/desc")
|
||||
@@ -26,7 +34,8 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("MIT", treeSitterDbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(treeSitterDbLocation),
|
||||
Locations: file.NewLocationSet(treeSitterDbLocation),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
BasePackage: "tree-sitter",
|
||||
Package: "tree-sitter",
|
||||
@@ -52,7 +61,8 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.NewLicenseFromLocations("GPL3", emacsDbLocation),
|
||||
),
|
||||
Locations: file.NewLocationSet(emacsDbLocation),
|
||||
Locations: file.NewLocationSet(emacsDbLocation),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
BasePackage: "emacs",
|
||||
Package: "emacs",
|
||||
@@ -78,6 +88,7 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
fuzzyDbLocation,
|
||||
file.NewLocation("var/lib/pacman/local/fuzzy-1.2-3/files"),
|
||||
),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "fuzzy",
|
||||
Version: "1.2-3",
|
||||
@@ -95,11 +106,12 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
}
|
||||
|
||||
madeupPkg := pkg.Package{
|
||||
Name: "madeup",
|
||||
Version: "20.30-4",
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Locations: file.NewLocationSet(madeupDbLocation),
|
||||
Name: "madeup",
|
||||
Version: "20.30-4",
|
||||
Type: pkg.AlpmPkg,
|
||||
FoundBy: "alpm-db-cataloger",
|
||||
Locations: file.NewLocationSet(madeupDbLocation),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
Package: "madeup",
|
||||
Version: "20.30-4",
|
||||
@@ -123,6 +135,7 @@ func TestAlpmCataloger(t *testing.T) {
|
||||
file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/files"),
|
||||
file.NewLocation("var/lib/pacman/local/gmp-6.2.1-2/mtree"),
|
||||
),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: pkg.AlpmDBEntry{
|
||||
BasePackage: "gmp",
|
||||
Package: "gmp",
|
||||
|
||||
@@ -16,13 +16,14 @@ func newPackage(m *parsedData, release *linux.Release, dbLocation file.Location,
|
||||
locs.Add(otherLocations...)
|
||||
|
||||
p := pkg.Package{
|
||||
Name: m.Package,
|
||||
Version: m.Version,
|
||||
Locations: locs,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...)...),
|
||||
Type: pkg.AlpmPkg,
|
||||
PURL: packageURL(m, release),
|
||||
Metadata: m.AlpmDBEntry,
|
||||
Name: m.Package,
|
||||
Version: m.Version,
|
||||
Locations: locs,
|
||||
Licenses: pkg.NewLicenseSet(pkg.NewLicensesFromLocation(dbLocation.WithoutAnnotations(), licenseCandidates...)...),
|
||||
Type: pkg.AlpmPkg,
|
||||
PURL: packageURL(m, release),
|
||||
Dependencies: pkg.CompleteDependencies,
|
||||
Metadata: m.AlpmDBEntry,
|
||||
}
|
||||
p.SetID()
|
||||
|
||||
|
||||
@@ -108,7 +108,7 @@ func Test_PackageURL(t *testing.T) {
|
||||
ID: "arch",
|
||||
BuildID: "rolling",
|
||||
},
|
||||
expected: "pkg:alpm/arch/p@v?arch=a&upstream=origin&distro=arch-rolling",
|
||||
expected: "pkg:alpm/arch/p@v?arch=a&distro=arch-rolling&upstream=origin",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -41,6 +42,8 @@ type parsedData struct {
|
||||
|
||||
// parseAlpmDB parses the arch linux pacman database flat-files and returns the packages and relationships found within.
|
||||
func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var errs error
|
||||
|
||||
data, err := parseAlpmDBEntry(reader)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
@@ -52,24 +55,25 @@ func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environ
|
||||
|
||||
base := path.Dir(reader.RealPath)
|
||||
|
||||
var locs []file.Location
|
||||
|
||||
// replace the files found the pacman database with the files from the mtree These contain more metadata and
|
||||
// thus more useful.
|
||||
files, fileLoc := fetchPkgFiles(base, resolver)
|
||||
backups, backupLoc := fetchBackupFiles(base, resolver)
|
||||
|
||||
var locs []file.Location
|
||||
if fileLoc != nil {
|
||||
files, fileLoc, err := fetchPkgFiles(base, resolver)
|
||||
errs = unknown.Join(errs, err)
|
||||
if err == nil {
|
||||
locs = append(locs, fileLoc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
data.Files = files
|
||||
}
|
||||
|
||||
if backupLoc != nil {
|
||||
backups, backupLoc, err := fetchBackupFiles(base, resolver)
|
||||
errs = unknown.Join(errs, err)
|
||||
if err == nil {
|
||||
locs = append(locs, backupLoc.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.SupportingEvidenceAnnotation))
|
||||
data.Backup = backups
|
||||
}
|
||||
|
||||
if data.Package == "" {
|
||||
return nil, nil, nil
|
||||
return nil, nil, errs
|
||||
}
|
||||
|
||||
return []pkg.Package{
|
||||
@@ -79,63 +83,56 @@ func parseAlpmDB(_ context.Context, resolver file.Resolver, env *generic.Environ
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
locs...,
|
||||
),
|
||||
}, nil, nil
|
||||
}, nil, errs
|
||||
}
|
||||
|
||||
func fetchPkgFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, *file.Location) {
|
||||
func fetchPkgFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, file.Location, error) {
|
||||
// TODO: probably want to use MTREE and PKGINFO here
|
||||
target := path.Join(base, "mtree")
|
||||
|
||||
loc, err := getLocation(target, resolver)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", target).Trace("failed to find mtree file")
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to find mtree file: %w", err))
|
||||
}
|
||||
if loc == nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
|
||||
reader, err := resolver.FileContentsByLocation(*loc)
|
||||
reader, err := resolver.FileContentsByLocation(loc)
|
||||
if err != nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to get contents: %w", err))
|
||||
}
|
||||
defer internal.CloseAndLogError(reader, loc.RealPath)
|
||||
|
||||
pkgFiles, err := parseMtree(reader)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", target).Trace("failed to parse mtree file")
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to parse mtree: %w", err))
|
||||
}
|
||||
return pkgFiles, loc
|
||||
return pkgFiles, loc, nil
|
||||
}
|
||||
|
||||
func fetchBackupFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, *file.Location) {
|
||||
func fetchBackupFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, file.Location, error) {
|
||||
// We only really do this to get any backup database entries from the files database
|
||||
target := filepath.Join(base, "files")
|
||||
|
||||
loc, err := getLocation(target, resolver)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "path", target).Trace("failed to find alpm files")
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
}
|
||||
if loc == nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to find alpm files: %w", err))
|
||||
}
|
||||
|
||||
reader, err := resolver.FileContentsByLocation(*loc)
|
||||
reader, err := resolver.FileContentsByLocation(loc)
|
||||
if err != nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to get contents: %w", err))
|
||||
}
|
||||
defer internal.CloseAndLogError(reader, loc.RealPath)
|
||||
|
||||
filesMetadata, err := parseAlpmDBEntry(reader)
|
||||
if err != nil {
|
||||
return []pkg.AlpmFileRecord{}, nil
|
||||
return []pkg.AlpmFileRecord{}, loc, unknown.New(loc, fmt.Errorf("failed to parse alpm db entry: %w", err))
|
||||
}
|
||||
if filesMetadata != nil {
|
||||
return filesMetadata.Backup, loc
|
||||
return filesMetadata.Backup, loc, nil
|
||||
}
|
||||
return []pkg.AlpmFileRecord{}, loc
|
||||
return []pkg.AlpmFileRecord{}, loc, nil
|
||||
}
|
||||
|
||||
func parseAlpmDBEntry(reader io.Reader) (*parsedData, error) {
|
||||
@@ -171,20 +168,21 @@ func newScanner(reader io.Reader) *bufio.Scanner {
|
||||
return scanner
|
||||
}
|
||||
|
||||
func getLocation(path string, resolver file.Resolver) (*file.Location, error) {
|
||||
func getLocation(path string, resolver file.Resolver) (file.Location, error) {
|
||||
loc := file.NewLocation(path)
|
||||
locs, err := resolver.FilesByPath(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return loc, err
|
||||
}
|
||||
|
||||
if len(locs) == 0 {
|
||||
return nil, fmt.Errorf("could not find file: %s", path)
|
||||
return loc, fmt.Errorf("could not find file: %s", path)
|
||||
}
|
||||
|
||||
if len(locs) > 1 {
|
||||
log.WithFields("path", path).Trace("multiple files found for path, using first path")
|
||||
}
|
||||
return &locs[0], nil
|
||||
return locs[0], nil
|
||||
}
|
||||
|
||||
func parseDatabase(b *bufio.Scanner) (*parsedData, error) {
|
||||
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
%NME%
|
||||
tree-sitter
|
||||
|
||||
%VER.6-1
|
||||
|
||||
%BASE%
|
||||
tree-sitter
|
||||
|
||||
%DESC%
|
||||
Incremental parsing library
|
||||
|
||||
|
||||
%BUILDDATE%
|
||||
1714945746
|
||||
|
||||
%INSTALLDATE%
|
||||
1715026360
|
||||
|
||||
%PACKA@archlinux.org>
|
||||
|
||||
%SIZE%
|
||||
223539
|
||||
|
||||
%REASON%
|
||||
1
|
||||
|
||||
%LICENSE%
|
||||
MIT
|
||||
|
||||
%VALIDATION%
|
||||
pgp
|
||||
|
||||
%PROVIDE
|
||||
libtree-sitter.so=0-64
|
||||
@@ -6,8 +6,10 @@ package binary
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -59,12 +61,14 @@ func (c cataloger) Name() string {
|
||||
func (c cataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var packages []pkg.Package
|
||||
var relationships []artifact.Relationship
|
||||
var errs error
|
||||
|
||||
for _, cls := range c.classifiers {
|
||||
log.WithFields("classifier", cls.Class).Trace("cataloging binaries")
|
||||
newPkgs, err := catalog(resolver, cls)
|
||||
if err != nil {
|
||||
log.WithFields("error", err, "classifier", cls.Class).Warn("unable to catalog binary package: %w", err)
|
||||
log.WithFields("error", err, "classifier", cls.Class).Debugf("unable to catalog binary package: %v", err)
|
||||
errs = unknown.Join(errs, fmt.Errorf("%s: %w", cls.Class, err))
|
||||
continue
|
||||
}
|
||||
newPackages:
|
||||
@@ -82,7 +86,7 @@ func (c cataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Pac
|
||||
}
|
||||
}
|
||||
|
||||
return packages, relationships, nil
|
||||
return packages, relationships, errs
|
||||
}
|
||||
|
||||
// mergePackages merges information from the extra package into the target package
|
||||
@@ -98,18 +102,21 @@ func mergePackages(target *pkg.Package, extra *pkg.Package) {
|
||||
}
|
||||
|
||||
func catalog(resolver file.Resolver, cls Classifier) (packages []pkg.Package, err error) {
|
||||
var errs error
|
||||
locations, err := resolver.FilesByGlob(cls.FileGlob)
|
||||
if err != nil {
|
||||
err = unknown.ProcessPathErrors(err) // convert any file.Resolver path errors to unknowns with locations
|
||||
return nil, err
|
||||
}
|
||||
for _, location := range locations {
|
||||
pkgs, err := cls.EvidenceMatcher(cls, matcherContext{resolver: resolver, location: location})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
errs = unknown.Append(errs, location, err)
|
||||
continue
|
||||
}
|
||||
packages = append(packages, pkgs...)
|
||||
}
|
||||
return packages, nil
|
||||
return packages, errs
|
||||
}
|
||||
|
||||
// packagesMatch returns true if the binary packages "match" based on basic criteria
|
||||
|
||||
@@ -348,6 +348,17 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("haproxy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "haproxy/2.0.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "haproxy",
|
||||
Version: "2.0.0",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/haproxy@2.0.0",
|
||||
Locations: locations("haproxy"),
|
||||
Metadata: metadata("haproxy-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "haproxy/2.7.3/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -485,6 +496,17 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "redis-server/7.2.5/linux-386",
|
||||
expected: pkg.Package{
|
||||
Name: "redis",
|
||||
Version: "7.2.5",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/redis@7.2.5",
|
||||
Locations: locations("redis-server"),
|
||||
Metadata: metadata("redis-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "python-shared-lib/3.7.4/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -625,6 +647,36 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
Metadata: metadata("go-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "node/0.10.48/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "node",
|
||||
Version: "0.10.48",
|
||||
PURL: "pkg:generic/node@0.10.48",
|
||||
Locations: locations("node"),
|
||||
Metadata: metadata("nodejs-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "node/0.12.18/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "node",
|
||||
Version: "0.12.18",
|
||||
PURL: "pkg:generic/node@0.12.18",
|
||||
Locations: locations("node"),
|
||||
Metadata: metadata("nodejs-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "node/4.9.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "node",
|
||||
Version: "4.9.1",
|
||||
PURL: "pkg:generic/node@4.9.1",
|
||||
Locations: locations("node"),
|
||||
Metadata: metadata("nodejs-binary"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "node/19.2.0/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
@@ -850,6 +902,60 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// note: dynamic (non-snippet) test case
|
||||
name: "positive-ruby-3.4.0-dev",
|
||||
logicalFixture: "ruby-shared-libs/3.4.0-dev/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "ruby",
|
||||
Version: "3.4.0dev",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ruby@3.4.0dev",
|
||||
Locations: locations("ruby", "libruby.so.3.4.0"),
|
||||
Metadata: pkg.BinarySignature{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
match("ruby-binary", "ruby"),
|
||||
match("ruby-binary", "libruby.so.3.4.0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// note: dynamic (non-snippet) test case
|
||||
name: "positive-ruby-3.4.0-preview1",
|
||||
logicalFixture: "ruby-shared-libs/3.4.0-preview1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "ruby",
|
||||
Version: "3.4.0preview1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ruby@3.4.0preview1",
|
||||
Locations: locations("ruby", "libruby.so.3.4.0"),
|
||||
Metadata: pkg.BinarySignature{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
match("ruby-binary", "ruby"),
|
||||
match("ruby-binary", "libruby.so.3.4.0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// note: dynamic (non-snippet) test case
|
||||
name: "positive-ruby-3.3.0-rc1",
|
||||
logicalFixture: "ruby-shared-libs/3.3.0-rc1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "ruby",
|
||||
Version: "3.3.0rc1",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ruby@3.3.0rc1",
|
||||
Locations: locations("ruby", "libruby.so.3.3.0"),
|
||||
Metadata: pkg.BinarySignature{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
match("ruby-binary", "ruby"),
|
||||
match("ruby-binary", "libruby.so.3.3.0"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// note: dynamic (non-snippet) test case
|
||||
logicalFixture: "ruby-bullseye-shared-libs/2.7.7/linux-amd64",
|
||||
@@ -1562,7 +1668,7 @@ func Test_Cataloger_ResilientToErrors(t *testing.T) {
|
||||
|
||||
resolver := &panicyResolver{}
|
||||
_, _, err := c.Catalog(context.Background(), resolver)
|
||||
assert.NoError(t, err)
|
||||
assert.Nil(t, err) // non-coordinate-based FindBy* errors are now logged and not returned
|
||||
assert.True(t, resolver.searchCalled)
|
||||
}
|
||||
|
||||
|
||||
@@ -35,6 +35,9 @@ func newClassifierPackage(classifier Classifier, location file.Location, matchMe
|
||||
Type: pkg.BinaryPkg,
|
||||
CPEs: cpes,
|
||||
FoundBy: catalogerName,
|
||||
// classifiers are limited to identifying package identities, but have no information about dependencies.
|
||||
// Furthermore, we have no understanding about the mechanisms for dependency resolution for these packages.
|
||||
Dependencies: pkg.UnknownDependencyCompleteness,
|
||||
Metadata: pkg.BinarySignature{
|
||||
Matches: []pkg.ClassifierMatch{
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ func DefaultClassifiers() []Classifier {
|
||||
FileGlob: "**/redis-server",
|
||||
EvidenceMatcher: evidenceMatchers(
|
||||
FileContentsVersionMatcher(`(?s)payload %5.*?(?P<version>\d.\d\.\d\d*)[a-z0-9]{12,15}-[0-9]{19}`),
|
||||
FileContentsVersionMatcher(`(?s)\x00(?P<version>\d.\d\.\d\d*)[a-z0-9]{12}-[0-9]{19}\x00.*?payload %5`),
|
||||
FileContentsVersionMatcher(`(?s)\x00(?P<version>\d.\d\.\d\d*)[a-z0-9]{12,15}-[0-9]{19}\x00.*?payload %5`),
|
||||
),
|
||||
Package: "redis",
|
||||
PURL: mustPURL("pkg:generic/redis@version"),
|
||||
@@ -153,8 +153,14 @@ func DefaultClassifiers() []Classifier {
|
||||
{
|
||||
Class: "nodejs-binary",
|
||||
FileGlob: "**/node",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(
|
||||
`(?m)node\.js\/v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
EvidenceMatcher: evidenceMatchers(
|
||||
// [NUL]node v0.10.48[NUL]
|
||||
// [NUL]v0.12.18[NUL]
|
||||
// [NUL]v4.9.1[NUL]
|
||||
// node.js/v22.9.0
|
||||
FileContentsVersionMatcher(`(?m)\x00(node )?v(?P<version>(0|4|5|6)\.[0-9]+\.[0-9]+)\x00`),
|
||||
FileContentsVersionMatcher(`(?m)node\.js\/v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
|
||||
),
|
||||
Package: "node",
|
||||
PURL: mustPURL("pkg:generic/node@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:nodejs:node.js:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
@@ -190,6 +196,7 @@ func DefaultClassifiers() []Classifier {
|
||||
Class: "haproxy-binary",
|
||||
FileGlob: "**/haproxy",
|
||||
EvidenceMatcher: evidenceMatchers(
|
||||
FileContentsVersionMatcher(`(?m)version (?P<version>[0-9]+\.[0-9]+(\.|-dev|-rc)[0-9]+)(-[a-z0-9]{7})?, released 20`),
|
||||
FileContentsVersionMatcher(`(?m)HA-Proxy version (?P<version>[0-9]+\.[0-9]+(\.|-dev)[0-9]+)`),
|
||||
FileContentsVersionMatcher(`(?m)(?P<version>[0-9]+\.[0-9]+(\.|-dev)[0-9]+)-[0-9a-zA-Z]{7}.+HAProxy version`),
|
||||
),
|
||||
@@ -344,7 +351,7 @@ func DefaultClassifiers() []Classifier {
|
||||
},
|
||||
{
|
||||
Class: "mariadb-binary",
|
||||
FileGlob: "**/mariadb",
|
||||
FileGlob: "**/{mariadb,mysql}",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(
|
||||
// 10.6.15-MariaDB
|
||||
`(?m)(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-MariaDB`),
|
||||
@@ -666,6 +673,9 @@ var libpythonMatcher = fileNameTemplateVersionMatcher(
|
||||
)
|
||||
|
||||
var rubyMatcher = FileContentsVersionMatcher(
|
||||
// ruby 3.4.0dev (2024-09-15T01:06:11Z master 532af89e3b) [x86_64-linux]
|
||||
// ruby 3.4.0preview1 (2024-05-16 master 9d69619623) [x86_64-linux]
|
||||
// ruby 3.3.0rc1 (2023-12-11 master a49643340e) [x86_64-linux]
|
||||
// ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
|
||||
// ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [x86_64-linux]
|
||||
`(?m)ruby (?P<version>[0-9]+\.[0-9]+\.[0-9]+(p[0-9]+)?) `)
|
||||
`(?m)ruby (?P<version>[0-9]+\.[0-9]+\.[0-9]+((p|preview|rc|dev)[0-9]*)?) `)
|
||||
|
||||
@@ -2,6 +2,7 @@ package binary
|
||||
|
||||
import (
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -15,7 +16,10 @@ func newELFPackage(metadata elfBinaryPackageNotes, locations file.LocationSet) p
|
||||
PURL: packageURL(metadata),
|
||||
Type: pkgType(metadata.Type),
|
||||
Locations: locations,
|
||||
Metadata: metadata.ELFBinaryPackageNoteJSONPayload,
|
||||
// though we can look for shared libs, we cannot see static dependencies nor dynamic dependencies using dlopen.
|
||||
// this means that, even in cases where the dep info is actually complete, we can't programmatically determine that.
|
||||
Dependencies: pkg.UnknownDependencyCompleteness,
|
||||
Metadata: metadata.ELFBinaryPackageNoteJSONPayload,
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
@@ -26,17 +30,7 @@ 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
|
||||
}
|
||||
}
|
||||
os, osVersion := osNameAndVersionFromMetadata(metadata)
|
||||
|
||||
if os != "" {
|
||||
osQualifier := os
|
||||
@@ -67,6 +61,26 @@ func packageURL(metadata elfBinaryPackageNotes) string {
|
||||
).ToString()
|
||||
}
|
||||
|
||||
func osNameAndVersionFromMetadata(metadata elfBinaryPackageNotes) (string, string) {
|
||||
os := metadata.OS
|
||||
osVersion := metadata.OSVersion
|
||||
|
||||
if os != "" && osVersion != "" {
|
||||
return os, osVersion
|
||||
}
|
||||
|
||||
if metadata.OSCPE == "" {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
attrs, err := cpe.NewAttributes(metadata.OSCPE)
|
||||
if err != nil {
|
||||
log.WithFields("error", err).Trace("unable to parse cpe attributes for elf binary package")
|
||||
return "", ""
|
||||
}
|
||||
return attrs.Product, attrs.Version
|
||||
}
|
||||
|
||||
const alpmType = "alpm"
|
||||
|
||||
func purlDistroType(ty string) string {
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/internal/mimetype"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
@@ -52,6 +53,7 @@ func (c *elfPackageCataloger) Name() string {
|
||||
}
|
||||
|
||||
func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var errs error
|
||||
locations, err := resolver.FilesByMIMEType(mimetype.ExecutableMIMETypeSet.List()...)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get binary files by mime type: %w", err)
|
||||
@@ -62,7 +64,8 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
|
||||
for _, location := range locations {
|
||||
notes, key, err := parseElfPackageNotes(resolver, location, c)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
errs = unknown.Append(errs, location, err)
|
||||
continue
|
||||
}
|
||||
if notes == nil {
|
||||
continue
|
||||
@@ -87,7 +90,7 @@ func (c *elfPackageCataloger) Catalog(_ context.Context, resolver file.Resolver)
|
||||
// why not return relationships? We have an executable cataloger that will note the dynamic libraries imported by
|
||||
// each binary. After all files and packages are processed there is a final task that creates package-to-package
|
||||
// and package-to-file relationships based on the dynamic libraries imported by each binary.
|
||||
return pkgs, nil, nil
|
||||
return pkgs, nil, errs
|
||||
}
|
||||
|
||||
func parseElfPackageNotes(resolver file.Resolver, location file.Location, c *elfPackageCataloger) (*elfBinaryPackageNotes, elfPackageKey, error) {
|
||||
@@ -104,7 +107,7 @@ func parseElfPackageNotes(resolver file.Resolver, location file.Location, c *elf
|
||||
|
||||
if err != nil {
|
||||
log.WithFields("file", location.Path(), "error", err).Trace("unable to parse ELF notes")
|
||||
return nil, elfPackageKey{}, nil
|
||||
return nil, elfPackageKey{}, err
|
||||
}
|
||||
|
||||
if notes == nil {
|
||||
@@ -173,7 +176,7 @@ func getELFNotes(r file.LocationReadCloser) (*elfBinaryPackageNotes, error) {
|
||||
if len(notes) > headerSize {
|
||||
var metadata elfBinaryPackageNotes
|
||||
newPayload := bytes.TrimRight(notes[headerSize:], "\x00")
|
||||
if err := json.Unmarshal(newPayload, &metadata); err == nil {
|
||||
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")
|
||||
|
||||
@@ -3,6 +3,8 @@ package binary
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
@@ -14,6 +16,7 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
name string
|
||||
fixture string
|
||||
expected []pkg.Package
|
||||
wantErr require.ErrorAssertionFunc
|
||||
}{
|
||||
{
|
||||
name: "go case",
|
||||
@@ -32,7 +35,8 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
pkg.License{Value: "MIT", SPDXExpression: "MIT", Type: "declared"},
|
||||
),
|
||||
|
||||
Type: pkg.BinaryPkg,
|
||||
Type: pkg.BinaryPkg,
|
||||
Dependencies: pkg.UnknownDependencyCompleteness,
|
||||
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "testfixture",
|
||||
Vendor: "syft",
|
||||
@@ -53,7 +57,8 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
Licenses: pkg.NewLicenseSet(
|
||||
pkg.License{Value: "MIT", SPDXExpression: "MIT", Type: "declared"},
|
||||
),
|
||||
Type: pkg.BinaryPkg,
|
||||
Type: pkg.BinaryPkg,
|
||||
Dependencies: pkg.UnknownDependencyCompleteness,
|
||||
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "testfixture",
|
||||
Vendor: "syft",
|
||||
@@ -63,6 +68,7 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
wantErr: require.Error,
|
||||
},
|
||||
{
|
||||
name: "fedora 64 bit binaries",
|
||||
@@ -76,8 +82,9 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
file.NewLocation("/sha256sum").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
file.NewLocation("/sha1sum").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(),
|
||||
Type: pkg.RpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(),
|
||||
Type: pkg.RpmPkg,
|
||||
Dependencies: pkg.UnknownDependencyCompleteness,
|
||||
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "rpm",
|
||||
Architecture: "x86_64",
|
||||
@@ -98,8 +105,9 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
file.NewLocation("/sha256sum").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
file.NewLocation("/sha1sum").WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
Licenses: pkg.NewLicenseSet(),
|
||||
Type: pkg.RpmPkg,
|
||||
Licenses: pkg.NewLicenseSet(),
|
||||
Type: pkg.RpmPkg,
|
||||
Dependencies: pkg.UnknownDependencyCompleteness,
|
||||
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "rpm",
|
||||
Architecture: "arm",
|
||||
@@ -116,6 +124,7 @@ func Test_ELF_Package_Cataloger(t *testing.T) {
|
||||
WithImageResolver(t, v.fixture).
|
||||
IgnoreLocationLayer(). // this fixture can be rebuilt, thus the layer ID will change
|
||||
Expects(v.expected, nil).
|
||||
WithErrorAssertion(v.wantErr).
|
||||
TestCataloger(t, NewELFPackageCataloger())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -113,6 +113,18 @@ func Test_packageURL(t *testing.T) {
|
||||
},
|
||||
want: "pkg:generic/system/test@1.0",
|
||||
},
|
||||
{
|
||||
name: "bad or missing OSCPE data cannot be parsed allows for correct string",
|
||||
metadata: elfBinaryPackageNotes{
|
||||
Name: "test",
|
||||
Version: "1.0",
|
||||
ELFBinaryPackageNoteJSONPayload: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
System: "system",
|
||||
OSCPE: "%$#*(#*@&$(",
|
||||
},
|
||||
},
|
||||
want: "pkg:generic/system/test@1.0",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
@@ -142,10 +154,11 @@ func Test_newELFPackage(t *testing.T) {
|
||||
},
|
||||
|
||||
expected: pkg.Package{
|
||||
Name: "syfttestfixture",
|
||||
Version: "0.01",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/syftsys/syfttestfixture@0.01",
|
||||
Name: "syfttestfixture",
|
||||
Version: "0.01",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/syftsys/syfttestfixture@0.01",
|
||||
Dependencies: pkg.UnknownDependencyCompleteness,
|
||||
Metadata: pkg.ELFBinaryPackageNoteJSONPayload{
|
||||
Type: "binary",
|
||||
System: "syftsys",
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user